Проблема привязки командного параметра WPF - PullRequest
5 голосов
/ 03 ноября 2008

У меня возникли проблемы с пониманием того, как работает привязка параметров команды.

Когда я создаю экземпляр класса виджета перед вызовом InitializeComponent, кажется, что он работает нормально. Изменения параметра (Widget) в функции ExecuteCommand будут «применены» к _widget. Это поведение, которое я ожидал.

Если экземпляр _widget создан после InitializeComponent, я получаю нулевые ссылочные исключения для параметра e.Parameter в функции ExecuteCommand.

Почему это? Как мне заставить эту работу работать с шаблоном MVP, где связанный объект может быть создан после создания представления?

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++;
    }
}

Ответы [ 2 ]

4 голосов
/ 03 ноября 2008

InitializeCompenent обрабатывает xaml, связанный с файлом. Именно в этот момент привязка CommandParameter сначала обрабатывается. Если вы инициализируете свое поле до InitializeCompenent, то ваше свойство не будет иметь значение null. Если вы создадите его позже, тогда оно будет нулевым.

Если вы хотите создать виджет после InitializeCompenent, вам нужно будет использовать свойство зависимости. Proeprty для зависимостей вызовет уведомление, которое приведет к обновлению CommandParameter и, следовательно, не будет нулевым.

Вот пример того, как сделать TestWidget свойством зависимости.

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); }
}
0 голосов
/ 22 декабря 2008

Даже со свойством зависимости вам все равно нужно вызвать CommandManager.InvalidateRequerySuggested, чтобы принудительно выполнить CanExecute команды.

...