Draw Your Own Pie Chart dynamically

In this tutorial, I’ll show you how to draw your own pie chart just using lines and a list of data items. The code itself is in C#, but the concept can be used on any platform that has a drawing library and ArcSegments.

You can view the entire solution in this GitHub Gist. Okay, let’s get started!

Setting Up

The first thing you’ll need to understand is how the platform’s drawing library makes arcs. In this code, I’ll be using RadPath from Telerik UI for Xamarin most libraries work the same way. RadPath lets you use a custom Geometry which has RadArcSegment object, which has helpful StartAngle and SweepAngle properties.

Let’s start with the data model, with simple Title and Value properties:

public class ChartDataPoint
{
    public string Title { get; set; }
    public double Value { get; set; }
}

Next, let’s create a list that is populated with some activities to represent a 24 hour period.

var dataPoints = new List<ChartDataPoint>
{
    new ChartDataPoint { Title = "Work", Value = 9 },
    new ChartDataPoint { Title = "Commute", Value = 1.5 },
    new ChartDataPoint { Title = "Leisure", Value = 6 },
    new ChartDataPoint { Title = "Sleep", Value = 7.5 },
};

Finally, to finish the setup, create a list of colors we can use for the pie slices.

var colors = new List<Color>
{
    Color.FromHex("#BAB65A"),
    Color.FromHex("#6196D1"),
    Color.FromHex("#3D4268"),
    Color.FromHex("#8A56E2"),
};

Part 1 – Creating the Slices

Now that we have some items, lets move on to generating and drawing the slices. First, we’ll create a container to put the pie and legend into. A Grid is convenient option because we can have two rows, one for the pie and one for the legend.

// Root container to hold the chart and any legend
var container = new Grid();
container.RowDefinitions.Add(new RowDefinition { Height = new GridLength(3, GridUnitType.Star) });
container.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });

Now we can start calulating the slice sizes, to do this, we need a total value to get a percentage of that total. We also need a variable to hold the current position on the 360 degree arc where the slices are rendered.

// Sum up all the values to be displayed
var totalValue = dataPoints.Sum(d => d.Value);

// Variable to keep track of where each slice ended.
double currentPosition = 0;

Now for the guts of the operation. We need iterate over the data points and create the arc segments using the data point’s Value property. The code comments will guide you through what each line does, in a nustshell here’s the lifecycle

  • Calculate the data item’s percentage of the total sum
  • Use that percentage to get what percent of 360 degree pie that slice needs
  • Create the RadArcSegment using the current position for the StartAngle and the angle percentage as the SweepAngle
  • Construct the RadPath using the ArgSegment’s geometry and a color from the colors list
// Iterate over the data points to create slices.
for (int i = 0; i < dataPoints.Count; i++)
{
    // Determine the what percentage that data item's value is of the whole
    double slicePercentage = dataPoints[i].Value / totalValue;

    // Calculate the sweep angle using that percentage amount.
    double sweep = slicePercentage * 360;

    // Create the ArcSegment using the current position and sweep
    var segment = new RadArcSegment
    {
        Center = new Point(0.5, 0.5),
        Size = new Size(1, 1),
        StartAngle = currentPosition,
        SweepAngle = sweep,
    };

    // Important - Calculate the last segment's ending angle in order to have a valid start angle for the next loop.
    currentPosition = currentPosition + sweep - 360;

    // Prepare the required PathFigure and add the ArcSegment
    var figure = new RadPathFigure { StartPoint = new Point(0.5, 0.5) };
    figure.Segments.Add(segment);

    // Create the PathGeometry and add the PathFigure
    var geometry = new RadPathGeometry();
    geometry.Figures.Add(figure);

    // Construct the RadPath
    // - Select a Fill color from the brushes parameter (important: use a modulus to wrap to the beginning)
    // - Use the Geometry created from the value
    var slice = new RadPath
    {
        Fill = new RadSolidColorBrush(colors[i % colors.Count]),
        Geometry = geometry,
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center,
        WidthRequest = 100,
        HeightRequest = 100,
        Margin = new Thickness(0, 20, 0, 0)
    };

    // This isn't necessary, but added for completion.
    Grid.SetRow(slice, 0);

    // Finally, add it to the container.
    container.Children.Add(slice);
}

