Category Archives: Windows Phone

WPF Drawing Application

The following is a XAML page that allows the user to draw on it. This was written and tested under a Windows 10 desktop application, but should work for WPF. Here’s the XAML:

        <Grid>
            <Border>
                <Canvas PointerPressed="Canvas_PointerPressed" PointerMoved="Canvas_PointerMoved"
                        PointerReleased="Canvas_PointerReleased"
                        Background="Orange" Name="Canvas" />
            </Border>
        </Grid>

The background is a different colour to identify the canvas.

There’s the code to allow drawing:

        
        Path _currentPath;

        private void Canvas_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
        {            
            _currentPath = new Path
            {
                Data = new PathGeometry
                {
                    Figures = { new PathFigure
                    {
                        StartPoint = e.GetCurrentPoint((UIElement)sender).Position,
                        Segments = { new PolyLineSegment() }
                    }}
                },
                Stroke = new SolidColorBrush(Colors.Black),
                StrokeThickness = 5
            };

            Canvas.Children.Add(_currentPath);
        }

        private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (_currentPath == null) return;

            var pls = (PolyLineSegment)((PathGeometry)_currentPath.Data).Figures.Last().Segments.Last();
            pls.Points.Add(e.GetCurrentPoint((UIElement)sender).Position);
            
        }

        private void Canvas_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            _currentPath = null;
        }

As you can see, it doesn’t do anything for my drawing ability:

drawing

It’s all the code behind and, while I typically shy away from this, it seems to fit well for an application such as this (as the drawing relates more to the view than to anything else).

How to Draw a Diagonal Line Using Only XAML (and no path data)

I’ve been playing around with an app to maintain a score. The first thing that I wanted to do was to split the screen, but I wanted a diagonal split; similar to this:

phonescreen

There are a number of ways that this can be achieved; however, this is a method using only XAML. The same method will (I believe) work on Windows 8, WPF and Windows Phone.

First, split the screen into three rows or columns (which depends on how you want the orientation):

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

This needs to be an odd number of rows / columns if you want the line to be central; additionally, an increased number of rows or columns will result in a smaller incline. Next, draw a transparent rectangle across the middle grid square:

        <Rectangle Name="Placeholder" Grid.Row="1" 
                   HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                   Opacity="0"/>

Finally, simply draw a line, binding the X2, Y2 values to the height and width of your rectangle:

        <Line Grid.Row="1" 
              X1="0" X2="{Binding ActualWidth, ElementName=Placeholder}"
              Y1="0" Y2="{Binding ActualHeight, ElementName=Placeholder}"
              StrokeThickness="2" Stroke="Blue"/>

Conclusion

I’m not saying the is the best, most efficient, or only way of doing this. However, it does mean that I can do it on both Windows Phone and Windows Store (and in WPF if I were so inclined) using a single shared project.

Adding a tile to Windows Phone (Annoying and unhelpful errors – Part 2)

Following on from this post, I encountered the following error:

Value does not fall within the expected range.

Here’s the culpable code again:

SecondaryTile tileData = new SecondaryTile()
{
    TileId = title,
    DisplayName = title,
    Arguments = arguments
};
tileData.VisualElements.Square150x150Logo = new Uri("ms-appx:///assets/TileIcon150x150.png");
tileData.VisualElements.ShowNameOnSquare150x150Logo = true;

And, as is typical with these things, it worked yesterday, when I wrote the above post. So, why now, when I run it, does it point to the instantiation of `SecondaryTile` above and cry foul?

This time, it was caused by the ID containing an invalid character (a space). Something similar to the following will fix it:

SecondaryTile tileData = new SecondaryTile()
{
    TileId = title.Replace(" ", ""),
    DisplayName = title,
    Arguments = arguments
};
tileData.VisualElements.Square150x150Logo = new Uri("ms-appx:///assets/TileIcon150x150.png");
tileData.VisualElements.ShowNameOnSquare150x150Logo = true;

MVVM Cross – Creating a New Plug-in

This is quite a straightforward process, and well documented in several places – including here and here; and the sample that I used to create mine (and this tutorial) is here. However, I couldn’t find a step-by-step tutorial in a single place. So this is pretty much for my future self.

Disclaimer(s)

This only applies to Windows Store and Windows Phone. Although I believe the same process applies to iOS and Android.

The plug-in that I’m creating here will create a secondary tile on Windows Phone / Windows Store. However, this only covers the phone part, and doesn’t actually cover tile creation.

Create a Project

The project that I’m dealing with is a re-write of this application, using C# and MVVM Cross.

Obviously, your structure may be different, but the important thing is the Plugins folder at this stage; create a new portable project.

plugin1

Next, add the MVVMCross libraries:

plugin2

Interface and Plugin Loader

