Kello’s Code Corner

January 11, 2009

Model-View-ViewModel and InputBindings for ICommand

Filed under: .NET,C#,Model-View-ViewModel,VB.NET,WPF — jonaskello @ 20:55
Tags: , , , ,

The problem

When using the Model-View-ViewModel pattern best practice is currently to expose properties of type ICommand from the ViewModel and map them to the Command property on Buttons, MenuItems etc. in the view. Using this approach is considered better than using RoutedCommands but we loose InputBindings becuase there is no ICommand.InputBindings like there is RoutedCommand.InputBindings. Instead we have to map KeyBindings at some level to the commands, for example at the Window or UserControl level. I tried to do this in XAML like this:

<Window>
  <Window.InputBindings>
    <KeyBinding Command="{Binding Path=SetViewCommand}" 
                     CommandParameter="Right" Key="Ctrl+R" />
    <KeyBinding Command="{Binding Path=SetViewCommand}" 
                     CommandParameter="Left" Key="Ctrl+L" />
  </Window.InputBindings>
</Window>

This syntax seemed so natural to me that I intuitively tried it without thinking too much but it didn’t work. There must be some other simple solution I thought and started searching. Oh boy, was I wrong… Below is my tale of researching this issue.

Why it does not work

I came across this post where this issue is discussed by people who understand WPF far better than me. It seems that the problem is two-fold:

  1. The KeyBinding.Command is not a DependencyProperty so it does not support data binding.
  2. Even if it was a DependencyProperty the KeyBinding is not part of the WPF element tree and therefore has no access to the DataContext (which is usually set to the ViewModel that exposes the ICommand we want to bind to).

The solution …err.. I mean the work-arounds

I have found two work-around solutions worth mentioning:

  1. This solution uses a “virtual branch“. But the XAML syntax gets very complex.
  2. Here is another solution that I like better. It uses a markup extension and the XAML syntax gets a litte bit less complicated.

But I didn’t like the work-arounds enough to use them. Mainly because advanced concepts such as virtual branches and markup extensions are not likely to be easily understood by all developers on a team and certainly not by the maintainers that would have to figure out how it all works later on.

RoutedCommand you have InputBinding… Why will you not be my friend?

This is where I turned back to look at RoutedCommand again because it actually has an InputBindings collection built-in. I knew that exposing RoutedCommands in ViewModel were considered bad and we should use ICommand instead but I did not fully understand why. So I read this article that explains a lot about RoutedCommand. In the section  “Routed Command Challenges” it also explains why RoutedCommands only complicates things for the M-V-VM pattern.

I don’t fully understand it all but it seems like one thing that makes RoutedCommands complicate things is that they are hard-wired to trigger things according to which control that have focus in the UI and then bubble up from that control. When we have many controls working together in a Window it then becomes hard to predict which handler the RoutedCommand will invoke. In contrast, mapping a Button.Command property to an ICommand on the ViewModel we get a very clear and direct understanding of what command handler will be invoked (the bound ICommand’s Execute method of course). Also since I am having a hard time fully understanding all implications of RoutedCommands they cannot be any good ;-).

So the problem is in the framework classes? Then why don’t you rewrite the framework…

Looking further at the badness of RoutedComands I also found this interesting post where the whole idea behind commanding in WPF is being rethought. I don’t understand it all so maybe I have gotten the wrong idea but I think what is proposed is something like this:

  • RoutedCommands and CommandBinding classes should be obsoleted so we work only with ICommand.
  • Instead of RoutedCommand there would be VerbGesture. A VerbGesture would be an abstract representation of something that can be invoked in the application like Save, Cut, Close etc. It would be treated as the other InputGestures so we would have KeyGesture, MouseGesture, and VerbGesture. Because of this a VerbGesture would be routed like other InputGestures.
  • The VerbGestures could be added to a control’s InputBindings so we would have KeyBinding, MouseBinding and VerbBinding. This way we could bind a VerbGesture to an ICommand.
  • VerbGestures could be mapped to other InputGestures so we could map a KeyGesture to a VerbGesture. For example pressing a key would trigger the KeyGesture which in turn would trigger the VerbGesture.
  • When a VerbGesture is triggered an associated VerbBinding is sought and when it is found it’s associated ICommand is executed. So it would work just like KeyGesture and MouseGesture.