At this point, you now have a full 360 degree chart, with colored slices for each of the data points representing their percentage of the whole.

Part 2 – Creating the Legend

The next phase of the operation is to create the legend. This needs to create text for each slice, as well as a marker that matches the same color of that slice. This could have been done in the same loop as creating the slice, but having a separate loop lets you decide to use a legend or not.

Again, we iterate over the data points. This time we use the Title property of the data point to create the text. To match the color, we use the same modulus to get index and assign it to a RadBorder that creates a thick bar underneath the text.

// Create a horizontal StackLayout to hold the legend items
var legendPanel = new StackLayout
{
    Orientation = StackOrientation.Horizontal,
    HorizontalOptions = LayoutOptions.Center,
    VerticalOptions = LayoutOptions.Center,
    Margin = new Thickness(0, 16, 0, 0),
    Spacing = 5
};

// Iterate over the data points and create a legend item with a matching color
for (int i = 0; i < dataPoints.Count; i++)
{
    // Use a RadBorder with only a bottom thickness and match the color to the slice
    var legendItem = new RadBorder
    {
        BorderColor = colors[i % colors.Count],
        BorderThickness = new Thickness(0, 0, 0, 2)
    };

    // Create a Label for each data point and use the Title property
    var label = new Label
    {
        Text = dataPoints[i].Title,
        FontSize = 12,
        Margin = new Thickness(0, 0, 0, 2),
        TextColor = Color.DimGray
    };

    legendItem.Content = label;

    legendPanel.Children.Add(legendItem);
}

// Insert the legend panel in the root container's 2nd row.
Grid.SetRow(legendPanel, 1);
container.Children.Add(legendPanel);

The last thing to do is add the entire container to the UI. In this example, I’m just setting the entire page’s content to the container Grid..

this.Content = container;

Wrapping Up

I hope this is useful for those times when you need ultimate control over rendering of a chart. If you need more complex setup, I recommend the RadPieChart itself, which is far more feature complete than drawing a few arcs 🙂

Talk to the View


There are many scenarios where a UI component might not have a Command or bindable property available for certain features that you need to access from the view model. There is a simple, but powerful, thing you can do to allow this and not break MVVM or testing capability, leverage the Interface.

Scenario

Let’s imagine the scenario where we have a special ListView control that has a HighlightItem method that can only be called with a direct reference to that control. There isn’t a Command or DependencyProperty (aka BindableProperty in Xamarin.Forms) alternative, so it has to be done in the code behind.

To keep this simple, here’s the page XAML…

<Page x:Class="MyPage">
    <Page.DataContext>
        <MyPageViewModel x:Name="ViewModel" />
    </Page.DataContext>

    <SpecialListView x:Name="MyListView" />
</Page>

…and here is the page code behind.

public partial class MyPage : Page
{
    public MyPage()
    {
        InitializeComponent();

        // The only way to use HighlightItem method is in the code-behind
        MyListView.HighlightItem(itemToHighlight);
    }
}

As you can see, the HighlightItem method has to be used in the code behind because it has a reference to ‘MyListView’. However, you need to use it in the view model, like this:

public class MyPageViewModel
{
    private void PleaseHighlightItemNow()
    {
        // But, you need to highlight the item from view model
    }
}

Solution

One solution for this is to define an interface.

public interface IHighlightableView
{
    void Highlight(object item);
}

With this, you can add a property to the view model and invoke the method:

public class MyPageViewModel
{
    public IHighlightableView View { get; set; }

    private void PleaseHighlightItem()
    {
        // Call the method on the interface!
        View?.Highlight(itemToHighlight);
    }
}

Now, the interface can be implemented on the page to complete the circle

public partial class MyPage : Page, IHighlightableView
{
    public MyPage()
    {
        InitializeComponent();

        // Assign this page instance to the view model
        ViewModel.View = this;
    }

    private void Highlight(object itemToHighlight)
    {
       // Now you can directly use the UI component's method
        MyListView.HighlightItem(itemToHighlight);
    }
}