The portable class library above needs two files. Start with creating an interface:

    public interface ITile
    {
        Task<bool> CreateTile(string title);
    }

And next, the plug-in loader:

    public class PluginLoader : IMvxPluginLoader
    {
        public static readonly PluginLoader Instance = new PluginLoader();

        public void EnsureLoaded()
        {
            var manager = Mvx.Resolve<IMvxPluginManager>();
            manager.EnsurePlatformAdaptionLoaded<PluginLoader>();
        }
    }
}

Obviously, the next stage depends on your specific implementation. Mine depends entirely on the platform, so that’s all I need to do in the portable library.

Windows Phone

Create a new library project:

plugin3

plugin4

As before, you need to add the MVVM Cross Libraries, and a reference to the portable class above:

plugin5

The next stage is to implement your plug-in logic for the destination platform; in my case, this is logic to add a tile to Windows Phone:

    public class TilePlugin : ITile
    {
        public async Task<bool> CreateTile(string title)
        {
            // Create Tile code
        }
    }

Finally, just implement IMvxPlugin like this:

    public class Plugin : IMvxPlugin
    {
        public void Load()
        {
            Mvx.RegisterSingleton<ITile>(new TilePlugin());
        }
    }

Using the plug-in – the View Model

So, to all intents and purposes, the plug-in is now written (admittedly it only exists for Windows Phone, but that’s beside the point).

Next, it needs to be used; the first thing is to reference it.

plugin6

Here’s my logic to actually use the plug-in inside the view model:

    class SetupNewMailViewModel : BaseViewModel
    {
	. . . 
	
	        private void AddTile()
	        {
	            var tile = Mvx.Resolve<ITile>();
	            tile.CreateTile(MailHeading);
	        }
	    }
	

Using the plugin – Platform Specific

In the relevant project, a reference is needed:

plugin7

The only thing left to do for the platform is to add the bootstrap code:

public class TilePluginBootstrap : MvxPluginBootstrapAction<Tile.Plugin.PluginLoader> {    }

`MvxPluginBootstrapAction` signifies that the call to `MvxSetup` will call the BootstrapRunner for this plugin. Or, to put it another way, this magically makes MVVMCross aware of your new plugin for each platform.

Notes on Deployment

Above, I’ve basically manually added the reference to the correct project. This works fine when I’m working locally, but for an actual distributed plug-in, it’s NuGet that handles this.

Windows Tile Updater (Part 7 – Multibinding command parameters)

We left the last post where the Tile Updater could update text, or images, but we basically had to choose which at design time. In this post, I’m going to pass through the image and text, and have the command work out which to update.

Note: you can have both image and text in live tiles. Look out for that in a future post.

Multibinding

The first thing to know about multibinding for WinRT is that it doesn’t exist. However, to get around this, we can simply take the same approach that we do with a function – a function can only return one value, but that value can be a class; so we’ll bind to the VM (one of the advantages of exposing a static singleton instance of the VM):

<Button Grid.Row="2" Command="{Binding UpdateCommand}" CommandParameter="{Binding MVM}">Update</Button>        

The command itself needs to look something like this for now:

        public bool CanExecute(object parameter)
        {
            MainViewModel mvmInst = (MainViewModel)parameter;
            if (mvmInst == null) return false;

            if (string.IsNullOrWhiteSpace(mvmInst.ImagePath) &amp;&amp; string.IsNullOrWhiteSpace(mvmInst.DisplayText))
                return false;
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            MainViewModel mvmInst = (MainViewModel)parameter;

            if (string.IsNullOrWhiteSpace(mvmInst.ImagePath))
                TileUpdater.Model.UpdateTile.UpdateTileText(mvmInst.DisplayText);
            else
                TileUpdater.Model.UpdateTile.UpdateTileImage(mvmInst.ImagePath);
        }

There’s a couple of points here:
1. The CanExecute will not prevent the command from firing where neither parameter has a value.
2. It doesn’t disable the button when this occurs.

And that’s it – we now have a command that can accept multiple parameters and update either image or text depending on what is displayed.

Conclusion

Okay, strictly speaking, this is not multi-binding. It does however, solve the problem. I suppose the design question would be: does it introduce a tighter coupling than multi-binding?

It definitely does; however, with a bit of manipulation, you could introduce a mini-VM that just had the parameters that you need. However, for most cases, I would think that it was fine to just pass the VM to the command. We’ll see if this comes back to bite me when we start putting some unit tests in place.

Windows Tile Updater (Part 6 – Binding a command parameter)

We’re still missing functionality to show the text, or show the text and an image where both are specified.

Add text

