2014-04-26 15 views
6

Brzmi dość proste? Mam TreeView i chcę, aby coś się stało, gdy jeden z węzłów zostanie rozszerzony. Używam MVVM, więc "coś" jest poleceniem w ViewModelu.Polecenie wywołania, gdy TreeViewItem jest rozwinięty

Cóż, stwierdzam, że wcale nie jest takie proste. Rozejrzałem się i spróbowałem kilku rzeczy. Na przykład, przy użyciu MVVM Light EventToCommand:

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

ten kod (w oparciu o this i this) nie działa (pożarów nic; komenda jest związany w ViewModel ale odpowiednia metoda nie jest zwolniony, gdy węzeł jest rozszerzony). Próbowałem również zastąpienie cmd:EventToCommand z i:InvokeCommandAction, a wyniki są takie same. "Rozwiązanie" w drugim łączu jest wyraźnie przesadzone i nie chcę zmieniać ToggleButton, ponieważ chcę użyć WPF TreeView WinForms Style, który ma własny ToggleButton. Drugorzędna odpowiedź w drugim łączu sugeruje, że mogę próbować użyć zdarzenia na TreeView, które nie istnieje.

Kolejne possible solution może wiązać właściwość TreeViewItem o wartości IsExpanded. Chciałbym jednak, aby obiekty, do których się zobowiązuję, były czyste jako DTOs i wykonują akcję w ViewModel, a nie w wiązanych obiektach.

Co trzeba zrobić, aby wywołać polecenie w ViewModel, gdy TreeViewItem jest rozszerzony?

+0

Tak, proszę podać odpowiedź. Nie użyłem Prism, ale zobaczę, czy uda mi się go uruchomić. – Gigi

+0

Dobrze, opublikuję odpowiedź, która daje krok po kroku, a zobaczymy, czy jest przydatna. –

+0

Uwaga: ponieważ polecenie jest powiązane z zachowaniem, nie powinieneś wstawiać deklaracji " –

Odpowiedz

7

Aby to zadziałało, możesz użyć dołączonego zachowania, a zobaczysz, że jest to czysta strategia MVVM.

Tworzenie aplikacji WPF i XAML dodać to ...

<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> 

Następnie należy utworzyć widoku modelu takiego ...

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 
} 

używam polecenia Relay, ale można użyć Deleguj komendę zamiennie. Źródłem Komendy przekaźnik jest na http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

Następnie należy utworzyć oddzielną klasę, która wygląda tak ...

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 
} 

następnie zaimportować przestrzeń nazw tej klasy do swojej Xaml ...

xmlns: bindTreeViewExpand = "clr-namespace: BindTreeViewExpand" (twoja przestrzeń nazw będzie inna!)

Resharper zrobi to za Ciebie lub poda ci podpowiedź intellesense.

Na koniec podłącz model widoku. Użyj szybki i brudny sposób tak ...

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

Następnie po przestrzenie nazw są rozwiązane i okablowanie jest prawidłowe, to zacznie pracę. Zakotwicz swój debugger w metodzie Execute i obserwuj, że otrzymujesz argument RoutedEvent. Możesz to przeanalizować, aby uzyskać informację, który element widoku drzewa został rozwinięty.

Kluczowym aspektem tego rozwiązania jest zachowanie określone w STYLU! Jest więc stosowany do każdego drzewa TreeViewItem. Bez żadnego kodu (oprócz zachowania).

Powyższe zachowanie oznacza zdarzenie jako obsługiwane. Możesz to zmienić w zależności od zachowań, których szukasz.

+1

Wypróbowałem i działa całkiem nieźle. Trzeba przyznać, że jest to dość trudny wysiłek dla czegoś, co powinno być tak proste, ale wykonuje to zadanie i robi to dobrze. – Gigi

+0

Pamiętaj tylko, żeby nie wstawiać gettera. –

+1

To działa dobrze! Dziękuję za to rozwiązanie – peter70