2008-11-02 17 views
5

Tengo problemas para entender cómo funciona el enlace de parámetros de comando.WPF CommandParameter Enlace Problema

Cuando creo una instancia de la clase de widget antes de la llamada a InitializeComponent, parece que funciona bien. Las modificaciones al parámetro (Widget) en la función ExecuteCommand se "aplicarán" a _widget. Este es el comportamiento que esperaba.

Si la instancia de _widget se crea después de InitializeComponent, obtengo excepciones de referencia nula para e.Parameter en la función ExecuteCommand.

¿Por qué es esto? ¿Cómo puedo hacer que esto funcione con el patrón MVP, donde el objeto enlazado puede crearse después de que se crea la vista?

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

Respuesta

4

InitializeCompenent procesa el xaml asociado con el archivo. Es en este punto en el tiempo que el enlace CommandParameter se procesa primero. Si inicializa su campo antes de InitializeCompenent, su propiedad no será nula. Si lo creas después, entonces es nulo.

Si desea crear el widget después de InitializeCompenent, necesitará usar una propiedad de dependencia. La propiedad de dependencia generará una notificación que hará que CommandParameter se actualice y, por lo tanto, no será nulo.

Aquí hay un ejemplo de cómo hacer que TestWidget sea una propiedad de dependencia.

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

Incluso con la propiedad de dependencia, que todavía tienen que llamar CommandManager.InvalidateRequerySuggested para forzar la CanExecute del comando que se está evaluando.