Summary

This is a simple approach that will work if you don’t want to (or are not allowed to) use an existing MVVM framework’s built-in DependencyInjection and IoC features. Enjoy!

Using PowerShell to Install an SDK in a DevOps Build Pipeline

I have an open source UWP project that relies on the Ad SDK and Engagement SDK to be installed to Visual Studio. This normally isn’t a problem, because you can just run the MSI on your PC (see installation instructions here and here).

I decided to move the project into DevOps so that I get some of that CI/CD goodness to build and publish new releases automatically to the Microsoft Store. However, there was a problem, when trying to build the project in a DevOps build pipeline, you’ll get the following error:

This means that the Hosted VS2017 Agent that DevOps uses to build a UWP project doesn’t have the Extensions SDKs installed. I have two choices to move forward:

  • Use a private Agent (this uses your local PC to build the project via a server connection )
  • Find a way to install the SDK into the Hosted agent

I did try the first option. I set up the Windows Agent on my Surface Book, connected it to DevOps and it works nicely (this is why you see that early successful build early in the first screenshot above). However, it’s not a solution for me because it means my PC has to be always on and network-connected.

My friend, and MVP peer, Oren Novotny mentioned that you can actually use a PowerShell script to download the MSI file and install it to the Hosted VS2017, this was the break I was looking for!

To implement this, I downloaded the MSI files I needed from the Visual Studio Marketplace and put them into an Azure Blob. It provides me with two reliable URLs to download the files in the PowerShell script.

Next, I wrote the very simple script. It has two phases: a download phase and an install phase (with a quiet switch “/q”). Here’s what that looks like:

# Predefined Variables
$adSdkUrl = "https://dvlup.blob.core.windows.net/general-app-files/MSIs/MicrosoftAdvertisingSDK.msi"
 $servicesSdkUrl = "https://dvlup.blob.core.windows.net/general-app-files/MSIs/MicrosoftStoreServicesSDK.msi"
 $adSdkPath = Join-Path $env:TEMP "MicrosoftAdvertisingSDK.msi"
 $servicesSdkPath = Join-Path $env:TEMP "MicrosoftStoreServicesSDK.msi"

# Download the files to local temp folder
 Write-Output "downloading $adSdkUrl…"
 Invoke-WebRequest -Uri $adSdkUrl -OutFile $adSDKPath
 Write-Output "downloading $servicesSdkUrl…"
 Invoke-WebRequest -Uri $servicesSdkUrl -OutFile $servicesSdkPath

# Install the SDKs (use the "qn" flag to install silently)
 Write-Output "installing $adSdkPath…"
 Start-Process $adSdkPath -ArgumentList "/q" -Wait
 Write-Output "installing $servicesSdkPath…"
 Start-Process $servicesSdkPath -ArgumentList "/q" -Wait

Next, you need to add a Powershell step to your DevOps Build pipeline and paste in the script. Here’s a screenshot to help guide you:

Now, when the build is triggered, it will download and install the missing SDKs before building the project! Here’s the result of that build step in the Hosted agent’s console:

I hope this post finds you as soon as you need help. It took me several days, a dozen different documentation articles (some are hyperlinked above) and my MVP peers to find a good simple solution that didn’t require my PC .

LoginDialog for easy Live SDK OAuth

Almost every developer I speak with agrees that getting a nicely working OAuth flow can be difficult. After much trial and error, I’ve built a UWP ContentDialog that makes this easy and has built-in refresh token support.

Using the LoginDialog

This is very simple to use, when the app launches, instantiate the dialog by passing your Live SDK ClientID and let the LoginDialog do the heavy lifting:

// Pass your app's OAuth ClientId (sometimes called an AppId)
var loginDialog = new LoginDialog("YourAppClientId");

// *** Perform login **** //
// Case 1 - If the user was previously signed in, there is a refresh_token stored and no user-entered credentials are needed.
// Case 2 - If the access token fails, or if it's the first sign-in, the user will see a popup to enter credentials.
await loginDialog.SignInAsync();

