RSS 2.0
# Friday, December 14, 2007

This is the third in a series.

Design

As with any good development effort, we begin by designing the solution, complete with an assessment of the pros and cons of each option. Of course, as with any good development effort, we begin with every intention of designing the solution, but we usually skip right past this to writing the code since designs are never any fun. Design: There is a server and there are 0 or more clients that want to control what the server spits out the sound card.

Right. With the design done, let's talk about implementation options:

As I see it, there are two options for implementing the functionality as exhaustively enumerated in Part 0 of our series: 1) A standalone application in which we host and automate a Windows Media Player control, 2) A plug-in to the existing standalone Windows Media Player in which we automate the instance of Windows Media Player in which we are hosted.

So, which of these approaches is better? I have no idea. So, we'll design the approach in such a way that the end user (client machine) doesn't care. How? SOA, WCF, and other TLA's. I figure I can hide my media playing component/service/application behind a WCF connection point. Then, if hosting the ActiveX control fails miserably or creating the WMP plug-in doesn't play nicely with .NET, then I'll always be able to call some arcane WIN32 function to dump directly to the sound card or convert to Linux, convert all of my WMA's to raw .au files and just cat [file.au] > /dev/audio.

Of course, that's an exaggeration. I'm sure one of the two approaches will work fine. I just don't know which. So, hiding the solution behind a WCF connection point should give me the flexibility to try both approaches without rewriting the clients.

At this point, I find that there are a couple of interesting questions to which I do not know the answer. This, of course, will not keep me from plunging headlong into the implementation. They're just questions.

  • If I host an instance of the WMP ActiveX control, does it still do all of the clever background work of sniffing media information from the Internet, listening for changes in my media library, and downloading album art or is it just a numb playback machine using the existing media library?
  • What are the multi-threading characteristics of the WMP library? Will I have to perform the read/write locks, or will WMP take care of that for me?
Friday, December 14, 2007 2:54:53 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Tuesday, December 11, 2007

Build or Buy decision

This is the second in a series. The overview is here.

State of the industry:

So far, I can find a bunch of solutions to stream media to a client. That is, given a server containing a bunch of media, Spit it out to a client machine (PC, cell phone, television, etc.) so you can consume it wherever you are. This doesn't cut it for our purposes. Instead, we want the media to play at the source, but be able to change the behavior of the media player at the host from a client somewhere.

Items of interest:

So far, that's all I have. So, if I want a remote control to work on a laptop, pocket pc, smartphone, (and maybe a website), it looks like I'm out of luck in the commercial market. So, it looks like the build option is the one for me.

Of course, that's what I expected to choose. Otherwise, this would have been a really short series.

Tuesday, December 11, 2007 2:51:22 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Monday, December 10, 2007

This is the beginning in a series of unknown length regarding controlling the Windows Media Player from remote machines. So far, I'm at the requirements phase. Next will come industry research (buy or build), but I'm pretty sure I'm going to build something anyway (even if there are perfectly usable buy options -- I'm just stubborn that way).

Here are the requirements in no particular format or order:

  • I have a Server 2003 machine running with its audio output hooked up to the whole house audio system (okay, it's just two speakers in a different room, but there's a volume control in there, so I can call it what I want). The server houses the media collection and runs a uPnP server (TwonkyMedia) for the rest of the machines in the house to stream.
  • I can play audio through the server if I remote terminal to the server, run media player, and choose some songs. That, of course, completely fails the Wife Acceptance Factor (WAF).
  • What I would like is a service or application running on the server to be ready to play at all times, and have the rest of the machines (including handheld and smartphone) be able to dynamically change the playlist, start, stop, fast forward (most important) and rewind.
  • I'd like this to run as part of Media Player. I'm not sure why, since SOA dictates that we're just exposing a service, not a particular technology. I think it's mainly because I'm used to maintaining the media library in WMP and I don't want to change.
  • An added bonus is if there were a sidebar gadget for my wife's Vista machine (yes, I'm still on XP until some of the performance issues are worked out), and a 10' UI for the media center PC in the family room.
  • Since the uPnP server streams media to all of the important places (laptop and media center), there's no reason to mess around with streaming the actual audio to the remote control client, though the now-playing art would be nice.

That's it. In those few, short paragraphs, I have managed to cut out a lot of work for myself. I'm looking for a media player with remote controls that run on smartphone, pda, xp, and as a sidebar gadget. I'd like it to run on server 2003 every time it starts up, be multi-remote control robust, and extremely fault tolerant.

