I try do display hierarchical data with a TreeView and I would like to set different DataTemplates for my different Children types.
But the thing is, that my style does not get applied.
Maybe its a very simple mistake but i really do not find it.
<TreeView ItemsSource="{Binding List}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Main}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Property1}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Type2}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding Property2}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Type3}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="False"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Ok, I know what is going wrong. HierarchicalDataTemplate.ItemContainerStyle contains a style which is applied to the ItemsContainer where the children for the current node are stored. Try this as an experiment, change your style to:
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="Foreground" Value="Navy" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
You will notice that the node that you put this style onto continues to have a black foreground, but all it's children will now have a foreground of Navy.
It is a little counter-intuitive, but when you think about it, I guess it makes sense. So, bearing this in mind, I think the best solution is to bind IsExpanded for all TreeViewItems to a variable in the VM and then pick different values based on types there.
I had a similar issue maybe. In case Main, Type2 and Type3 are interfaces the selection in XAML won't work, I had to use classes. If you want to use interfaces you could implement a template selector.
Related
I am a fairly novice Visual Basic developer trying to use the MultiSelectTreeView control (https://github.com/ygoe/MultiSelectTreeView) in a MVVM application I am developing. To become familiar with using them together, I translated the MultiSelectTreeView demo to VB, and started implementing viewmodels using Catel.
What I found is that Catel seems to make some MultiSelectTreeView properties unavailable. On running the test using Catel, the demo window opens and mostly behaves as expected, but the immediate window shows a number of errors like:
System.Windows.Data Warning: 40 : BindingExpression path error: '(Controls:MultiSelectTreeView.HoverHighlighting)' property not found on 'object' ''MultiSelectTreeView' (Name='TheTreeView')'. BindingExpression:Path=(Controls:MultiSelectTreeView.HoverHighlighting); DataItem='MultiSelectTreeView' (Name='TheTreeView'); target element is 'MultiSelectTreeViewItem' (Name=''); target property is 'HoverHighlighting' (type 'Boolean')
Similar messages are present for the MultiSelectTreeView IsKeyboardMode and ItemIndent properties. Setting these properties in code or directly in the MainWindow XAML has no effect, and the MultiSelectTreeView's HoverHighlighting effect no longer works if Catel is used.
I have uploaded my test project to GitHub (https://github.com/AnotherKiwi/MultiSelectTreeViewDemoVB). The master branch contains the VB translation of MultiSelectTreeView demo, with all features working. In the ImplementingMainWindowViewModel branch I have started implementing a viewmodel using Catel. Most features of the demo work, except the ones involving the properties mentioned above.
I would really appreciate it if someone could provide guidance on why Catel seems to be interfering with these MultiSelectTreeView properties!
Some further information added after the answer from #Geert:
The declaration of the MultiSelectTreeView control in MainWindow.xaml is as follows
<controls:MultiSelectTreeView
x:Name="TheTreeView"
ItemsSource="{Binding RootNode.Children}"
AllowEditItems="{Binding AllowEditItems}"
VerticalRulers="{Binding VerticalRulers}">
<controls:MultiSelectTreeView.ContextMenu>
...
</controls:MultiSelectTreeView.ContextMenu>
<i:Interaction.Triggers>
...
</i:Interaction.Triggers>
<controls:MultiSelectTreeView.ItemContainerStyle>
<Style TargetType="{x:Type controls:MultiSelectTreeViewItem}">
<Setter Property="DisplayName" Value="{Binding DisplayName}"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="IsEnabled" Value="{Binding IsEnabled, Mode=TwoWay}"/>
<Setter Property="IsVisible" Value="{Binding IsVisible, Mode=TwoWay}"/>
<Setter Property="IsEditable" Value="{Binding IsEditable, Mode=TwoWay}"/>
<Setter Property="IsEditing" Value="{Binding IsEditing, Mode=TwoWay}"/>
<Setter Property="Remarks" Value="{Binding Remarks}"/>
<Setter Property="IsKeyboardMode" Value="{Binding IsKeyboardMode, Mode=TwoWay}"/>
<Setter Property="HoverHighlighting" Value="{Binding HoverHighlighting}"/>
<Setter Property="ItemIndent" Value="{Binding ItemIndent}"/>
<Setter Property="ToolTip" Value="{Binding ToolTip}" />
<Setter Property="ContentTemplateEdit">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border Background="YellowGreen" CornerRadius="3" Width="16" Height="16"/>
<controls:EditTextBox
Text="{Binding DisplayName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Padding="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</controls:MultiSelectTreeView.ItemContainerStyle>
<controls:MultiSelectTreeView.Resources>
<!--
Here the general item appearance is defined, for the ViewModel.TreeItemViewModel type
-->
<HierarchicalDataTemplate DataType="{x:Type vm:TreeItemViewModel}" ItemsSource="{Binding DataContext.Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding DataContext.ImageSource}" Width="16" Height="16" SnapsToDevicePixels="True"/>
<TextBlock Text="{Binding DataContext.DisplayName}" VerticalAlignment="Center" Padding="4,0,2,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</controls:MultiSelectTreeView.Resources>
</controls:MultiSelectTreeView>
I have tried applying the advice given in #Geert's answer, but when I run the application the MultiSelectTreeView is not displayed. I'm very new to WPF and I'm probably not modifying the appropriate XAML statements. Some more help with this would be really appreciated!
Note that items in an itemscontrol get a new DataContext (which is the item), so you cannot bind directly to the VM inside an ItemTemplate.
If you need to bind to the vm, you should do something like this:
<ItemsControl x:Name="myItemsControl" ItemsSource="{Binding MyItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Content="{Binding ElementName=myItemsControl, Path=DataContext.SomePropertyOnTheVm}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have a DataTrigger to set a TextBox's Background based on a bound property.
Here's a streamlined version of the xaml:
<TreeView >
<TreeViewItem Header="Things" >
<TreeViewItem.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="True">
<Setter Property="Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
<HierarchicalDataTemplate DataType="{x:Type local:Type1}" ItemsSource="{Binding Children, Mode=OneWay}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="6,0,6,0" />
<TextBlock Text="{Binding IsDirty}" Margin="6,0,6,0" />
<i:Interaction.Behaviors>
<dragDrop:FrameworkElementDropBehavior DragEffect="Move" />
</i:Interaction.Behaviors>
</StackPanel>
</HierarchicalDataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
I added a TextBlock to display the value of the IsDirty property; when that is true, the Background remains unchanged.
I have tried moving the Style to the HierarchicalDataTemplate.Resources, but that made no difference.
What am I overlooking?
Thanks --
That's because implicit styles targeting types not derived from Control do not cross the template boundary, i.e. are not applied inside templates unless they're defined within that template's scope. Here's a good post explaining how it works and why does it work this way.
In order to cross the template boundary, you should use a type deriving from Control (e.g. a Label) instead of a TextBlock and define implicit style targeting that type.
Otherwise, you could put your style in scope of the template in question by moving it into the template's resources dictionary:
<HierarchicalDataTemplate (...)>
<HierarchicalDataTemplate.Resources>
<Style TargetType="{x:Type TextBlock}">
(...)
</Style>
</HierarchicalDataTemplate.Resources>
(...)
</HierarchicalDataTemplate>
I would like to bind my ListViewItem's ToolTip to its ContentTemplate's TextBlock's ToolTip.
I tried the following but it didnt work:
<ListView ItemsSource="{Binding DoestMatter}" >
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding ElementName=Title, Path=ToolTip}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock x:Name="Title" Text="{Binding Title}" ToolTip="Test"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
The ToolTip's value is dynamically generated, here I just showed it as static string but actually it isn't, and that's why I need to bind it to the TextBlock's ToolTip.
How can I make it work?
Your code works just fine as it is... well, I had to change some Bindings to get it to work, but the main XAML is fine:
<ListView ItemsSource="{Binding Tests}" >
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding ToolTip, ElementName=Title}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock x:Name="Title" Text="{Binding Name}"
ToolTip="Test" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
However, one thing that I did notice was that it only works if you put your cursor directly over the TextBlock and that was not stretching across the width of the ListViewItem. To fix that, just set the HorizontalContentAlignment property to Stretch:
<ListView ItemsSource="{Binding Tests}" >
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding ToolTip, ElementName=Title}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock x:Name="Title" Text="{Binding Name}"
ToolTip="Test" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
UPDATE >>>
You're right... the ToolTip was still on the TextBlock. Then you just need to update your ToolTip.Binding to *the same value that works on the Text property:
<ListView ItemsSource="{Binding Tests}" >
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding ToolTip}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock x:Name="Title" Text="{Binding Title}"
ToolTip="{Binding ToolTip}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Now you should see 2 tooltips, one when over the TextBlock and one when over the ListViewItem.
UPDATE 2 >>>
#Ron, I really cannot understand why you are taking such a negative reaction to my answer(s)... you really should watch your attitude, because I am trying to help you and I don't feel that I deserve your bad attitude. So, to address your second, even ruder comment:
I said that the HorizontalAlignment is set to Stretch by default
Really? Where did you say that? In fact, you didn't say that, you said The ListViewItem stretches on default, which is something entirely different. As mentioned in the comment, I was stretching the TextBlock inside the ListViewItem with the HorizontalContentAlignment property, which isn't set to Strecth by default.
Who said that I want the Title as my ToolTip?
Nobody said that, but you did say The ToolTip's value is dynamically generated... so I can only image that you are Binding your dynamically generated ToolTip. If this is so, then you can simply data Bind that same value to the ListViewItem.ToolTip property as well.
UPDATE 3 >>>
In response to your last comment:
I stick to my question from the beginning because I cant really explain the problem. I know whats the solution though I dont know the practical way. I want to bind to the TextBlock's ToolTip Property.
Well sorry, but you can't do that in XAML because the TextBlock is declared in a DataTemplate. You can only access DataTemplate generated elements in code because they don't just exist until runtime... see the How to: Find DataTemplate-Generated Elements page on MSDN to find out how to do that. So you'll have to find another way to achieve your goal and that's why I've been suggesting these other methods all along.
I have a view model called
ViewModelClass
which contains a boolean.
I have another view model which contains
ObservableCollection<ViewModelClass> m_allProjects;
Then I have this in my view:
<DataTemplate>
<views:ProjectInfoView x:Key="ProjectInfoDetailTemplate"/>
</DataTemplate>
<ItemsControl Grid.Row="1" Grid.Column="0"
ItemsSource="{Binding AllProjects}"
ItemTemplate="{StaticResource ProjectInfoDetailTemplate}"
Margin="10,28.977,10,10">
</ItemsControl >
I want, based on the boolean in the AllProjects-collection, to use a different datatemplate. What is the best way to do this?
I know I can do this with different ViewModels and use a kind of ViewModel-base object, but I prefer just to use 1 view model.
EDIT:
I want to do this with data triggers. Can someone provide me with some code please?
I usually use a ContentControl to display the data, and swap out the ContentTemplate in a trigger based on the property that changes.
Here's an example I have posted on my blog that swaps a template based on a bound property
<DataTemplate x:Key="PersonTemplate" DataType="{x:Type local:ConsumerViewModel}">
<TextBlock Text="I'm a Person" />
</DataTemplate>
<DataTemplate x:Key="BusinessTemplate" DataType="{x:Type local:ConsumerViewModel}">
<TextBlock Text="I'm a Business" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ConsumerViewModel}">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource PersonTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ConsumerType}" Value="Business">
<Setter Property="ContentTemplate" Value="{StaticResource BusinessTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
A DataTemplateSelector will also work, but only if the property that determines which template to show doesn't change since DataTemplateSelectors don't respond to change notifications. I usually avoid them if possible since I also prefer my view selection logic in my view so I can see whats going on.
Here is what I want to do:
<TreeView>
<TreeView.Resources>
<Style TargetType="TreeViewItem" >
<Setter Property="dz:VirtualListItemBase.AutoLoad" Value="true" />
</Style>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Data.Nodes}" >
<Label Grid.Column="1" Grid.Row="0" Content="{Binding Data.Name}" dz:VirtualListItemBase.AutoLoad="true" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
The style part never gets applied. The HierachicalDataTemplate seems to override the initial style. I cannot use the type to apply the template since multiple types are involved. Any ideas?
Thanks
DataTemplate has higher precedence over Style. Try moving the DataTemplate into the Style as:
<Style TargetType="TreeViewItem" >
<Setter Property="dz:VirtualListItemBase.AutoLoad" Value="true" />
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate ...
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>