Let’s start with the text field. It’s currently just hard coded – that’s pretty straightforward:

        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="1">
            <TextBlock FontSize="30" Margin="10">Text</TextBlock>
            <TextBox Text="{Binding DisplayText, Mode=TwoWay}" Margin="10"/>
        </StackPanel>

Next, let’s hook this up to the command.

<Button Grid.Row="2" Command="{Binding UpdateCommand}" CommandParameter="{Binding DisplayText}">Update</Button>

So that works, and the logic to show the text tile is quite straightforward:

        public static void UpdateTileText(string text)
        {
            XmlDocument xmlDoc = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150Text01);

            XmlElement textNode = (XmlElement)xmlDoc.GetElementsByTagName("text")[0];
            textNode.InnerText = text;

            Windows.UI.Notifications.TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
            TileNotification tileNotification = new TileNotification(xmlDoc);
            tileUpdater.Update(tileNotification);
        }

So, we can call this, or we can call the UpdateImage. At the moment, we can’t call both. In the next post I’ll look at how we can do this using Multibinding.

Windows Tile Updater (Part 5 – sharing XAML)

In Part 4 we finished with an MVVM app, running on both Windows 8 and Windows Phone. In this post I’m going to explore the idea of using the same XAML.

Is this a good idea?

The very short answer is probably “NO!”. The problem is that in anything but the most trivial of applications, the XAML will need to differ. In this application, it probably doesn’t matter, but then, it wouldn’t kill anyone to maintain two sets of very trivial XAML either.

My approach to this, and to the above question is to create a custom user control, and then use that in both applications. The good thing about this is that it does make sense to share user controls (to me anyway).

New UserControl

We’ll create a View directory in the shared project, and add the UserControl there:

NewView

It should look like this:


<UserControl
    x:Class="TileUpdater.View.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TileUpdater.View"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    DataContext="{Binding MVM, Source={StaticResource MainViewModel}}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
            <TextBlock FontSize="30" Margin="10">Image</TextBlock>
            <TextBox Text="{Binding ImagePath}" Margin="10"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="1">
            <TextBlock FontSize="30" Margin="10">Text</TextBlock>
            <TextBox Text="My Phone Number: 123456 890 12" Margin="10"/>
        </StackPanel>

        <Button Grid.Row="2" Command="{Binding UpdateCommand}">Update</Button>
    </Grid>
</UserControl>

You’ll be familiar with this XAML by now. Nothing had changed, it’s the same. In the Phone app, we’ll replace the whole XAML with the following:

<Page
    x:Class="TileUpdater.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TileUpdater"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:view="using:TileUpdater.View"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <view:MainView/>
</Page>

And the Windows 8 XAML with this:

<Page
    x:Class="TileUpdater.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TileUpdater"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:view="using:TileUpdater.View"
    mc:Ignorable="d">    

    <view:MainView/>
</Page>

Other than the theme, they are the same. You could obviously just have MainPage in the shared project, but personally, I don’t believe that is extensible; if you do so, you run the risk of ending up with an overly complex XAML file trying to cater for each separate project type.

Conclusion

Just a short post this time. In the next post on this, I’m going to add the missing functionality. This should be far easier now that the whole application is basically inside the shared project.

Windows Tile Updater (Part 3 – Move the code to a shared project and create the Phone App)

In the third part of my series on creating a universal app, I’m going to transfer the code that we created in part 2 and move it into a shared project.

If you want to have a look at the source code, or just use it for something, it’s here:

https://windowstileupdater.codeplex.com/

So far, we have code to update the tile, but it’s in the Windows 8.1 app. Let’s just move that to the shared project:

movetoshared

TileUpdater.cs looks like this:

    public class UpdateTile
    {
        private static void UpdateTileImage(string image)
        {
            XmlDocument xmlDoc = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150Image);

            XmlElement imageNode = (XmlElement)xmlDoc.GetElementsByTagName("image")[0];
            imageNode.SetAttribute("src", string.Format("ms-appx://{0}", image));

            Windows.UI.Notifications.TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
            TileNotification tileNotification = new TileNotification(xmlDoc);
            tileUpdater.Update(tileNotification);
        }

        public static void UpdateTileImage()
        {
            string image = "/Assets/test.png";
            UpdateTileImage(image);
        }
    }

You might notice this is precisely the same code. The button click event on the main page looks like this now:

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            TileUpdater.UpdateTile.UpdateTileImage();            
        }

Now, run again and the behaviour should remain the same. This isn’t much – I mean, if you’re reading this and have just found out how to separate code from the main program then you might be reading the wrong blog. So, let’s try wiring up the phone app. We’ll start with the XAML:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
            <TextBlock FontSize="30" Margin="10">Image</TextBlock>
            <TextBox Text="c:locationimage.png" Margin="10"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="1">
            <TextBlock FontSize="30" Margin="10">Text</TextBlock>
            <TextBox Text="My Phone Number: 123456 890 12" Margin="10"/>
        </StackPanel>

        <Button Grid.Row="2" Click="Button_Click">Update</Button>
    </Grid>

