Building WPF Applications with the Page Navigation framework (It's just like ASP.NET but with state...)
As I'm sure many of you are aware, this is an ASP.NET blog site, and I (allegedly) am an ASP.NET Developer, so what, you might be asking yourself, would I have to say on WPF applications that might be of value?
Well, recently I've had to forge ahead building my first WPF application which was an interesting learning experience, so I'd like to share that experience and the tips and tricks I've gleaned from the blood, sweat and tears which now sits in a congealed pool beneath my development laptop.
As with all religious journeys of discovery, there's a holy book to help with guidance and sage advice. For my pilgrimage into the distant lands of Windows application development I selected and read from the sacred book of Windows Presentation Foundation Unleashed.
The book is truly one of the best technical tomes I've ever had the pleasure of reading. I picked it up and read the book cover to cover over the course of a few evenings; it prepared me for WPF and did so in a way that enabled me to start thinking about how to build my application whilst leafing through page after page of detailed descriptions and expert knowledge.
Windows vs Pages
If you're an ASP.NET developer like myself, then you're familiar with the concept of pages and links and the general way in which navigation works. In turn, it's possible you know nothing about the way in which Windows (of the XAML variety) work, how to move from one to the next and generally make an application look and feel seamless. That's the position I approached this from.
As mentioned above, in the sacred works of Adam Nathan, I gleaned detailed technical knowledge of WPF and how it all fits together. In doing so, I happened upon a chapter which detailed the Page based model in WPF. I'd never heard of this approach and phrases leaped at me from the pages, burning into my consciousness. Phrases like "Frame" and "Link", "Uri" and "History". Before long I was wrapped up in a familiar environment and was certain that for my application (content heavy) building it with the Page framework was absolutely essential.
Not only did the Page model fit my mind set, but also my skills set and that of my colleagues (Web Designers). We could build an app in a way which was familiar whilst taking benefit of all the great functionality you get from WPF.
Now, you might think different, but as I said earlier, this is my first WPF application and it felt "right" to go down this direction.
Unlike HTML pages where you can just slap an anchor into the code to allow linking: -
window.location.href = "Blah.htm";
There's a lovely little service inside WPF which helps with Navigation, and it's, unsurprisingly, called the NavigationService. It's relatively simple to use too.
NavigationService.Navigate(new System.Uri("Blah.html", UriKind.RelativeOrAbsolute));
As well as the appropriately named Navigate there's also a cool range of methods such as GoBack and GoForward as well as a range of events to let you monitor and intercept happenings Navigating, Navigated and LoadCompleted to name just a few.
Using the Navigation service and hooking up buttons or other programmatic devices to the flow of your application is such a natural way to work if you're already familiar with Web Application development.
There's a very simple way to grab a reference to the currently displaying page in your application and then change the Uri currently being displayed, you just need to go up the tree a little before finding the right place: -
((NavigationWindow)(Application.Current.MainWindow)).Navigate(new System.Uri("Blah.xaml", UriKind.RelativeOrAbsolute));
Dirty? Unwindows? Perhaps.
Begone Back and Forward Buttons!
It's actually unbelievably simple to hide the Navigation buttons
this.ShowsNavigationUI = false;
This will result in the buttons show here and it's entire bar being removed, which is pretty funky, what you effectively have now is a "popup window"; in my instance, the application is running full screen which with the navigation removed becomes a super sexy user interface.
Wrap up your functionality in a nice warm cuddly user control blanket
WPF, just like ASP.NET has the concept of Usercontrols, which is excellent, I am a prolific usercontrol user, so much so, Dave Sussman regularly questions why I've used a usercontrol to wrap up even the most page specific of functionality.
Adding a usercontrol is really easy, amazingly so in fact. There's even a template built into Visual Studio for them: -
Once the control is all written (here's a sample control I have which is in a different assembly from that of the main application), it's very easy to reference and include in your pages.
<Grid x:Name="AlertMechanism" Margin="0">
<Image Source="AlertLayer\Incoming_call.png" Opacity="1" x:Name="AlertImage1" MouseDown="AlertImage_MouseDown" Visibility="Hidden" Stretch="None"></Image>
<Image Source="AlertLayer\visitor_alert.png" Opacity="1" x:Name="AlertImage2" MouseDown="AlertImage_MouseDown" Visibility="Hidden" Stretch="None"></Image>
The reference in the Page tag and the instance looks something like this: -
<Grid Background="Black" Width="Auto" Margin="0" Opacity="1">
Cool huh? Just like ASP.NET in terms of the flow and concept.
HTML Content and WPF applications
One of the really cool things for web developers is that we can very quickly, using the <Frame /> control, link to and use HTML content we've already built including existing web sites and assets without any changes needed to them whatsoever.
So here we are, with our Frankenstein's Monster of applications, and on cue, the monster goes nuts and tries to kill everyone...
This is where my application came a little unstuck, you see, the <Frame /> control in WPF is the least WPF control in the toolbox, in fact, it's not WPF at all, but rather just seems to create an ActiveX Internet Explorer object which is embedded into your application.
The result of this is somewhat irritating; the <Frame /> when containing HTML content can't be overlayed with WPF content. Not only that, transforms don't work. This problem is detailed elsewhere, here's an example.
Don't touch the edges!
One problem I ran into whilst trying to embed HTML within my WPF application was the existence and stubbornness of the borders which the frame's were showing when pointing at HTML content.
As the application I was writing had to load both local content (HTML pages on disk) and remote content (out in the wild and uncontrollable web it's self) I had to solve this problem in rather a novel way as the only way you can disable the borders (which I could find) was the actually modify the HTML of the pages you're loading.
As you can see from the above, no frame border is visible on the frame and it's HTML content butts up smoothly with the outer WPF content, in fact, the above image is labeled slightly misleadingly, the dark gray bar inside the Frame section is actually WPF, whereas the two lighter gray bars are actually HTML, can you see the join? (That's a rechtorical question, of course you can, they're different colours, but that's all part of the design of the application, it could be very easily made the same colour and there'd be no join at all.).
How was this achevied? Well, through nasty hacking and having pages load pages which have specific styles set up on them: -
Firstly, here's the Frame declaration: -
<Frame Name="ContentFrame" Grid.Row="1" Width="Auto" Height="Auto"></Frame>
Then the code behind for the Page which hosts the Frame: -
string urlToNavigateTo = System.Web.HttpUtility.UrlEncode("http://localhost:30000/Blah.htm");
Uri frameUrl = new Uri(@"http://localhost:30000/BorderRemovingFrame.asp?FrameSrc=" + urlToNavigateTo);
LocalServerManager.StartCassini(); //You can use IIS just as easily we only used a local server as we needed offline access
ContentFrame.Source = frameUrl;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<iframe id="innerFrame" frameborder="0" style="overflow:hidden;border-style:none;padding:0px;margin:0px;" scrolling="auto" height="100%" width="100%" src='<%= Request.QueryString["FrameSrc"] %>' />
The Page loaded into the iFrame, in the Frame of the Page ... (erm ... yes, that's right) ... is just a normal HTML page: -
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
Messy, but it does appear to work.
Using the above method I build a WPF application which hosted both content as Pages in XAML and HTML; my HTML pages even contained Flash files, which is really funky - WPF loading Flash - with the HUGE amount of Flash content out there, it's great to be able to take advantage of and use it.
If you are in control of the devices onto which your applications will run you can modify the Start Navigation sound in windows to prevent the "click" noise every time you move from page to page.
It's all pretty cool. Feel free to share any tips or guidance on how best to use the controls and functionality I've mentioned here. I'm almost certain there are better ways to do things.
Many thanks to RBT and the designer for their help in putting this application together.