I Command Thee – Exception?

Ok – so I’m reading through WPF Unleashed and I finish reading the chapter on the various deployment types which also covers the inbuilt page navigation framework. Now this is something that I’m very interested in having attempted the feat myself a few times. So I decide its time to stop reading, roll up the sleeves and give it a try.

To start with I try something like this:

The startup XAML defines we’re using a NavigationWindow with the startup page being HomePage.xaml.

<NavigationWindow x:Class="WpfParameterPassing.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    Source="HomePage.xaml">
</NavigationWindow>

The HomePage contains a Button and a TextBox. The idea is that the Button triggers the navigation passing the content of the TextBox to the called page.

<Page x:Class=”WpfParameterPassing.HomePage”
   xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
   xmlns:local=”clr-namespace:WpfParameterPassing”
   Title=”HomePage”
   x:Name=”_this”>
   <StackPanel>
        <Button Content=”Make Selection” Click =”Button_Click”/>
        <TextBox x:Name=”selectionTextBox”/>
  </StackPanel>
</
Page>

The page to call is defined as a PageFunction which means it allows a value to be returned. The type of this return value is defined by the x:TypeArguments attribute, in this case a string. The called form just has an Ok and Cancel button, together with a TextBox that is populated with the input parameter and which is used to populate the return value.

<PageFunction x:Class="WpfParameterPassing.SelectFunction"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    x:TypeArguments="sys:String"
    Title="SelectFunction"
    RemoveFromJournal="True">
    <StackPanel>
        <WrapPanel>
            <Label Content="Choice to return:"/>
            <TextBox x:Name="choice" HorizontalAlignment="Stretch" Width="150"/>
        </WrapPanel>
        <WrapPanel>
            <Button x:Name="okButton" IsDefault ="True" Content="_OK" 
HorizontalAlignment
="Right" Click
="Button_Click" /> <Button x:Name="cancelButton" IsCancel="True" Content="_Cancel"
HorizontalAlignment
="Right" Click
="cancelButton_Click" /> </WrapPanel> </StackPanel> </PageFunction>

A little bit of code behind for the HomePage is used to trigger the navigation and to subscribe to the Return event so we can update the TextBox with the value returned from the called page.

    public partial class HomePage : Page
    {
public HomePage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectFunction nextPage = new SelectFunction(selectionTextBox.Text);
nextPage.Return += new ReturnEventHandler<string>(nextPage_Return);
this.NavigationService.Navigate(nextPage);
}
private void nextPage_Return(object sender, ReturnEventArgs<string> e)
{
if (!string.IsNullOrEmpty(e.Result))
selectionTextBox.Text = e.Result;
}
}

Some code behind on the PageFunction derived class is used to wire up the Ok and Cancel buttons. The constructor overload is then used to allow the input parameters to be passed. I like the idea of using constructors to pass the parameters – neat. [Its a shame parameterized constructors can’t be used to initialize objects in XAML – but that’s a whole other story.]

    public partial class SelectFunction : PageFunction<string>
{
public SelectFunction()
{
InitializeComponent();
}
public SelectFunction(string previousSelection) : this()
{
choice.Text = previousSelection;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OnReturn(new ReturnEventArgs<string>(choice.Text));
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
OnReturn(new ReturnEventArgs<string>());
}
}

So – put all this together and it works as expected. Enter a value on the first page (HomePage) – click the button and the value is transferred to the second page (SelectFunction) where it can be modified and returned via the return result.

The next step is to get rid of all that code behind that binds HomePage to SelectFunction. What better way to do this than with a Command? This is where things began to get fuzzy. If I want to create my own command what should it derive from? After a few false starts attempting to derive from RoutedCommand etc. I decide not to derive from anything and just implement ICommand myself.

    public class SelectCommand : ICommand, INotifyPropertyChanged
    {
private string _selection;
public SelectCommand()
{
//Text = "Select Choice";
        }
public Page CallingPage { get; set; }
public string Selection
{
get
{ return _selection; }
set
{
_selection = value;
OnPropertyChanged("Selection");
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}

        #region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (CallingPage == null)
CallingPage = (Page) parameter;
if (CallingPage == null)
throw new InvalidOperationException(
"CallingPage cannot be null when invoking the Execute method."); SelectFunction nextPage = new SelectFunction(Selection); nextPage.Return += new ReturnEventHandler<string>(nextPage_Return); CallingPage.NavigationService.Navigate(nextPage); }
#endregion private void nextPage_Return(object sender, ReturnEventArgs<string> e) { if (!string.IsNullOrEmpty(e.Result)) Selection = e.Result; } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion }

Get rid of all the code behind in HomePage and redefine the Button and TextBox to instantiate a new command and a bit of binding to glue it together.

       <Button Content=”Make Selection” CommandParameter=”{Binding ElementName=_this}”>
           <Button.Command>
               <local:SelectCommand x:Name=”selectCommand”/>
           </Button.Command>
       </Button>
       <TextBox x:Name=”selectionTextBox” Text=”{Binding ElementName=selectCommand, Path=Selection}”/>

Now that’s much better. My button is set to execute a command when its activated (Clicked) and the command controls the page navigation and parameter passing for me. We get the result back into the page simply by binding our output field (in this case selectionTextBox) to the Selection property of the command (essentially the output parameter).

But does it work… well no. It generates this little beauty:

WPF Exception

Hmm… so is this a bug – or have I somehow hooked into some interop? If I remove the event handler subscription to the Return event everything works fine – except of course I don’t get my value .

I even tried Dispatching back to the UI thread in case it was a weird threading related issue – no luck. Interestingly when I first attempted to create my command by inheriting from RoutedCommand the button was disabled. This was despite the fact that my CanExecute method is hard-coded to return true. Its at this point I think I need to go back to the books – I’m obviously missing something.

___________________________________

And the answer is given here.

To work around the limitation I need to make the callback for the return an instance method of the calling form. Hmm… easy enough passing as a callback delegate in C# but how to declare that in XAML? Not too sure about the merits of this limitation – means putting code in the HomePage code behind again.

        public void Returned(object sender, ReturnEventArgs<string> e)
{
if (!string.IsNullOrEmpty(e.Result))
selectionTextBox.Text = e.Result;
}

Oh – and a bit of reading into RoutedUICommand makes things clearer as to why inheriting from it got me nowhere Maybe it can help me with the problem above though?

One thought on “I Command Thee – Exception?”

Comments are closed.