if(!string.IsNullOrEmpty(loginDialog.Authentication))
{
    // This will be the access token you can use for API calls.
    var accessToken = loginDialog.Authentication;

    // Make your API calls with the accessToken, for example:
    var user = await apiService.GetUSerProfileAsync(accessToken);
}

This works just like a MessageDialog, but instead will show a WebView for the user to sign in, or automatically sign them in silently if they were previously logged in.

Get the full source code in this GitHub Gist.

See this tweet for a video of what it looks like when it automatically logs you in and here’s a screenshot what it looks like when the dialog needs to appear:

Dialog Visible

Operational Summary

Explaining OAuth 2.0 is outside the scope of this article, but to summarize there are a couple round trip to the authentication endpoints.

  1. Load the sign-in landing page and the user signs in with their credentials.
  2. In the redirect after successful sign-in, you’ll get an access_token and a refresh_token for that authenticated user.

The access_token is what is used in all of your API calls that verifies the application is operating on the behalf of that user. The access_token expires after a preset amount of time (e.g. 60 minutes), after which time you have to request another access_token.

Here is where the refresh_token comes in handy. You could go back to step #1 above, but this would require the user re-enter their credentials. Instead, you can just use the refresh_token  you already have for the user to get a new access_token without ever interrupting them to show a WebView.

This is what the LoginDialog does for you, when you call SignInAsync() the following happens:

  • Checks to see if there is a refresh_token stored locally. If there isn’t one, show the WebView and have the user sign in for the first time.
  • If there is a refresh_token stored locally, then use that to automatically get a new access_token without showing the dialog

Since this is general OAuth 2.0 workflow, you could probably adapt the dialog to work for other OAuth 2 endpoints by changing the dialog’s _signInUri, _signOutUri and _redirectUrl values.

Wrapping Up

The LoginDialog lets you have the least amount of interruption to the user while quickly getting an access token for your API use.

The RS5 Update Resources and Tutorials

My friends at Microsoft have put together an amazing compilation of resources of all the new stuff available in the Windows 10 October 2018 Update (aka RS5). Below is a list of some of those resources relevant to developers and enthusiasts, enjoy!

SDK Downloads

New Samples and Sample Updates

  • XAML Hosting API Sample uses the new XAML hosting API and C++/WinRT to demonstrate usage of XAML controls within a Win32 application.
  • Customer Orders Database Sample has been updated to use the new Data Grid and other new controls in RS5.
  • Photo Editor C++/WinRT Sample has been updated to build with the RS5 SDK and to use the new single_threaded_observable_vector function to create an IObservableVector for data binding. An additional performance update will be coming out in a couple weeks.
  • Data binding and MVVM is a new topic that was identified as a need based on the Code First program. The topic describes the benefits of using the MVVM design pattern and how it has been applied to the sample apps.

Videos

Application Developer Resources

Driver Developer Resources

Epic Build 2018 Playlists

Your one-stop shop for Microsoft Build 2018 sessions, slide decks, post links, keynotes and much more.

Living Document Note:

These Playlists will continue to be reordered and updated in real time as new content becomes available. However, the links will always remain the same and you can refresh the playlist page to get any updated ordering/content.

Easy XamlCompositionBrushBase

The upcoming Windows 10 update, the Fall Creator’s Update, introduces a new design system, Microsoft Fluent Design System. The design system has 5 major building blocks:

  • Light
  • Depth
  • Motion
  • Material
  • Scale

You can use any of these in a multitude of ways, however Microsoft has made it very easy to use in the latest Preview SDK (16190). Some of the things that used to be relatively hard, or at the least verbose, can now be done with a few lines of code.

Today, I want to show you the XamlCompositionBrushBase (aka XCBB). Before I do, let’s briefly run through a XAML Brush and a Composition Brush.

The XAML Brush

We use Brushes in almost everything we do, they paint the elements in an application’s UI. For example, UIElement.Foreground and UIElement.Background both are of type Brush.

The most commonly used Brush is the SolidColorBrush, an example of setting the Foreground brush:

XAML

&lt;TextBlock x:Name=&quot;myTextBlock&quot; Foreground=&quot;Black&quot;/&gt;

C#

myTextBlock.Foreground = new SolidColorBrush(Colors.Black);

There are other brush types, such as ImageBrush, that are good for specific approaches, that make our live easier because it would otherwise require more work to achieve the same result.

The Composition Brush

A composition brush utilizes the relatively new Composition layer in Windows 10. This layer sits underneath the XAML layer where all your XAML elements live. You can do a lot with the composition layer, animations, effects and more. The mechanism that paints Composition layer elements also uses a “Brush”, however it’s a type of Composition Brush and cannot be used with your XAML directly.

XamlCompositionBrushBase

With the release of the 16190 preview SDK, you can now use the Composition layer and XAML layer together by using the XamlCompositionBrushBase!

This is BIG news because the XCBB allows for interoperability between the Composition layer and the XAML layer and lets you set up composition effect without needing to implement a behavior or other more advanced setups. As an example, let’s create a Brush that applies the Win2D Invert Effect,

Note: I wanted to keep this as simple as possible to focus on XCBB, you can expand on this with more complex Effects, such as the GaussianBlur here.

First, let’s example the XCBB’s two methods that you want to override:

  • OnConnected
  • OnDisconnected

So, here’s our starting point:

public class InvertBrush : XamlCompositionBrushBase
{
    protected override void OnConnected()
    {
        // Set up CompositionBrush
        base.OnConnected();
    }

    protected override void OnDisconnected()
    {
        //Clean up
        base.OnDisconnected();
    }
}

Now, for the awesome part… the XCBB has a CompositionBrush property! All you need to do is instantiate your effect. Here’s the completed Brush code and I’ve broken it down to the important steps:

public class InvertBrush : XamlCompositionBrushBase
{
    protected override void OnConnected()
    {
        // Back out if it's not ready yet
        if (CompositionBrush == null) return;

        // 1 - Get the BackdropBrush
        // NOTE: BackdropBrush is what is behind the current UI element (also useful for Blur effects)
        var backdrop = Window.Current.Compositor.CreateBackdropBrush();

        // 2 - Create your Effect
        // New-up a Win2D InvertEffect and use the BackdropBrush as it's Source
        var invertEffect = new InvertEffect
        {
            Source = new CompositionEffectSourceParameter("backdrop")
        };

        // 3 - Get an EffectFactory
        var effectFactory = Window.Current.Compositor.CreateEffectFactory(invertEffect);

        // 4 - Get a CompositionEffectBrush
        var effectBrush = effectFactory.CreateBrush();

        // and set the backdrop as the original source
        effectBrush.SetSourceParameter("backdrop", backdrop);

        // 5 - Finally, assign your CompositionEffectBrush to the XCBB's CompositionBrush property
        CompositionBrush = effectBrush;

    }

    protected override void OnDisconnected()
    {
        // Clean up
        CompositionBrush?.Dispose();
        CompositionBrush = null;
    }
}

Now that the Brush’s definition is complete, how do we actually use it? This is the most exciting part… you use it like any other Brush in XAML!


&lt;Grid&gt;
    &lt;Grid.Background&gt;
        &lt;brushes:InvertBrush /&gt;
    &lt;/Grid.Background&gt;
&lt;/Grid&gt;

Showtime

Here’s an example implementation. I have an Ellipse with an ImageBrush and it’s currently showing Tuvok (full disclosure: I’m a Trekkie AND a Star Wars fan)

&lt;Ellipse x:Name=&quot;ImageEllipse&quot;&gt;
    &lt;Ellipse.Fill&gt;
        &lt;ImageBrush ImageSource=&quot;{Binding SelectedCharacter.ImagePath}&quot; Stretch=&quot;UniformToFill&quot; /&gt;
    &lt;/Ellipse.Fill&gt;
&lt;/Ellipse&gt;

Sketch (2)

If I put another matching Ellipse using my custom InvertBrush on top of the Tuvok Ellipse, here’s the result:


