2014-04-26 34 views
6

聽起來很簡單嗎?我有一個TreeView,並且我希望當其中一個節點展開時發生。我使用的是MVVM,因此'something'是ViewModel中的一個命令。當TreeViewItem擴展時調用命令

嗯,我發現它並不是那麼簡單。我環顧四周,嘗試了一些東西。例如,使用MVVM光的EventToCommand:

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="TreeViewItem.Expanded"> 
     <cmd:EventToCommand Command="{Binding Path=FolderNodeToggledCommand}" /> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

此代碼(基於thisthis)不工作(沒有火災;該命令被綁定在視圖模型,但是當一個節點是相應的方法從來沒有發射擴展)。我也試着用i:InvokeCommandAction替換cmd:EventToCommand,結果是一樣的。第二個鏈接中的'解決方案'顯然是矯枉過正,我不想更改ToggleButton,因爲我想使用WPF TreeView WinForms Style,它有自己的ToggleButton。第二個鏈接中的次要答案表明我可能試圖在TreeView上使用不存在的事件。

另一個possible solution可能會綁定TreeViewItem的IsExpanded屬性。不過,我想保留我綁定的對象清潔爲DTOs,並在ViewModel中執行操作,而不是在綁定的對象中執行操作。

那麼當TreeViewItem被展開時,在ViewModel中調用一個命令需要什麼?

+0

是的,請一定要發表一個答案。我沒有使用棱鏡,但我會看看我是否可以得到它的工作。 – Gigi

+0

好吧,我會發佈一個一步一步的答案,我們會看看它是否有用。 –

+0

注意:由於該命令綁定到某個行爲,因此您不應該內聯聲明「 –

回答

7

爲了達到這個目的,您可以使用附加的行爲,並且您會發現它是一個乾淨的MVVM策略。

創建一個WPF應用程序,並添加此XAML ...

<Grid> 
    <TreeView> 
     <TreeView.Resources> 
      <Style TargetType="TreeViewItem"> 
       <Setter Property="bindTreeViewExpand:Behaviours.ExpandingBehaviour" Value="{Binding ExpandingCommand}"/> 
      </Style> 
     </TreeView.Resources> 
     <TreeViewItem Header="this" > 
      <TreeViewItem Header="1"/> 
      <TreeViewItem Header="2"><TreeViewItem Header="Nested"></TreeViewItem></TreeViewItem> 
      <TreeViewItem Header="2"/> 
      <TreeViewItem Header="2"/> 
      <TreeViewItem Header="2"/> 
     </TreeViewItem> 
     <TreeViewItem Header="that" > 
      <TreeViewItem Header="1"/> 
      <TreeViewItem Header="2"/> 
      <TreeViewItem Header="2"/> 
      <TreeViewItem Header="2"/> 
      <TreeViewItem Header="2"/> 
     </TreeViewItem>   
    </TreeView> 
</Grid> 

然後創建一個這樣的視圖模型...

public class ViewModel : INotifyPropertyChanged 
{ 
    public ICommand ExpandingCommand { get; set; } 
    public ViewModel() 
    { 
     ExpandingCommand = new RelayCommand(ExecuteExpandingCommand, CanExecuteExpandingCommand); 
    } 
    private void ExecuteExpandingCommand(object obj) 
    { 
     Console.WriteLine(@"Expanded"); 
    } 
    private bool CanExecuteExpandingCommand(object obj) 
    { 
     return true; 
    } 
    #region INotifyPropertyChanged Implementation 
    public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void OnPropertyChanged(string name) 
    { 
     var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null); 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 
    #endregion 
} 

我採用中繼的命令,但你可以使用可以交換委託命令。繼電器命令的源是在http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

然後創建一個單獨的類,看起來像這樣...

public static class Behaviours 
{ 
    #region ExpandingBehaviour (Attached DependencyProperty) 
    public static readonly DependencyProperty ExpandingBehaviourProperty = 
     DependencyProperty.RegisterAttached("ExpandingBehaviour", typeof(ICommand), typeof(Behaviours), 
      new PropertyMetadata(OnExpandingBehaviourChanged)); 
    public static void SetExpandingBehaviour(DependencyObject o, ICommand value) 
    { 
     o.SetValue(ExpandingBehaviourProperty, value); 
    } 
    public static ICommand GetExpandingBehaviour(DependencyObject o) 
    { 
     return (ICommand) o.GetValue(ExpandingBehaviourProperty); 
    } 
    private static void OnExpandingBehaviourChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     TreeViewItem tvi = d as TreeViewItem; 
     if (tvi != null) 
     { 
      ICommand ic = e.NewValue as ICommand; 
      if (ic != null) 
      { 
       tvi.Expanded += (s, a) => 
       { 
        if (ic.CanExecute(a)) 
        { 
         ic.Execute(a); 

        } 
        a.Handled = true; 
       }; 
      } 
     } 
    } 
    #endregion 
} 

然後導入這個類的名稱空間爲XAML中......

xmlns:bindTreeViewExpand =「clr-namespace:BindTreeViewExpand」(你的名字空間將會不同!)

resharper會爲你做這個,或者給你一個intellesense提示。

最後連接視圖模型。使用快速和骯髒的方法是這樣的...

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new ViewModel(); 
    } 

然後,在名稱空間解決,接線是否正確,它就會開始工作。將您的調試器固定在Execute方法中,並觀察您得到一個RoutedEvent參數。你可以解析這個來獲得哪個Tree view項目被展開。

此解決方案的關鍵方面是在STYLE中指定的行爲!所以它應用於每個TreeViewItem。沒有任何代碼(除了行爲)。

上面列出的行爲標記事件處理。根據你以後的行爲,你可能希望改變它。

+1

我試了一下,它工作得很好。無可否認,對於那些應該如此簡單的事情來說,這是一項相當大的努力,但它能夠完成這項工作,並且做得很好。 – Gigi

+0

只記得不要內聯獲取者 –

+1

它工作正常!感謝您對這個解決方案 – peter70