Bind Expanded & Collapsed event of TreeViewItem to viewmodel - c#

Some time ago i asked here how to bind the expanded event to the viewmodel and came to a solution using AttachedCommandBehavior:
<TreeView Name="tv" ItemsSource="{Binding TreeViewElements}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children, Mode=OneTime}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="local:CommandBehavior.Event" Value="Expanded"></Setter>
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.ExpandCommand, ElementName=tv}"></Setter>
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding}"></Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Now it is necessary to bind the collapsed event, too.
Adding this to the style section like this does not work, only the collapsed event is binded:
<Setter Property="local:CommandBehavior.Event" Value="Expanded"></Setter>
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.ExpandCommand, ElementName=tv}"></Setter>
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding}"></Setter>
<Setter Property="local:CommandBehavior.Event" Value="Collapsed"></Setter>
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.CollapseCommand, ElementName=tv}"></Setter>
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding}"></Setter>
Then i found an example on the AttachedCommandBehavior homepage to use a collection of behaviors:
<local:CommandBehaviorCollection.Behaviors>
<local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
<local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
</local:CommandBehaviorCollection.Behaviors>
The problem is that adding such a collection in the style section does not work, visual studio gives the error that the behavior property can not be attached to style.
Has anybody an idea how i can bind both events to the viewmodel?

Do it like this:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="local:CommandBehaviorCollection.Behaviors">
<Setter.Value>
<local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
<local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
</Setter.Value>
</Setter>
</Style>

Related

Command binding to top-level MenuItem of Menu doesn't work

I'm learning WPF and developing a dynamic Menu which is driven by data binding of it's ItemsSource to an ObservableCollection. To do this I have a simple MenuItemViewModel and a HierarchicalDataTemplate for automatic binding of MenuItems to it.
The problem I have is that Command property doesn't work for top level menu items. Despite it is set, a MenuItem doesn't react on mouse click and doesn't get disabled if Command cannot be executed. Simply it's like it's not getting bound.
For lower level menu items though, it works as intended. I think this should be a problem of my HierarchicalDataTemplate but I can't find it, because as I see there's no code in template which might affect command binding of only top-level MenuItems.
MenuItemViewModel implements INotifyPropertyChanged and contains following public properties:
string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children
HierarchicalDataTemplate for MenuItem in my Window.Resources is as follows:
<HierarchicalDataTemplate DataType="{x:Type common:MenuItemViewModel}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" />
<TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
Can you please point me on my mistake?
EDIT: Top-level MenuItem doesn't contain any children (i.e. Children collection of associated ViewModel is empty).
Thanks to #sTrenat comment I've come to solution below.
<Menu.Resources>
<!-- cancel sharing of image so Icon will work properly -->
<Image x:Key="MenuIcon" Source="{Binding ImageSource}" x:Shared="False"/>
</Menu.Resources>
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding Text}" />
<Setter Property="Icon" Value="{StaticResource MenuIcon}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="ItemsSource" Value="{Binding Children}" />
<!-- centering MenuItem's Header -->
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<!-- setting Icon to null when ImageSource isn't specified -->
<Style.Triggers>
<DataTrigger Binding="{Binding ImageSource}"
Value="{x:Null}">
<Setter Property="Icon" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>

How to set 2 color background to a TreeView (WPF)

I want to set two color background to a custom tree view (wpf). You can set this propriety in a DataGrid. I could not figure out a way to do it for a tree view.
I also want to set the selection of a element to be as big as the cell.
<TreeView Grid.Row="2" Name="TreeView" DataContext="{Binding Path=TreeModel}" ItemsSource="{Binding TreeItems}" SelectedItemChanged="TreeView_OnSelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:NodeViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding NameNode}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Add" Command="{Binding AddMachinePart_Command}"/>
<MenuItem Header="Remove" Command="{Binding RemoveMachinePart_Command}" IsEnabled="{Binding IsModule}"/>
<MenuItem Header="Edit" Command="{Binding EditMachinePart_Command}" IsEnabled="{Binding IsModule}"/>
<Separator></Separator>
<MenuItem Header="Copy path" Command="{Binding CopyPath_Command}" IsEnabled="{Binding IsModule}"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Edit:
When using the solution from
https://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.alternationindex(v=vs.110).aspx suggested #jschroedl
It would be ideal to be as large as the tree view grid like shown in picture below:
You will probably want to use ItemsControl.AlternationIndex in a Style to control how many colors to cycle (2 in your case).
MSDN Docs for ItemsControl.AlternationIndex has an example with ListBox which you can probably adapt to TreeView as well.

Indexing WPF tab controls

I have bound a tab control to a collection via a ViewModel property. I want the tab headers to reflect the index of the item it is showing, but this seems to be inordinately difficult. Is there an easy way to do this?
<TabControl
Style ="{StaticResource GtlTabControl}"
ItemsSource="{Binding Images}"
SelectedIndex="{Binding CurrentImageIndex}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="???"/>
</Style>
</TabControl.ItemContainerStyle>
...
</TabControl>
You can use an AlternationIndex inside the xaml. Something like this:
<TabControl Style ="{StaticResource GtlTabControl}"
ItemsSource="{Binding Images}"
SelectedIndex="{Binding CurrentImageIndex}"
AlternationCount="{Binding Path=Items.Count, RelativeSource={RelativeSource Self}}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource FindAncestor, AncestorType=TabItem}}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
There are also alternatives posted here: Show SelectedIndex in WPF Tabcontrol header template
If "Images" is a property of type List<MyImage>, then you can have an "Id" property in class "MyImage" and use it to bind to "Header"
public class MyImage{
public int Id{get;set;}
}
....<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Path=Id}"/>
</Style>
</TabControl.ItemContainerStyle>