Now, to the buy or build comparisons...

Monday, December 10, 2007 7:48:37 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Wednesday, October 03, 2007

The Scribble application is back in Visual Studio 2008! Ok, it's not WPF, but at least it exists (http://msdn2.microsoft.com/en-us/library/06s5xsah(VS.80).aspx). It even has a tutorial on extending the application to include a plug-in model (http://msdn2.microsoft.com/en-us/library/11z7687s(VS.80).aspx).

It probably pays to start with the WinForms version of the example. Make sure you change the target CPU to Win32 (unless you're on an Itanium) so you'll be able to compile and debug. From there, you can wander through the code and figure out why all of the UI is created via code rather than the clever UI form designer that has been so nicely integrated into VS Orcas (2008). If you figure it out, let me know. Reading UI and UI layout code isn't my idea of a good time.

After the WinForms example, take a look at the MFC sample. It's a little involved, but definitely a nice example of integrating native C++/MFC with managed libraries using managed C++ proxy classes to expose managed interfaces to the plug-in libraries.

Wednesday, October 03, 2007 9:41:12 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
# Thursday, August 02, 2007

This segment looks at localization of button toolips and the descriptive text that appears in the statusbar when a menu item or button is active. The code for this segment is available at http://www.cavinconsulting.com/Code/Scribble11.zip.

The general idea is that we want to give helpful hints to our user when if comes to deciphering the little icons on the screen. Normally, we attach tooltips to tell the user what each icon does. However, some application go the extra step to make it clear what a command does by placing a brief description of the actions in the status bar. This description appears when the menu item or toolbar button becomes "active" and goes away when the button becomes inactive.

For our purposes, Active will mean the mouse is over a button and or that a menu item is the focus item on in the application (the latter lets us display description information even if the menu becomes active via Alt+f,arrow keys).

To implement the GotFocus and LostFocus event handlers for the MenuItems, I went down a number of paths including style triggers, control templates, and adding event handlers to each MenuItem individually. The implementation I landed on was to apply an EventSetter to the MenuItem style like this:

<Window.Resources>
  <Style TargetType="{x:Type MenuItem}" x:Key="{x:Type MenuItem}" >
    <EventSetter Event="GotFocus" Handler="OnGotFocus"/>
    <EventSetter Event="LostFocus" Handler="OnLostFocus"/>
  </Style>
</Window.Resources>

From there, we change the declaration of each MenuItem to look like this:

<MenuItem Header="{x:Static scribble:ScribbleCommands.FileMenuText}">
  <MenuItem Command="{x:Static scribble:ScribbleCommands.New}" />
  <MenuItem Command="{x:Static scribble:ScribbleCommands.Open}" />
  <MenuItem Command="{x:Static scribble:ScribbleCommands.Save}" />
  <MenuItem Command="{x:Static scribble:ScribbleCommands.SaveAs}" />
  <Separator />
  <MenuItem Command="{x:Static scribble:ScribbleCommands.Exit}" />
</MenuItem>

Now, all we have to do is implement the event handlers:

public void OnGotFocus(object sender, RoutedEventArgs e)
{
  DisplayCommandDescription(sender);
}

public void OnLostFocus(object sender, RoutedEventArgs e)
{
  ClearCommandDescription(sender);
}

Now, the implementation of the Display and Clear functions can look something like this:

private void DisplayCommandDescription(object sender)
{
  MenuItem item = sender as MenuItem;
  if ((null != item) && (null != item.Command))
  {
    ScribbleCommand command = item.Command as ScribbleCommand;
    if (null != command)
    {
      StatusText = command.Description;
    }
  }
  ScribbleButton button = sender as ScribbleButton;
  if (null != button)
  {
    StatusText = button.Description;
  }
}

private void ClearCommandDescription(object sender)
{
  StatusText = ScribbleCommands.StatusbarText;
}

Of course, if you compile and run at this point, you'll get an error about StatusText not existing. That's because it doesn't. Of course, you could just change that to _statusBar.Content, but that would be directly manipulating the UI from code. According to the rumor, it's better to set a property somewhere that notifies the observers that it has changed. So, that's what we'll do, by adding a dependency property to our main window:

static MainWindow()
{
  StatusTextProperty =
    DependencyProperty.Register(
    "StatusText", typeof(string), typeof(MainWindow), 
    new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.AffectsRender));
}

public static DependencyProperty StatusTextProperty;
public string StatusText
{
  get
  {
    return (string)GetValue(StatusTextProperty);
  }
  set
  {
    SetValue(StatusTextProperty, value);
  }
}

Now, we can databind the status bar's content to the main window's StatusText dependency property (we've added x:Name="main" to the Window tag at the top):

<StatusBar DockPanel.Dock="Bottom" 
  Visibility="{Binding Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=_viewStatusBar}">
  <Label x:Name="_statusBarText" Content="{Binding ElementName=main, Path=StatusText}" />
</StatusBar>

Now, a little bit about the ScribbleCommand class. The ScribbleCommands class was getting a little tedious, so I wrote the ScribbleCommand class to encapsulate all of the information we need in order to attach to a menu item or a button. It inherits from RoutedUICommand so it plays nicely with the expectations of WPF. Really, it's just a RoutedUICommand with additional properties for Description and Tooltip.

For the status bar text on the buttons, I tried a different approach. Rather than checking for a command of a particular type in the activate and deactivate handlers, I checked if it was one of my own buttons that had a Description property. This helped in a few ways, one of which was that my inherited class was able to override the Tooltip property with that of the attached command. I'm not sure if this is a good approach or a blatant violation of some of the separation principles we were hoping to instill with the command pattern. I'll lay it out, anyway, and we'll see what we can make of it later.

The ScribbleButton inherits from Button, and has a text dependency property called Description (just like StatusText above). Also, it listens to the OnPropertyChanged event to see when the Command property is changed. If we get attached to a ScribbleCommand, then we set our tooltip and description according to the ScribbleCommand.

static ScribbleButton()
{
  DescriptionProperty = DependencyProperty.Register(
    "Description", typeof(string), typeof(ScribbleButton),
    new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.AffectsRender));
}

public static DependencyProperty DescriptionProperty;
public string Description
{
  get
  {
    return (string)GetValue(DescriptionProperty);
  }
  set
  {
    SetValue(DescriptionProperty, value);
  }
}

protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
  base.OnPropertyChanged(e);
  if(0==string.Compare(e.Property.Name,"Command"))
  {
    ScribbleCommand newCommand = e.NewValue as ScribbleCommand;
    if (null!=newCommand)
    {
      ToolTip = newCommand.Tooltip;
      Description = newCommand.Description;
    }
  }
}

We do something similar to the GotFocus, LostFocus trick to hook up mouse focus:

<Style TargetType="{x:Type scribble:ScribbleButton}" x:Key="{x:Type scribble:ScribbleButton}" >
  <EventSetter Event="MouseEnter" Handler="OnMouseEnter"/>
  <EventSetter Event="MouseLeave" Handler="OnMouseLeave"/>
</Style>

And that's it. We finally have an application that looks like an application, localizes nicely, and takes advantage of some of the new features of WPF. I think this is probably the end of the Scribble tutorial, as such. I'm still not entirely convinced I've learned the localization model MS is trying to get across. Every example I see uses that LocBaml example and editing values in Excel. I'm not entirely convinced that's the correct route either.

Thursday, August 02, 2007 9:40:13 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
# Monday, July 16, 2007

Here we are on the 10th installment of what I thought would be a pretty simple walk-through into writing standard, distributable applications using WPF. This walk-through started out mirroring the old Scribble tutorial (http://msdn2.microsoft.com/en-us/library/aa314520(VS.60).aspx) but has recently turned into me complaining about things I don't understand very well. Specifically, I'm just not getting my head around the localization story with WPF and how it interacts with the built-in ApplicationCommands.X classes and keyboard access (mnemonics in  MFC). So, rather than complaining, let's just solve the problem.

The problem is this: ApplicationCommands.X classes give the developer a nice, automatically localized hook to use in hooking up UI elements to business logic to be executed. These commands are automatically localized based on the currently selected UI culture. These commands don't have access keys for use in MenuItem UI elements, so if I use the ApplicationCommands.New command in the file menu, I can't invoke it via Alt+f,s (something I've been doing from the old DOS days and am not interested in changing).

Solution 1 -- Implementing our own ScribbleCommands class from scratch

Here's a possible example. We could just implement our own ApplicationCommands-like class to serve up all of the commands we're interested in using from the UI. After all, we already implemented ScribbleCommands.Exit, so we can do something like that for the rest of the commands. This has the downside of requiring us to localize all of the text ourselves.

Solution 2 -- Implementing our own ScribbleCommands class as an adapter around ApplicationCommands

This is the one we're going to pursue. Since we already have ScribbleCommands and since MS already gives us much of the plumbing for the built-in commands classes, we'll just enhance that built-in functionality to convince it to do what we want. Here's the idea: for commands that don't have built-in ApplicationCommands.X implementation, we create string resources for all UI related aspects of the command (Name, Text, Gextures, Gesture display text, Tooltip, and Description). For commands that do have a built-in ApplicationCommands.X implementation, we create string resources for the bits of UI related aspects we want to override (mnemonic character, Tooltip, and Description).

The Scribble.Exit command looks like this:

public static RoutedUICommand Exit
{
	get
      {
      	return EnsureCommand(
			Properties.Resources.ExitName,
			Properties.Resources.ExitText,
			Properties.Resources.ExitGestures,
			Properties.Resources.ExitGesturesDisplayText);
	}
}
public static string ExitTooltip
{
	get
	{
		return Properties.Resources.ExitTooltip;
	}
}
public static string ExitDescription
{
	get
	{
		return Properties.Resources.ExitDescription;
	}
}

The Scribble.New command looks like this:

public static RoutedUICommand New
{
	get
	{
		RoutedUICommand command = ApplicationCommands.New;
		string commandText = command.Text;
		int accessIndex = 
			commandText.IndexOf(Properties.Resources.NewAccessKey);
		if ((accessIndex >= 0) && (commandText.IndexOf('_') < 0))
		{
			command.Text = commandText.Insert(accessIndex, "_");
		}
		return command;
	}
}
public static string NewTooltip
{
	get
	{
		return Properties.Resources.NewTooltip;
	}
}
public static string NewDescription
{
	get
	{
		return Properties.Resources.NewDescription;
	}
}

Notice, we have to use our own Tooltip and Description properties here since the built-in commands don't have these items provided. Also, notice we are searching the returned command.Text for a pre-existing _ character. This is so we don't forcibly overwrite any mnemonic that may already be set. Of course, you could just remove it instead.

Using any of these commands in the UI looks exactly the same as before

<MenuItem Command="{x:Static scribble:ScribbleCommands.New}" />

There it is. The code (in Spanish and English) is available at http://www.cavinconsulting.com/Code/Scribble10.zip. To run it in Spanish, just un-comment the marked lines in App.xaml.cs. If you're running Windows XP, be sure to install the Spanish language pack for the .NET 3.0 Framework (http://www.microsoft.com/downloads/details.aspx?FamilyID=41c61d2a-d411-4dde-9013-bb08eb688bb6&DisplayLang=en). Unfortunately, if you're running Vista, it isn't as simple as just installing the language pack. If you have Ultimate or Enterprise edition, you should see the language packs on Utimate Updates. I didn't, however, and ended up downloading it directly from here.

I'm still not sure what I think about this approach. It does salvage the automatic localization provided by the runtime. It allows each language to define their own mnemonics or to fall back on the default mnemonic (if the latter exists in the command). However, on windows Vista, the localization only appears supported on Ultimate or Enterprise editions, and the installation of the appropriate MUI packs doesn't seem as simple as the old language pack model. Also, it limits my target audience to only those fortunate enough to be running an expensive version of Windows.

Monday, July 16, 2007 9:39:29 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
# Friday, July 06, 2007

Up to this point, we've been concentrating on the infrastructure aspect of what it takes to make a "standard" application using WPF. We aren't quite there yet. The localization story for WPF applications is pretty fuzzy as is the keyboard accessibility framework. More importantly, getting the two to work together is questionable, at best.

The story so far:

WPF makes heavy use of the Command pattern for hooking execution logic to different UI elements. The WPF framework provides us with a number of built-in commands for our use (like ApplicationCommands.New, and EditingCommands.AlignCenter. Here are my impressions on using these commands out of the box:

The Good

  • UI elements which are "command aware" can have many of their properties localized to the current UI culture without the programmer having to do anything.
  • Having so many canned commands makes it all the more likely that developers will actually use the command pattern for their own commands.
  • Most commands are decorated with commonly KeyGestures so things like Ctrl+C or Shift+Delete invokes the ApplicationCommands.Copy command without the programmer having to do anything.

The Bad

  • The localized commands do not have a method (that I have found) to designate a mnemonic (now called an access key) for easy, localizable keyboard access inside a MenuItem. So, I haven't found a way to attach the ApplicationCommands.Save command to an item under the _File MenuItem and have the key sequence Alt+f,s invoke the save command. This is a big problem for me because I routinely use Alt+f,s to save rather than Ctrl+s. I may be the only one in the world, since it's a throwback to the old DOS editor, but I still use it.
  • There doesn't seem to be a really friendly way to extend or specialize the built-in commands overriding just the information you want to specialize. Instead, I resort to duplicating the built-in command, thereby loosing the automatic localization capabilities.

The localization story in WPF is also a little confusing to me. What with all of the different types of resources available (resource dictionaries in App.xaml, and Window.xaml along with the standard Properties.Resource available in .NET 2.0) it is unclear to me how a developer is expected to make robust, localizable applications. So far, I have encountered several discussions on how it can be done, but most of them rely on a pretty flimsy example application (LocBaml), this article http://msdn2.microsoft.com/en-us/library/ms753931.aspx and some clumsy hand-editing of both ApplicationSettings.cs and the .csproj file. Moreover, the localization mechanism is by way of replacing the localizable strings in a .csv file. What happened to the localizable resource editor we had in MFC 10 years ago? Moreover, we already have a mechanism for localizing resources for a .NET 2.0 application. Ultimately, the Baml information just gets stored as a localizable resource in the executable. Why is there no tool support here?

To be fair, I'm sure Microsoft hasn't forgotten that agencies other than Microsoft produce multi-cultural applications. On the contrary, I'd like to think that easy localization of an application is very important to Microsoft. However, I'm just not seeing that coming out of their new toolsets. It doesn't look like there will be anything of the sort in the Orcas timeframe either.

Why all the fuss? I started this project out simply trying to create a "generic" desktop application using the WPF framework. By generic, I mean just what the original MFC Scribble tutorial was -- a simple, standard, Windows application with all of the appropriate Windowsy goodness like tooltips, keyboard access, shortcuts, and localization. When it comes down to it, writing such an application in WPF appears to be a lot more work than I think it should be.

I still plan on pursuing this project as I think it is important that developers understand the importance of a "standard" look and feel even though our new toolsets open up the possibility to diverge from that standard (or create a new one). Also, I think it is always important to remember that there are other languages besides English. By remembering this, and designing for it, we can ensure the ability to localize our applications for markets we may otherwise be ineligible to join.

Friday, July 06, 2007 9:38:56 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
# Thursday, June 21, 2007

The code for this segment is available at http://www.cavinconsulting.com/Code/Scribble8.zip.

By now, we've gotten through most of the Scribble framework (writing a real Windows-like application using WPF). Of course, I'm sure there are better ways to do what I'm doing, but that's fine. Here's a short installment on vector icons instead of .bmp/.png icons.

In our previous steps, we displayed our toolbar buttons with something like this:

<Button Command="ApplicationCommands.New">
  <Image Width="20" Height="20" Source="Resources/new.png" />
</Button>

That's fine, except that WPF is all about vectors, and as soon as you start adding bitmaps, things get pixilated when you show them on your 24" wide-screen monitor. So, we'll switch the bitmap out into something drawn with the WPF vector system. The first step is to find or create some vector drawings for use as your button graphics. You can draw things in Adobe Illustrator and use the Illustrator to xaml plugin (http://www.mikeswanson.com/XAMLExport/), you can use Expression Studio to draw the items by hand, or you can find a source on the Internet for nice, friendly toolbar buttons in xaml (http://www.grafile.com/presentation/Vista_Toolbar_library.html).

The library that I started with has each icon separated into its own xaml file. Unfortunately, it also has each item drawn on an 800x600 panel, which doesn't lend itself too well to scaling onto a square button. So, there is a little work to be done before we can apply these icons to our buttons. I accomplished the task in Expression Blend. The first thing I did was generate a resource dictionary for our application. This is pretty much just a dictionary of useful bits of xaml that you can apply other places in the application. Do this by adding icons.xaml to the project and editing the App.xaml to look like this:

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="Icons.xaml" />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>

The next step is to paste in the icons of interest from their respective xaml files. I guess I could leave them in their individual xaml files and just include each one as a resource dictionary, but I didn't think of it until now, and it's too late.

In order to use the icons as button images, we store them in the resource dictionary as ControlTemplates. To do this, create a ControlTemplate tag in the icons.xaml file and paste in the xaml of the button of interest.

<ControlTemplate x:Key="New">
  <!-- Pasted text from the icon of interest -->
</ControlTemplate>

Once you have the ControlTemplate resource setup, the changes to the button definition are trivial:

<Button Command="ApplicationCommands.New" Width="32" Height="32">
  <ContentControl Template="{StaticResource New}" />
</Button>

Unfortunately, since the icons I used are on an 800x600 canvas, I have to do a little alteration to make them work on a square button face. First, I wrap the entire icon in a ViewBox to make it scale nicely. Then, I wrap the original canvas in a square canvas 345x345 (it seemed like a good number at the time). Finally, I add a render transform to the original canvas to translate the original image to be centered in the new, parent canvas. The whole thing looks something like this:

<ControlTemplate x:Key="New">
  <Viewbox Stretch="Uniform">
    <Canvas Width="345" Height="345">
      <Canvas Width="800" Height="600" Canvas.Left="0" Canvas.Top="0">
        <Canvas.RenderTransform>
          <TransformGroup>
            <ScaleTransform ScaleX="1" ScaleY="1"/>
            <SkewTransform AngleX="0" AngleY="0"/>
            <RotateTransform Angle="0"/>
            <TranslateTransform X="-220" Y="-157"/>
          </TransformGroup>
        </Canvas.RenderTransform>
        <!-- Actual drawing code -->
      </Canvas>
    </Canvas>
  </Viewbox>
</ControlTemplate>

That's it. Now, you have classy, WPF-like, xamlized, vector toolbar buttons. Some of them might be ugly, but they're scalable. I'm sure there is a better way to accomplish this, but we're one step closer to a real-world Windows-like application using WPF. We still don't have all of those things that I think should be inherent in a Windows application (like tooltips, updating of the status bar with command help-text, etc.) and we can't take advantage of the automatic localization available with the ApplicationCommands classes since we want to have our own mnemonics for keyboard access to each command.

I think we'll tackle the ToolTips and StatusBar text next. Specifically, we'll take a look at how to make these items easily localizable so we don't have a bunch of English phrases locked into our code.

Thursday, June 21, 2007 9:36:33 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
# Wednesday, May 30, 2007

This one turned out to be a lot simpler than I had expected. As a result, the code isn't available. You'll have to do it yourself.

The question was one of attaching a property of our UI (say, the checked property of a MenuItem) to one of the clever Resources.Properties values that are taken care of for us by VS 2005. In our Scribble application, there are a few things we'll want to persist between sessions (on a per-user basis) such as toolbar visibility and window size (and maybe window state).

First, we add values to the Settings class for the values we would like to persist. You can do this in the properties for the project or by double-clicking on the Settings.settings class (does anyone know why there are so many ways to accomplish the same task in VS2005?).

I added MainWindowWidth and MainWindowHeight as user-scoped, integer values. I also added InkToolbarVisible, FileToolbarVisible, and StatusbarVisible as bool values. I set these with reasonable defaults.

Now, we have to hook these settings up to the properties of the UI. We will use similar syntax to how we hooked up our Exit command.

For the window size, we set width and height like this:

<Window
    Height="{Binding Source={x:Static scribble:Properties.Settings.Default}, 
        Path=MainWindowHeight, Mode=TwoWay}"
    Width="{Binding Source={x:Static scribble:Properties.Settings.Default}, 
        Path=MainWindowWidth, Mode=TwoWay}">

For the toolbar and statusbar visibility, we bind the IsChecked property (since that's where the toolbar and statusbar get their visibility). We set all three values with the same syntax:

<MenuItem Header="_File Toolbar" 
          x:Name="_viewFileBar" 
          IsCheckable="True" 
          IsChecked="{Binding Source={x:Static scribble:Properties.Settings.Default}, 
              Path=FileToolbarVisible, Mode=TwoWay}"/>

That's it, except for saving the values when the application closes. To to this, we override the Windows OnClosing method in the Scribble.xaml.cs file. Here are the contents:

protected override void OnClosing(CancelEventArgs e)
{
    Properties.Settings.Default.Save();
    base.OnClosing(e);
}

Compile and run a few times, changing the toolbar visibility and the window size. The application will remember these values each time you run.

Wednesday, May 30, 2007 9:14:21 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
# Friday, May 25, 2007

The source code for this segment is available at http://www.cavinconsulting.com/Code/Scribble6.zip.

Now that we have a functioning application that loads and saves scribble documents, it's time to take advantage of some of the new capabilities of WPF. You have to admit, we haven't really done anything so far that couldn't be accomplished by the old non-declarative programming practice. Since someone (Don Box) tells us that the "declarative" programming model is "better" than the non-declarative model, we plan on embracing it whole-heartedly. So, what can we do with it?

We have already seen some examples in our previous steps. Using the Command= syntax, we allow the code behind to ignore how that code is being invoked. We use the same command handler whether File->New is clicked on a menu or the user clicks the NewFile button in a toolbar. Big deal. We could have done that before.

How about this? How about we use some clever WPF tidbits to allow UI elements to manage other UI elements? Here is an area that has long been a problem. A designer says, "I want the background color of my button to be the same as what the user chooses for his font color." Then, a programmer goes into a dim cubicle for a couple of days, writes some code, drinks some coffee, and viola, a masterpiece of interactive UI is produced. But, should a bunch of code behind really be necessary for the designer to apply logic that is strictly UI related? Shouldn't the programmers be busy doing things like drinking coffee and automatically updating the Customers table whenever they get a Twitter notification?

Clearly, the answers are no/yes (sorry, non-parallel sentence construction). No, designers shouldn't have to wait on programmers to add UI related code. Yes, programmers shouldn't be spending a bunch of time writing UI related code. Or, at least, that's what we are told.

Take our scribble tutorial. Let's add the standard View menu. These menus usually have entries to turn on or off optional toolbars and status bars. Items that are visible usually have checks next to them. Items that are invisible usually have no checks next to them.

We could implement this by harnessing the MenuItem's Checked and Unchecked events and writing 3 lines of code to set the appropriate toolbar/statusbar visibility according to whether the MenuItem just got checked or unchecked. Clearly, this is unacceptable. The UI is beholden to codebehind for simple UI interaction. We can accomplish this whole process within XAML, and it's pretty simple (though not as simple as it should be, given the version of the designer I'm using).

First, we need to add our view menu:

<MenuItem Header="_View">
  <MenuItem Header="_File Toolbar" x:Name="_viewFileBar" IsCheckable="True" IsChecked="True"/>
  <MenuItem Header="_Ink ToolBar" x:Name="_viewInkBar" IsCheckable="True" IsChecked="True"/>
  <MenuItem Header="Status _Bar" x:Name="_viewStatusBar" IsCheckable="True" IsChecked="True"/>
</MenuItem>

From here, we need to "bind" the visibility of each toolbar/statusbar to the IsChecked property of the appropriate MenuItem. Unfortunately, IsChecked is a boolean and the Visibility property of a MenuItem is a Visibility enum. So, Microsoft was kind enough to provide us with a mechanism to convert between booleans and visibilities. They give us a type converter called BooleanToVisibilityConverter. In order to use this converter, however, we have to put these lines somewhere in the Window element of our xaml file:

<Window.Resources>
  <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>

Now, we can make use of this converter by adding the following Visibility property to the toolbars and status bar:

<ToolBar Visibility="{Binding Path=IsChecked, 
  Converter={StaticResource BooleanToVisibilityConverter},
  ElementName=_viewFileBar}">
<ToolBar Visibility="{Binding Path=IsChecked, 
  Converter={StaticResource BooleanToVisibilityConverter}, 
  ElementName=_viewInkBar}">
<StatusBar DockPanel.Dock="Bottom" Visibility="{Binding Path=IsChecked, 
  Converter={StaticResource BooleanToVisibilityConverter}, 
  ElementName=_viewStatusBar}">

That's it. No code. Compile and run, and checking the view items will change the visibility of the corresponding toolbars or statusbars. The syntax is a little odd, and I'm hoping that there will be better tool support for this in the future. But, for now, it isn't that horrible to decipher the {...} syntax. This can be extended to include all sorts of properties, including font size, background color, whether or not the text flashes, etc.

Next time, I'm hoping that we can look at how to attach WPF properties (like IsChecked) to .NET 2.0 Settings (Properties.Settings) and have them dynamically save and load so the application remembers the user preferences each time the application starts and stops.

Friday, May 25, 2007 1:55:35 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
WPF
Navigation
Categories
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Cavin Consulting
Sign In
Statistics
Total Posts: 28
This Year: 0
This Month: 0
This Week: 0
Comments: 4
All Content © 2010, Cavin Consulting
DasBlog theme 'Business' created by Christoph De Baene (delarou)