The XAML is familiar – I copied it verbatim from the Win 8 app (as I said in the last post, I will try to rationalise this a little, although other than in a very trivial app such as this, you would probably want the XAML to be different). And the code behind:

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            TileUpdater.UpdateTile.UpdateTileImage();
        }

And, it works:

phoneappstartscreen

Did it just work – just like that?

Well, no.

Okay – there was some fiddling – here’s the fiddling:
1. The app will (again) not be pinned to the start screen, you’ll need to use the emulator to do so.
2. The tile will, again, initially be a small square (half the size of a wide tile – 150 x 150); this will need to be a wide tile, and to do so, you need to press and hold and the first reduce the size, before increasing to a wide tile.

Conclusion

Okay – so, we now have a Windows Store and Phone App. There’s a common codebase (admittedly it’s a bit scrappy), and the phone app took around 20 seconds.

In the next post I’m going to tidy up the code and investigate options for using shared XAML.

Windows Tile Updater (Part 2 – creating the project and tile update logic)

In my first post of this series I looked at some considerations of designing a store / phone app. This post will cover the basics of creating a new Phone / Store app.

Universal App

The new type of application is called a Universal App – so let’s create one:

NewUniversal

I’m going to select a blank app, and create the project. What this gives me is a solution containing three separate projects.

UniversalSolutionExplorer

If I select to run the Windows app I get a blank Windows Store App, and if I select to run the phone app I get a blank phone app. The shared project can be used to create a common application lifecycle, share events and logic. However, the UI is separate (ish – we’ll come back to that in a later post). Let’s start with the Window 8 app

Create the tile image

You can’t update a tile that doesn’t exist; for this example, we’re going to update the wide tile, so you’ll need one. Just create any image 310 x 150 as a png file and point your Windows app at it:

universalmanifest

The app will use a wide tile if it is present on the first install only. If you miss this, then uninstall using the following option:

universaluninstall

Create the tile updater

Next, create another tile image – just copy the first and change the text or draw something on it. Add this to your Windows 8.1 project, and call it test.png (place it under the Assets folder).

The code to update a tile in Windows 8 is as follows:

        private static void UpdateTileImage(string image)
        {
            XmlDocument xmlDoc = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150Image);

            XmlElement imageNode = (XmlElement)xmlDoc.GetElementsByTagName("image")[0];
            imageNode.SetAttribute("src", string.Format("ms-appx://{0}", image));

            Windows.UI.Notifications.TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
            TileNotification tileNotification = new TileNotification(xmlDoc);
            tileUpdater.Update(tileNotification);
        }

A little (actually not so little) gotcha is that Live Tiles don’t work in the simulator (http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8357462e-f97b-48c2-8fea-57d47c7ead2a/do-live-tiles-updated-properly-in-the-simulator?forum=toolsforwinapps)

Testing is further complicated because tiles are not automatically pinned to the start menu; so you may deploy and not see your tile. Even worse, I noticed a few times that despite the tile not being on the start screen, on searching for it, Windows claimed it was (so I had to un-pin and re-pin).

Just write the code for Windows

Okay, so let’s just create the tile updater inside Windows 8.1 (this is the code for MainPage.xaml.cs):

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            UpdateTileImage();
        }

        private void UpdateTileImage(string image)
        {
            XmlDocument xmlDoc = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150Image);

            XmlElement imageNode = (XmlElement)xmlDoc.GetElementsByTagName("image")[0];
            imageNode.SetAttribute("src", string.Format("ms-appx://{0}", image));

            Windows.UI.Notifications.TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
            TileNotification tileNotification = new TileNotification(xmlDoc);
            tileUpdater.Update(tileNotification);
        }

        public void UpdateTileImage()
        {
            string image = "/Assets/test.png";
            UpdateTileImage(image);
        }

If you need the XAML, it’s here:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
            <TextBlock FontSize="30" Margin="10">Image</TextBlock>            
            <TextBox Text="c:locationimage.png" Margin="10"/>
        </StackPanel>
        
        <StackPanel Orientation="Horizontal" Margin="20" Grid.Row="1">
            <TextBlock FontSize="30" Margin="10">Text</TextBlock>
            <TextBox Text="My Phone Number: 123456 890 12" Margin="10"/>
        </StackPanel>

        <Button Grid.Row="2" Click="Button_Click">Update</Button>

Conclusion

You now have a little program for Windows 8.1 that updates tiles based on a button click. In the next post, we’ll move this into the shared project and call it from the phone app, too. In later posts, I’ll change and refactor the architecture, too.