Ancestor level binding not working in MenuItem command

We have use the hierarchical template to populate the menuitem
<UserControl.DataContext>
<local:MenuViewModel/>
</UserControl.DataContext>
<Grid>
<!--Initialize the Menu-->
<Menu Name="Part_Menu" ItemsSource="{Binding MenuCollection}" Background="#E5E5E5" VerticalAlignment="Center">
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding MenuItemCollection}">
<TextBlock Text="{Binding Header}" />
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="CommandParameter" Value="{Binding Header}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Command"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MenuViewModel}, AncestorLevel=2,Mode=FindAncestor},Path=MenuClick}"></Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
</Grid>
In this i have tried to bind the MenuClick(ICommand) to MenuItem , but it did not bind correctly
I have check the binding in the following forum link
[http://stackoverflow.com/questions/23941314/wpf-how-can-i-create-menu-and-submenus-using-binding?rq=1][1]
In this command added in the MenuModel , i need to Command in the MenuViewmodel
This way of binding :
{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MenuViewModel},
AncestorLevel=2, Mode=FindAncestor}
..is not working, because the AncestorType does not derive from UIElement.
The Path of the binding should be DataContext.MenuClick, and the AncestorType should be Menu. Putting it all together :
<Setter Property="Command"
Value="{Binding Path=DataContext.MenuClick,
RelativeSource={RelativeSource AncestorType={x:Type Menu},
AncestorLevel=2}}">
</Setter>
Mode=FindAncestor is the default mode, so i left that out.
In the MSDN: RelativeSource.AncestorType Documentation it is only stated that any Type could theoretically be used, however, FindAncestor inspects the visual tree to try and find the given ancestor, so whatever type you are looking for must be present in the visual tree. Hope this helps.

Set the ItemTemplateSelector of a treeview inside a user control

I have a problem with the user control I created. This control consists of a search textbox and a treeview. The treeview shows different data templates for different node types. So I created the usercontrol with a dependency property of type datatemplate which can be bound when using my control. Inside the control, the treeview binds to the dependency property. But sadly the treeviewtemplate selector doesn't get called.
<UserControl x:Class="yyy.yyy.yyy.UI.UserControls.SearchableTreeView.SearchableTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:behaviours="clr-namespace:yyy.yyy.yyy.UI.Behaviours;assembly=yyy"
mc:Ignorable="d"
x:Name="parent"
d:DesignHeight="300" d:DesignWidth="300">
<DockPanel DataContext="{Binding ElementName=parent}">
<TextBox DockPanel.Dock="Top" Margin="5" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"/>
<TreeView DockPanel.Dock="Top" Margin="5" ItemsSource="{Binding TreeViewItems}" ItemTemplateSelector="{Binding TreeViewTemplateSelector}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="behaviours:TreeViewItemBehaviour.IsBroughtIntoViewWhenSelected" Value="true"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
<DataTrigger Binding="{Binding Path=IsVisible}" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</DockPanel>
The code behind with the dependency property looks like that:
public partial class SearchableTreeView : UserControl
{
public SearchableTreeView()
{
InitializeComponent();
}
public static readonly DependencyProperty TreeViewTemplateSelectorProperty = DependencyProperty.Register(
"TreeViewTemplateSelector", typeof (DataTemplateSelector), typeof (SearchableTreeView), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector TreeViewTemplateSelector
{
get { return (DataTemplateSelector) GetValue(TreeViewTemplateSelectorProperty); }
set { SetValue(TreeViewTemplateSelectorProperty, value); }
}
}
And the usercontrol is used in a xaml like that:
<searchableTreeView:SearchableTreeView TreeViewTemplateSelector="{StaticResource TreeViewFieldTemplateSelector}"/>
Where the TreeViewFieldTemplateSelector is a class of type datatemplateselector, which allready worked before i startet to create a usercontrol out of the searchable treeview.
Does anybody know what I'm doing wrong? Or is it not possible to bind a datatemplateselector directly to a treeview?
Thanks
Manuel
You are complicating your system by using a DataTemplateSelector. While it is true that these objects were created for this purpose, there is a much easier way to achieve your requirements. Basically, if you declare a DataTemplate for each data type without specifying the x:Key values, then they will be applied implicitly to all objects of the correct type:
<DataTemplate DataType="{x:Type YourPrefix:YourDataType">
...
</DataTemplate>
<DataTemplate DataType="{x:Type YourPrefix:YourOtherDataType">
...
</DataTemplate>
<DataTemplate DataType="{x:Type YourPrefix:SomeOtherDataType">
...
</DataTemplate>
Now, if you put items of these data types into a collection and data bind that to a collection control, then you'll see your various items rendered as expected, but without the complications of the DataTemplateSelector.
UPDATE >>>
Ok, then try this instead... first remove the DataContext="{Binding ElementName=parent}" setting and then add a RelativeSource Binding for the TreeViewTemplateSelector property:
<DockPanel>
<TextBox DockPanel.Dock="Top" Margin="5" Text="{Binding SearchText,
UpdateSourceTrigger=PropertyChanged}"/>
<TreeView DockPanel.Dock="Top" Margin="5" ItemsSource="{Binding TreeViewItems}"
ItemTemplateSelector="{Binding TreeViewTemplateSelector, RelativeSource={
RelativeSource AncestorType={x:Type YourPrefix:SearchableTreeView}}}">
<TreeView.ItemContainerStyle>
...
</TreeView.ItemContainerStyle>
</TreeView>
</DockPanel>

Categories

Resources