&lt;Ellipse x:Name=&quot;ImageEllipse&quot;&gt;
    &lt;Ellipse.Fill&gt;
        &lt;ImageBrush ImageSource=&quot;{Binding SelectedCharacter.ImagePath}&quot; Stretch=&quot;UniformToFill&quot; /&gt;
    &lt;/Ellipse.Fill&gt;
&lt;/Ellipse&gt;
&lt;Ellipse&gt;
    &lt;Ellipse.Fill&gt;
        &lt;strong&gt;&lt;brushes:InvertBrush /&gt;&lt;/strong&gt;
    &lt;/Ellipse.Fill&gt;
&lt;/Ellipse&gt;

Sketch (3)

Notice how it only inverted what was directly behind the Ellipse and not the page background or the drop shadow?

Level Up

In the case of the InvertEffect, we don’t have any effect variables to update, so there’s no need for a DependencyProperty to set initial values of the effect. However, in most cases, you will need a DependencyProperty in your XCBB to tweak the effect’s values.

To see this, look at the BackdropBlurBrush example here and notice how the Blur.BlurAmount effect property can be updated by using a ScalarParameter when calling CreateEffectFactory.

I hope I was able to clarify how easy it is to get started using the XCBB and how this makes things easier for XAML devs to get the benefits of working with the Composition layer without doing a lot of work.

Happy coding!

Lance

Surface Dial and Real-Time Video Effects

I was given a Surface Dial the other day and I thought “what can I do with this to create a better experience for the user”. One thing came right to mind, applying real-time video effects.

I have a UWP app in the Windows Store, Video Diary, where you can apply real-time video effects while recording a video. One of the features of these effects is to increase or decrease the video effect’s properties. For example, the intensity of a Vignette effect, here’s what I want:

2016-11-10_19-39-54

So I whipped out the RadialController API documentation and dug in. It turns out to be extremely simple, here is the result:

 

Let’s take a look at the code.

Note: Going into the specifics of applying real time video effects is out of scope for this article. You can see the source code to this demo app here  to see how it’s done, or  you can see my DynamicBlur Video Effect contribution to the official Win2D Demo app.

Since I didn’t want to go too crazy with the Surface Dial for my first demo, I thought about how the controller can be interacted with; turning the dial and clicking down on the dial. So I thought, why not use the menu to select a video effect and the rotation to change the effect’s intensity. Let’s get started.

First, when the page loads, I need to get a handle to the RadialController:

 

dialController = RadialController.CreateForCurrentView();

 

Next, I want to hook into the event that fires when the dial is turned and set the rotation resolution:

dialController.RotationResolutionInDegrees = 1;
dialController.RotationChanged += DialControllerRotationChanged;

 

Now, I want to make some room before adding my custom menu items, so I grab a handle to the RadialControllerConfiguration and assign it just one default menu item:

var config = RadialControllerConfiguration.GetForCurrentView();
config.SetDefaultMenuItems(new[] { RadialControllerSystemMenuItemKind.Scroll });

 

I need to add some menu items to the circular menu that appears when the dial is clicked. For this I just iterated over the list of effects I added and create a RadialControllerMenuItem for each one and hook into it’s Invoked event:

foreach (var effect in PageViewModel.VideoEffects)
{
    // Create a menu item, using the effect's name and thumbnail
    var menuItem = RadialControllerMenuItem.CreateFromIcon(effect.DisplayName,
 RandomAccessStreamReference.CreateFromUri(new Uri(effect.IconImagePath)));

    // Hook up it's invoked event handler
    menuItem.Invoked += MenuItem_Invoked;

    // Add it to the RadialDial
    dialController.Menu.Items.Add(menuItem);
 }

 

The menu item’s Invoked event handler is fired when an effect is chosen by the user, I get the selected effect by checking what the DisplayName of the menu item was using the RadialControllerMenuItem sender

private async void MenuItem_Invoked(RadialControllerMenuItem sender, object args)
{
    var selectedEffect = PageViewModel.VideoEffects.FirstOrDefault(
        e => e.DisplayName == sender?.DisplayText);

    // apply effect
 }

 

At this point, the effect is applied to the video stream. So we need to switch our focus to the RadialControler’s RotationChanged event handler. This is where I can get the rotation delta (which direction was it turned and by how much) from the RotationDeltaInDegrees property of the RadialControllerRotationChangedEventArgs. 

Since I also have a slider in the UI for the user to change the value (because not every user is going to have a Surface Dial!), I update the slider’s value directly:

 

private void DialControllerRotationChanged(RadialController sender, RadialControllerRotationChangedEventArgs args)
{
    SelectedEffectSlider.Value += args.RotationDeltaInDegrees / 100;
    UpdateEffect();
}

 

Now in the UpdateEffect method, I can use the slider’s new value to apply the effect change:

 

private void UpdateEffect()
{
    // Update effect's values
    PageViewModel.SelectedEffect.PropertyValue = (float) SelectedEffectSlider.Value;
    effectPropertySet[PageViewModel.SelectedEffect.PropertyName] = (float) PageViewModel.SelectedEffect.PropertyValue;
}

 

That’s it! Check out the video above to see the app in action and see the full source code here on GitHub.

 

 

 

 

One XAML for UWP, Windows 8.1 and Windows Phone 8.1

I was recently involved in a conversation on how to have the easiest way to maintain a XAML view across a UWP (Universal Windows Project) a Windows 8.1 and Windows Phone 8.1 projects. There are a few options and the most straight forward way to do it with a UserControl residing in a Portable Class Library. However…

What if you wanted to have tailored code for each without littering your code with #ifdef but still share a XAML view?  The XAML View might be what you’re looking for. This is a tutorial on how to do just that. An example project is available for download at the end of the article. Let’s get started:

Step 1:

Create a new 8.1 Universal app

tpx1

Step 2:

Move the Shared, Windows and Windows Phone projects out of the virtual folder and delete the folder (you can cut/paste or just click and drag them). The solution should look like this now

tpx2

(Note: If you’re doing this to a solution that has many other projects, you might want to skip this step and add the UWP project to the virtual folder instead)

Step 3:

Add a new UWP project to the solution and name it the same as the 8.1 app, but with the Universal suffix.

tpx3

(Your solution should look like this now)

tpx4

Step 4:

Add a “Views” folder to each of the projects

tpx5

Step 5:

Within the Universal project, right click and Add > New Item > XAML View

tpx6

Step 6:

Move the new XAML View to the Shared project’s Views folder and change the namespaces in the view to match

tpx7

Step 7:

Delete App.xaml from the Universal project and add a reference to the solution’s Shared project (Note: this is in the new Shared projects references section)

tpx8

Step 8:

Here’s where the magic happens. We’ll be adding a code behind for this view in each of the projects! I’ll break this down into sub-steps:

  1. Add a new class to the Windows 8.1 project’s Views folder (Add > New Item), name it as if it were the code-behind for the view. In this case it would be SharedPage.xaml.cs
  2. Change the namespace of the class to Views
  3. Add the public, sealed and partial modifiers to the class
  4. You’ll next need to inherit from the Page class
  5. Add a page constructor
  6. Now you can copy and paste this new class into each of the platform projects (remember, we do not need it in the Shared project)

This is what it should look like

tpx9

Step 9:

You’ll need to quickly pop into the Build Configuration Manager and check off Build and Deploy for the Universal app (you can find Configuration Manager in the target dropdown or in the Build menu)

tpx10

Step 10:

Lastly, for the purposes of this demo, go to App.xaml.cs and change the initial launch target (MainPage) to be the new shared page (SharedPage). I could have put a button on MainPage for each app, but let’s keep this tutorial as short as possible.

tpx11

Final result!

This is the same XAML View compiled with different code-behind files 🙂

tpx12

(NOTE: I put a TextBlock on the SharedPage and update the text in each constructor to show which platform launched it. Here are the WP8.1 emulator, Windows 10 PC and Windows 10 Mobile Emulator running their apps all showing the same XAML view).

Source Code

Download the Sample App Source Code From Here

Bonus:

The Telerik Universal Control can be used in the shared page as long as each of the projects have a reference to the Telerik UYI for Windows Universal DLLs. Send me a tweet and show me what you’ve done, I’ll RT your awesomeness!