Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF CommandParameter Binding Problem

I'm having some trouble understanding how command parameter binding works.

When I create an instance of the widget class before the call to InitializeComponent it seems to work fine. Modifications to the parameter(Widget) in the ExecuteCommand function will be "applied" to _widget. This is the behavior I expected.

If the instance of _widget is created after InitializeComponent, I get null reference exceptions for e.Parameter in the ExecuteCommand function.

Why is this? How do I make this work with MVP pattern, where the bound object may get created after the view is created?

public partial class WidgetView : Window
{
    RoutedCommand _doSomethingCommand = new RoutedCommand();

    Widget _widget;

    public WidgetView()
    {
        _widget = new Widget();
        InitializeComponent();
        this.CommandBindings.Add(new CommandBinding(DoSomethingCommand, ExecuteCommand, CanExecuteCommand));
    }

    public Widget TestWidget
    {
        get { return _widget; }
        set { _widget = value; }
    }

    public RoutedCommand DoSomethingCommand
    {
        get { return _doSomethingCommand; }
    }

    private static void CanExecuteCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        if (e.Parameter == null)
            e.CanExecute = true;
        else
        {
            e.CanExecute = ((Widget)e.Parameter).Count < 2;
        }
    }

    private static void ExecuteCommand(object sender, ExecutedRoutedEventArgs e)
    {
        ((Widget)e.Parameter).DoSomething();
    }
}



<Window x:Class="CommandParameterTest.WidgetView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WidgetView" Height="300" Width="300"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel>
        <Button Name="_Button" Command="{Binding DoSomethingCommand}"
             CommandParameter="{Binding TestWidget}">Do Something</Button>
    </StackPanel>
</Window>


public class Widget
{
    public int Count = 0;
    public void DoSomething()
    {
        Count++;
    }
}
like image 845
user22522 Avatar asked Oct 14 '22 18:10

user22522


1 Answers

InitializeCompenent processes the xaml associated with the file. It is at this point in time that the CommandParameter binding is first processed. If you initialize your field before InitializeCompenent then your property will not be null. If you create it after then it is null.

If you want to create the widget after InitializeCompenent then you will need to use a dependency property. The dependency proeprty will raise a notification that will cause the CommandParameter to be updated and thus it will not be null.

Here is a sample of how to make TestWidget a dependency property.

public static readonly DependencyProperty TestWidgetProperty =
    DependencyProperty.Register("TestWidget", typeof(Widget), typeof(Window1), new UIPropertyMetadata(null));
public Widget TestWidget
{
    get { return (Widget) GetValue(TestWidgetProperty); }
    set { SetValue(TestWidgetProperty, value); }
}
like image 154
Todd White Avatar answered Oct 20 '22 18:10

Todd White