Migrating to WPF on Windows, with ease (with Modern UI).

Being that I work for a big corporation and work with Microsoft tooling, that means that we have close to 1 gazillion Winforms/desktop apps. How does one try to embrace the future, with that old, dead technology lying around?

What I mean is, there are two problems:

  • There are entire segments of developers who don’t know anything other WinForms development.
  • End-users don’t know anything other than the traditional desktop look and feel.

So, how does one usher these two groups to new/exciting world of WPF, XAML, Windows Apps and beyond?

Enter WPF:
Well, WPF has been around for a while – but it’s just been sitting there, dormant. Sure, you can use MVVM with it, but application navigation, layout, theming, etc. all needed to be done by hand. It’s technically better than WinForms, but it’s still quite painful to work in just pure WPF.

Earlier in the week I was working with a developer who is writing a new app, and wanted to use that as an opportunity to learn WPF. I pair-programmed with him and we quickly fell into this same ol’ pitfalls until…

I look in NuGet and search for “wpf ui”, hoping that there is some popular, common framework by now that makes WPF less painful. And look at the first hit:

image

I install it, and I’m lost. I don’t know where to start. So, I click on that “Project Information” link in the NuGet Package Explorer and that brings me here:

http://mui.codeplex.com/ which redirects you to here: https://github.com/firstfloorsoftware/mui where I look at the Wiki and see there is a .vsix installer here:

Modern UI for WPF Templates
https://visualstudiogallery.msdn.microsoft.com/7a4362a7-fe5d-4f9d-bc7b-0c0dc272fe31

I install this, and voila – if I do File –> New Project in Visual Studio, I see new project templates:

image

If you choose that “Navigation Application”, that gives you a working demo which GREAT features right out the box! You have a settings page (with working theming), about page, help navigation, etc.

image

Getting Start with Modern UI:
I wanted to get familiar with this, so I immediately thought I’d whip up a simple UI for my new Division42 Network Tools NuGet package. This app has 4 pieces of functionality: ping, traceroute, port scan, and whois.

So, I need 4 screens:

image

So, modify the PagesHome.xaml to reflect a tab layout. When you click a heading on the left, it navigates to a XAML page on the right:

<mui:ModernTab Layout="List" SelectedSource="/Pages/NetworkTools/Welcome.xaml">
    <mui:ModernTab.Links>
        <mui:Link DisplayName="welcome" Source="/Pages/NetworkTools/Welcome.xaml" />
        <mui:Link DisplayName="ping" Source="/Pages/NetworkTools/Ping.xaml" />
        <mui:Link DisplayName="traceroute" Source="/Pages/NetworkTools/TraceRoute.xaml" />
        <mui:Link DisplayName="port scan" Source="/Pages/NetworkTools/PortScan.xaml" />
        <mui:Link DisplayName="whois" Source="/Pages/NetworkTools/Whois.xaml" />
    </mui:ModernTab.Links>
</mui:ModernTab>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

That’s pretty simple – so then I go create those pages:

image

Each page works similarly, you type something in a textbox and click a button. Here is how the Ping.xaml page looks:

<TextBlock Text="PING" Style="{StaticResource Heading2}" />
                
<TextBlock Text="Host or IP:"/>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="80"/>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Column="0" Text="{Binding HostName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <Button Grid.Column="1" Content="Ping" Command="{Binding Mode=OneWay}"/>
</Grid>
<ListView ItemsSource="{Binding PingResults}">
</ListView>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

and at the top of the file, I do declare a ViewModel to which this binds:

<UserControl.DataContext>
    <viewModels:PingResultViewModel/>
</UserControl.DataContext>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

and that results in a working screen like this:

image

As you might imagine, pretty much all of the magic is in the ViewModel – so how is that setup?

Binding to a ViewModel and using MVVM:
As I mentioned in a previous post – this API needs some work and is inconsistent. Because of that, each ViewModel is slightly different too. Generally speaking though, they all pretty much the same thing:

  • Use INotifyPropertyChanged to let that “host name” field be bindable.
  • Use an ObservableCollection<TResult> and bind the collection of results, to a list on the screen.
  • Use an ICommand to enable/disable the button, and to bind the .Command of that button to the code that actually starts executing.

