views:

815

answers:

3

Hi All,

I am busy creating my first MVVM application in WPF.

Basically the problem I am having is that I have a TreeView (System.Windows.Controls.TreeView) which I have placed on my WPF Window, I have decide that I will bind to a ReadOnlyCollection of CommandViewModel items, and these items consist of a DisplayString, Tag and a RelayCommand.

Now in the XAML, I have my TreeView and I have successfully bound my ReadOnlyCollection to this. I can view this and everything looks fine in the UI.

The issue now is that I need to bind the RelayCommand to the Command of the TreeViewItem, however from what I can see the TreeViewItem doesn't have a Command. Does this force me to do it in the IsSelected property or even in the Code behind TreeView_SelectedItemChanged method or is there a way to do this magically in WPF?

This is the code I have:

<TreeView BorderBrush="{x:Null}" 
      HorizontalAlignment="Stretch" 
      VerticalAlignment="Stretch">
<TreeView.Items>
    <TreeViewItem
        Header="New Commands"
        ItemsSource="{Binding Commands}"
        DisplayMemberPath="DisplayName"
        IsExpanded="True">
    </TreeViewItem>
</TreeView.Items>

and ideally I would love to just go:

<TreeView BorderBrush="{x:Null}" 
      HorizontalAlignment="Stretch" 
      VerticalAlignment="Stretch">
<TreeView.Items>
    <TreeViewItem
        Header="New Trade"
        ItemsSource="{Binding Commands}"
        DisplayMemberPath="DisplayName"
        IsExpanded="True"
        Command="{Binding Path=Command}">
    </TreeViewItem>
</TreeView.Items>

Does someone have a solution that allows me to use the RelayCommand infrastructure I have.

Thanks guys, much appreciated!

Richard

A: 

What I'd do is set the Header of the TreeViewItem to be a button, then skin the button so that it doesn't look or act like one, then perform my command binding against the button.

You might need to do this via a DataTemplate, or you might need to change the template of the TreeViewItem itself. Never done it, but this is how I've done similar things (such as tab page headers).


Here's an example of what I'm talking about (you can drop this in Kaxaml and play around with it):

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
   <Page.Resources>
      <Style x:Key="ClearButan" TargetType="Button">
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="Button">              
                 <Border Name="border"
                     Padding="4"
                     Background="transparent">
                     <Grid >
                     <ContentPresenter HorizontalAlignment="Center"
                                    VerticalAlignment="Center">
                     </ContentPresenter>
                     </Grid>
                 </Border>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
   </Page.Resources>
   <Grid>
      <TreeView>
         <TreeViewItem>
            <Button Style="{StaticResource ClearButan}">
            easy peasy
            </Button>
         </TreeViewItem>
      </TreeView>
   </Grid>
</Page>

I've created a new clear style for a button. I then just drop a button in the TVI and set its style. You can do the same thing using data templates, of course.

Will
Hi, I see what you're trying to do, and appreciate the quick response, but is this really the best solution? I have searched the net high and low and can't find a nice way of doing it! Have to say its annoying that this isn't just built in. But at least you've provided a way of doing this, but now how do you get the header to show the button, and the bound text?
Richard
@richard updated with sample
Will
Thanks Will for the update and I see that this would probably work. I have gone a different route, but very much appreciate the help. Hopefully this will help someone else who has this issue.
Richard
A: 

This is a good example of how the MVVM is very much an after-thought in WPF. You expect there to be Command support of certain gui items, but there isn't, so you're forced to go through an elaborate process (as shown in Will's example) just to get a command attached to something.

Let's hope they address this in WPF 2.0 :-)

Sean
MVVM came after WPF, so it wasn't exactly an "afterthought", it was a way to apply the principles of MVC to WPF. Also, this is more of a shortcoming of the treeview that WPF. The treeview wasn't developed for this kind of use, which was kinda short sighted imho.
Will
+1  A: 

Thanks for the input into the issue, and yes, I did say I didn't want a Code behind solution, however at that time I was still very much under the impression that I was simply missing something... so I ended up using the TreeView_SelectedItemChanged event.

Even though Will's approach seems like a good work around, for my personal situation I decided that I would use the code behind. The reason for this is so that the View and XAML would remain as it would be if the TreeViewItem had a "Command" property to which my Command could be bound. Now I do not have to change the Templates or the Views, all I have to do is add the code and the Event for the TreeView_SelectedItemChanged.

My solution:

  private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (sender != null)
        {
            var treeView = sender as TreeView;
            if (treeView != null)
            {
                var commandViewModel = treeView.SelectedItem as CommandViewModel;
                if (commandViewModel != null)
                {
                    var mi = commandViewModel.Command.GetType().GetMethod("Execute");
                    mi.Invoke(commandViewModel.Command, new Object[] {null});
                }
            }
        }
    }

As I already have the RelayCommand attached to the TreeViewItem, all I am now doing is to just manually invoke the "Execute" method on that specific RelayCommand.

If this is the completely wrong way of going about it then please let me know...

Thanks!

Richard
I believe with the ASP.NET treeview you can extend TreeView and TreeViewItem to add this kind of functionality. You'd change the TVI to add what you needed and then overrode a method in TV to create a new instance of your TVI. I don't know if the same pattern is available in the WPF treeview; I checked but not that thoroughly.
Will