This seems nice to me and I hope Microsoft implements something like this in the future. Anyway it seems like the consensus is that RoutedCommands complicate things so the thing I take away from all this is that I should avoid RoutedCommands and CommandBinding altogether. If I only knew this before it could have saved me a lot of reading up on how RoutedCommand works ;-).

Oh well, the framework rewrite would take me too long… Looks like it would have to be code-behind then..

But now back to present time. The way I solve this in my current project is to add the InputBindings in the code-behind file. It is not as nice as to have it in XAML but on the other hand it does not require advanced concepts like virtual branch or markup extensions. So to emulate the XAML in the beginning of this post I would have this for a view called MyView in the code-behind file MyView.xaml.vb:

Partial Public Class MyView   
  
  Public Sub New(ByVal viewModel As MyViewModel)   
  
    'This call is required by the Windows Form Designer.   
    InitializeComponent()   
  
.. Other code ...   
  
    'Create KeyBindings so the menus have shortcuts   
    CreateKeyBinding(_viewModel.SetViewCommand, "Right",   
Key.R, ModifierKeys.Control)   
    CreateKeyBinding(_viewModel.SetViewCommand, "Left",   
Key.L, ModifierKeys.Control)   
  
  End Sub  
  
  Protected Sub CreateKeyBinding(ByVal command As ICommand,   
ByVal commandParameter As Object,   
ByVal gestureKey As Key, ByVal modifiers As ModifierKeys)   
  
    Dim kb As New KeyBinding(command, gestureKey, ModifierKeys.Control)   
    kb.CommandParameter = commandParameter   
    InputBindings.Add(kb)   
  
  End Sub  
  
End Class

One thing that gets me is the fact that I have to add InputGestureText to each MenuItem like this.

<MenuItem Header="Right" Command="{Binding Path=SetViewCommand}"  
                CommandParameter="Right" InputGestureText="Ctrl+R" />  
<MenuItem Header="Left" Command="{Binding Path=SetViewCommand}"  
                CommandParameter="Left" InputGestureText="Ctrl+L" />  

The InputGestureText property is just a string that gets displayed to the right of the MenuItem and has nothing to do with mapping to keyboard shortcuts as it had in earlier GUI platforms such as WinForms. Now imagine what would happen if we change the KeyBinding’s KeyGesture in the code-behind file from Ctrl+R to Ctrl+W. The MenuItem.InputGestureText would still display Ctrl+R to the user but this keyboard shortcut would no longer trigger the ICommand in the MenuItem.Command property because it is now bound to the Ctrl+W KeyGesture.

RoutedCommand you are not my friend but you give InputGestureText a meaning…

When a MenuItem’s Command property is set to a RoutedCommand the InputGestureText is “automatically” set. I checked the code for this in the MenuItem class with Reflector and it actually tries to cast the Command property to RoutedCommand and if it succeds then it reads the InputBindings and uses the first KeyBinding it finds to set the InputGestureText:

private static object CoerceInputGestureText(DependencyObject d, object value)
{
RoutedCommand command;
MenuItem item = (MenuItem) d;
if ((string.IsNullOrEmpty((string) value)
&& !item.HasNonDefaultValue(InputGestureTextProperty))
&& ((command = item.Command as RoutedCommand) != null))
{
InputGestureCollection inputGestures = command.InputGestures;
if ((inputGestures == null) || (inputGestures.Count < 1)) { return value; } for (int i = 0; i < inputGestures.Count; i++) { KeyGesture gesture = ((IList) inputGestures)[i] as KeyGesture; if (gesture != null) { return gesture.GetDisplayStringForCulture(CultureInfo.CurrentCulture); } } } return value; } [/sourcecode] Too bad that it casts to a concrete implementation instead of an interface. This makes the MenuItem class tightly coupled to the RoutedCommand class. If instead it would cast to an interface like IInputGestureTextProvider we could provide our own implementation that could look for a parent KeyBinding in the element tree or something like that. Now there is nothing I can do to make MenuItem sync it's InputGestureText with the KeyBindings that are mapped to my ICommands? Perhaps I could make my DelegateCommand inherit from RoutedCommand and then shadow all the methods and properties including the InputBindings collection. It does not seem like a clean solution though so for now I suppose I have to keep my InputGestureTexts in sync with the KeyBindings manually. But I hope I will have time to get back to this later and find a better solution. kick it on DotNetKicks.com

January 8, 2009

Printing WPF Visuals that are created in code and not displayed

Filed under: .NET,C#,WPF,WPF Printing — jonaskello @ 20:23
Tags: , , ,

Today I had to print some WPF visuals (actually multiple views of a 3D model) that were not rendered to the screen but only existed in memory. If you try this you will notice it is not obvious how to do it. For example to print a simple button with a text you could assume the following code would do it but it won’t work.

      // This code will result in an empty page
      PrintDialog pd = new PrintDialog();
      if (pd.ShowDialog().GetValueOrDefault())
      {
        Button buttonToPrint = new Button();
        buttonToPrint.Content = "My Button";
        pd.PrintVisual(buttonToPrint, "Printing button");
      }

Why does it not work? Well if we put a breakpoint at the PrintVisual() line and check values for the button’s size in the immediate window we get this:

?buttonToPrint.Width
NaN
?buttonToPrint.Height
NaN
?buttonToPrint.ActualHeight
0.0
?buttonToPrint.ActualWidth
0.0

Aha! So we need to give our button a size, lets try this:

      // This code will also result in an empty page
      PrintDialog pd = new PrintDialog();
      if (pd.ShowDialog().GetValueOrDefault())
      {
        Button buttonToPrint = new Button();
        buttonToPrint.Content = "My Button";
        buttonToPrint.Width = 100;
        buttonToPrint.Height = 100;
        pd.PrintVisual(buttonToPrint, "Printing button");
      }

Hmm.. Still an empty page, let’s check the properties in the immediate window again:

?buttonToPrint.Height
100.0
?buttonToPrint.Width
100.0
?buttonToPrint.ActualWidth
0.0
?buttonToPrint.ActualHeight
0.0

The ActualWidth and ActualHeight is zero hence the button is not displayed on the printed page. Why is this? According to the doctor WPF layout engine does a 2-pass layout in which it first calls the Measure() method and then the Arrange() method on all Visuals recursively. When using PrintDialog.PrintVisual() it seems this is not done automatically so we need to do it ourselves using code like this:

      // This code will actually make the button appear on the page!
      PrintDialog pd = new PrintDialog();
      if (pd.ShowDialog().GetValueOrDefault())
      {
        Button buttonToPrint = new Button();
        buttonToPrint.Content = "My Button";

        // Do Measure and Arrange passes before printing so the button is layed out correctly
        buttonToPrint.Measure(new Size(double.MaxValue, double.MaxValue));
        buttonToPrint.Arrange(new Rect(buttonToPrint.DesiredSize));

        pd.PrintVisual(buttonToPrint, "Printing button");
      }

What happens in the Measure() and Arrange() calls that make this work? In the Measure() call the button checks how big it has to be in order to display all of it’s text and checks if this size is available within the size we pass in. This will always be the case since we pass in an “infinite” size. It then sets it’s DesiredSize to this size. Then in the second pass we call Arrange() which will layout the Button according to it’s DesiredSize property. Now we have simulated the layout engine’s work and the button is printed!

kick it on DotNetKicks.com

Create a free website or blog at WordPress.com.