INotifyPropertyChanged is a simple interface that simply lets consumers be notified when a property has been updated. WPF/XAML supports this natively. If you {Bind} to a property that uses this, the WPF control will update when the data structure property updates.

ObserveableCollection’s are the same idea, except it’s for a collection. WPF/XAML can bind a list/grid control to an ObservableCollection and will update whenever the collection changes. That is, whenever you add to, remove from, or clear the collection.

ICommand is a pretty clever idea too. It allows you to bind a button to a ViewModel property. This is good because it does two things. It checks the CanExecute() method to see if the button should be enabled or disabled, and if it’s enabled – it binds the Click event to the Execute() method of the ICommand. Put another way, it let’s you bind a button directly to your existing code without have to write any new code!

Setting the default color/theme:
For now, I left the settings screen in there, and one can change the colors, but it doesn’t persist anywhere. Instead, on every launch the app loads with a default theme (light or dark), and with an accent color. How I addressed this, was I made a static instance of that existing settings ViewModel in the App.xaml.cs:

/// <summary>
/// Gets the current AppearanceViewModel.
/// </summary>
public static AppearanceViewModel CurrentAppearanceViewModel
{
    get
    {
        if (_currentAppearanceViewModel == null)
            _currentAppearanceViewModel = new AppearanceViewModel();

        return _currentAppearanceViewModel;
    }
} private static AppearanceViewModel _currentAppearanceViewModel = null;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then, to make sure that the ViewModel gets loaded, I touch it from the constructor of MainWindow.xaml.cs:

AppearanceViewModel appearance = App.CurrentAppearanceViewModel;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

I also have the built-in PagesSettingsAppearance.xaml constructor use that same static reference:

this.DataContext = App.CurrentAppearanceViewModel;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

OK, at this point – everything is bound to that same instance of the ViewModel, but it’s still taking the default theme and color. To change that, I modified the constructor of the AppearanceViewModel to this:

image

Now, whenever the app loads, it defaults to black theme with a green accent color.

Changing the name in the title bar, and the icon:
This ended up being easier than I thought it was going to be. The name, is set in the MainWindow.xaml, in the attributes at the top:

image

The icon is a little more tricky – it’s not just a png or gif. Note the “LogoData” above (just below the highlighted “Title”). THAT is how the logo gets drawn. I believe this is .svg and you can get the .svg coordinates from an existing file. Where do you find something like this? For me, I went to:

http://modernuiicons.com/

Download the .zip, find the .svg that you want, and copy those coordinates. Paste those into the “LogoData” in MainWindow.xaml, and you got yourself a logo. I used “appbar.diagram”, which made the window look like this:

image

Pulling it all together:
When I used the techniques above, I fleshed out the various screens and came up with this:

image

image

image

image

image

It’s fairly crude and I think it will break if you try to break it – but it’s amazing I was able to learn this framework AND create a working app in a couple of hours. Best of all, since it’s all MVVM, there’s very little code behind at all.

I’ll improve this over time, but this will do for version 1.0.

Browse/download the source:
I didn’t include a lot of complete source code in this blog post because I’ve actually added this WPF project to the GitHub project for the component.

image

You can browse or download that here:

https://github.com/Division42LLC/Division42.NetworkTools

In fact, I incremented the Division42.NetworkTools library also because I had overridden the .ToString() in a couple of classes. That means I also published a minor release to NuGet too.

Next steps…
I am completely and utterly pleased with this Modern UI framework! Not only could I crank out a working app in a couple of hours, the framework is very clean and well-written. If I need to create a desktop app, I would definitely use this. Even if I wanted a different look/feel – this is still a great framework to start from.

So, at some point I’ll clean up the NetworkTools API – and maybe I’ll spruce up this little WPF front-end too. Like I said, this is fine for version 1.0 (or 1.1, now) – and this can just be cleaned-up and matured over time.

If you have any ideas for this component or the UI – by all means pull it down make some changes and do a pull request!

Posted in Computers and Internet, Development Tools, General, New Technology, Open Source, Professional Development, Uncategorized, WPF and MVVM
One comment on “Migrating to WPF on Windows, with ease (with Modern UI).
  1. […] my employer, I will be doing what I can to introduce Modern UI for WPF as our core framework. I’m very impressed with it and think that it makes the migration to MVVM […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 2 other followers

%d bloggers like this: