To get this working, you can use sticky behavior and you will see that this is a pure MVVM strategy.
Build a WPF application and add this 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>
Then create a view model like this ...
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 }
I use the relay command, but you can use the delegation command interchangeably. The source for Relay Command is at http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
Then create a separate class that looks like this:
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 }
Then import the namespace of this class into your Xaml ...
xmlns: bindTreeViewExpand = "clr-namespace: BindTreeViewExpand" (your namespace will be different!)
Resharper will do this for you or offer an intellesense invitation.
Finally, plug in the View Model. Use a quick and dirty method like this ...
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new ViewModel(); }
Then, after the namespaces are resolved and the wiring is correct, it will start working. Fix your debugger in the Execute method and notice that you get the RoutedEvent argument. You can analyze this to find out which element of the view tree has been expanded.
A key aspect of this solution is the behavior defined in STYLE! Therefore, it applies to all TreeViewItem. Missing code (other than behavior).
The behavior indicated above means the event that is being processed. You might want to change this depending on the behavior you are following.