2010-02-20 46 views
4

我想在我的Silverlight 3應用程序中使用MVVM模式,並且遇到了綁定到視圖模型的命令屬性工作的問題。首先,我想添加一個附加屬性稱爲點擊指令,像這樣:爲什麼Silverlight附加屬性不起作用?

public static class Command 
{ 
    public static readonly DependencyProperty ClickCommandProperty = 
     DependencyProperty.RegisterAttached(
      "ClickCommand", typeof(Command<RoutedEventHandler>), 
      typeof(Command), null); 

    public static Command<RoutedEventHandler> GetClickCommand(
     DependencyObject target) 
    { 
     return target.GetValue(ClickCommandProperty) 
      as Command<RoutedEventHandler>; 
    } 

    public static void SetClickCommand(
     DependencyObject target, Command<RoutedEventHandler> value) 
    { 
     // Breakpoints here are never reached 
     var btn = target as ButtonBase; 
     if (btn != null) 
     { 
      var oldValue = GetClickCommand(target); 
      btn.Click -= oldValue.Action; 

      target.SetValue(ClickCommandProperty, value); 
      btn.Click += value.Action; 
     } 
    } 
} 

通用指揮類是委託的包裝。我只是包裝一個委託,因爲我想知道是否有一個屬性的委託類型是最初事情不適合我的原因。下面是類:

public class Command<T> /* I'm not allowed to constrain T to a delegate type */ 
{ 
    public Command(T action) 
    { 
     this.Action = action; 
    } 

    public T Action { get; set; } 
} 

這裏是我如何使用附加屬性:

<Button u:Command.ClickCommand="{Binding DoThatThing}" Content="New"/> 

的語法似乎被接受,我想,當我測試了這一切與一個字符串屬性類型,這工作得很好。下面是被綁定到視圖模型類:

public class MyViewModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    public Command<RoutedEventHandler> DoThatThing 
    { 
     get 
     { 
      return new Command<RoutedEventHandler>(
       (s, e) => Debug.WriteLine("Never output!")); 
     } 
    } 
} 

中包含的命令財產委託永遠不會調用。另外,當我在附加屬性的getter和setter中放置斷點時,它們永遠不會到達。

在試圖隔離問題,我改變了屬性類型爲字符串; getter和setter中的斷點也沒有達到,但在它們中引發異常確實導致應用程序終止,所以我認爲這是一個框架偏心。

爲什麼這個東西不起作用?我也歡迎備用,希望更簡單的方法來綁定事件處理程序來查看模型。

+0

有趣。你有沒有嘗試過使用非通用版本? – smaclell 2010-02-20 19:23:32

+0

好想法。不過,我剛纔嘗試製作一個'RoutedEventCommand'類,並用它替換了'Command '的實例,行爲是一樣的。 – Jacob 2010-02-20 19:39:25

回答

9

這裏至少有兩個問題。

首先,你依靠的setXXX方法執行。當從XAML設置DP時,不執行CLR依賴屬性的包裝(屬性設置器或SetXxx方法);相反,WPF直接設置內部管理的DP「插槽」的值。 (這也解釋了爲什麼你的斷點從未被擊中。)因此,你的邏輯處理變化必須總是發生在設置者的OnXxxChanged回調中,而不是; WPF將在屬性更改時調用該回調函數,而不管該更改來自何處。因此(從稍微不同的執行命令而採取的例子,但應該給你的想法):

// Note callback in PropertyMetadata 

public static readonly DependencyProperty CommandProperty = 
    DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(Click), 
    new PropertyMetadata(OnCommandChanged)); 

// GetXxx and SetXxx wrappers contain boilerplate only 

public static ICommand GetCommand(DependencyObject obj) 
{ 
    return (ICommand)obj.GetValue(CommandProperty); 
} 

public static void SetCommand(DependencyObject obj, ICommand value) 
{ 
    obj.SetValue(CommandProperty, value); 
} 

// WPF will call the following when the property is set, even when it's set in XAML 

private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    ButtonBase button = d as ButtonBase; 
    if (button != null) 
    { 
    // do something with button.Click here 
    } 
} 

其次,即使有這樣的變化,對還沒有設定的值會引起控制設置點擊指令一個例外,因爲oldValue爲null,因此oldValue.Action導致NullReferenceException。你需要檢查這種情況(你也應該檢查newValue == null,雖然這不太可能發生)。

+0

非常感謝。做改變的處理程序解決了這個問題。 – Jacob 2010-02-20 20:29:48

+0

@itowlson,這是一個很好的解釋。讓我明白了一些事情,這讓我頭痛。謝謝 – infografnet 2012-09-11 21:18:20

相關問題