I have a TreeView bound to an MVVM observable collection.
My item template is composed by an image and a textblock like the following code show:
<HierarchicalDataTemplate x:Key="TreeViewItemTemplate" ItemsSource="{Binding Items, Mode=OneWay, NotifyOnSourceUpdated=True}">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image
Margin="-20,0,5,0"
Source="{Binding Icon, Converter={StaticResource TreeViewIconConverter}, Mode=OneWay}"
Style="{DynamicResource SmallIcon}"/>
<Label Content="{Binding Label}"/>
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</HierarchicalDataTemplate>
The problem happens when you Click over an item. If the Mouse cursor is over the StackPanel, the selection will not happen.
I have included also a screenshot to make this more clear.
Of course this happens because the StackPanel is now over the selection area.
Is there any workaround?
I found the answer by myself.
When you customize the TreeView using an hierarchical data template then you should not replicate the TreeViewItem.Header template because at runtime WPF will create one for you.
So, in order to have a custom TreeViewItem this code is enough:
<HierarchicalDataTemplate x:Key="TreeViewItemTemplate" ItemsSource="{Binding Items, Mode=OneWay, NotifyOnSourceUpdated=True}">
<StackPanel Orientation="Horizontal">
<Image
Margin="0,0,5,0"
Source="{Binding Icon, Converter={StaticResource TreeViewIconConverter}, Mode=OneWay}"
Style="{DynamicResource SmallIcon}"/>
<Label Content="{Binding Label}"/>
</StackPanel>
</HierarchicalDataTemplate>
Related
We are going to develop a behavior tree editor using WPF.
However, we are totally new to WPF.
How to generate shape components that represent tree nodes and the components should be able to respond to mouse event like right mouse clicked.
Do you have any suggestions on this?
You can specify different HierarchicalDataTemplate for your tree. They get chosen automatically by classname. You can also specify one for your base type. Be sure not to use interfaces, but real classes.
So if you use different classes for your view models you can get along with something like this:
<TreeView
ItemsSource="{Binding MyTreeVariable}"
SelectedItemChanged="MyTree_SelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}" DataType="{x:Type self:MyBaseType}">
<StackPanel Orientation="Horizontal">
<Rectangle Width="16" Height="16"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}" DataType="{x:Type self:MySpecialType1}">
<StackPanel Orientation="Horizontal">
<Ellipse Width="16" Height="16"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}" DataType="{x:Type self:MySpecialType2}">
<StackPanel Orientation="Horizontal">
<!--- Triangle -->
<Polygon Points="50,0 100,100 0,100" Width="16" Height="16"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
If you like to tell the different behaviors by data, you can use DataTrigger and Setter, but I`d recommend the way shown above.
I am using WPF ComboBox to populate a list of countries as following:
Inside ComboBox I have a VirtualizingStackPanel that contains an image and a TextBlock:
<ComboBox.ItemTemplate>
<DataTemplate>
<VirtualizingStackPanel Orientation="Horizontal">
<Image Width="30" Height="30" Margin="0" Source="{Binding code, Converter={StaticResource ImageComboBoxConverter}}" VerticalAlignment="Center"/>
<TextBlock Margin="5" Text="{Binding country_Text}"/>
</VirtualizingStackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
I am using a simple converter for DataBinding to retrieve my flag files.
The problem is, the list takes around good 3-4 seconds to populate. Can I apply some kind of buffering or caching technique for images to load faster?
You need to virtualize the ComboBox.ItemsPanel, not the .ItemTemplate. The ItemsPanel is the panel used to display all controls, while the ItemTemplate is used to display each control.
All the Virtualized StackPanel does is not render items which are not visible. Instead, it only renders visible items (plus a few extras for a scroll buffer), and will replace the data behind each item as you scroll. So using it to display each individual item is useless, and you need to instead use it as the panel to display all items.
Your code should probably look something like this :
<ComboBox ItemsSource="{Binding Countries}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="30" Height="30" Margin="0" Source="{Binding code, Converter={StaticResource ImageComboBoxConverter}}" VerticalAlignment="Center"/>
<TextBlock Margin="5" Text="{Binding country_Text}"/>
</StackPanel >
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You may also need to add one or all of these properties to the <ComboBox> tag too.
<ComboBox VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.CanContentScroll="True" ...>
I feel like I'm missing some essential concept in WPF databinding. It's always hit-or-miss whether my bindings are going to work for me.
In this example, I want to two-way bind the IsExpanded property of a TreeViewItem to the corresponding property on a bound object. It does work with the first example; it does NOT with the second.
Can anyone explain why? I cannot understand why the second version doesn't work. And I can't help thinking it would save me untold grief if I could.
This works when placed in UserControl.Resources (binding to IsExpanded is done with a Style applied to TreeViewItem:
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded"
Value="{Binding Path=IsExpanded, Mode=TwoWay}" />
</Style>
<DataTemplate DataType="{x:Type viewModels:FolderItem}">
<TreeViewItem ItemsSource="{Binding Folders}"
IsExpanded="{Binding Mode=TwoWay,Path=IsExpanded}" >
<TreeViewItem.Header>
<StackPanel Orientation="Vertical">
<Image Source="{Binding IconSource}"
Width="16" Height="16"
Margin="4,0,4,0" VerticalAlignment="Center" />
<TextBlock Text="{Binding Title}"
VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</DataTemplate>
This does not (direct binding to IsExpanded in the data template):
<DataTemplate DataType="{x:Type viewModels:FolderItem}">
<TreeViewItem ItemsSource="{Binding Folders}"
IsExpanded="{Binding Path=IsExpanded,Mode=TwoWay}" >
<TreeViewItem.Header>
<StackPanel Orientation="Vertical">
<Image Source="{Binding IconSource}"
Width="16" Height="16" Margin="4,0,4,0"
VerticalAlignment="Center" />
<TextBlock Text="{Binding Title}"
VerticalAlignment="Center" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</DataTemplate>
The DataTemplate is used in the following Xaml fragment, with the data template given above used to perform data conversion. Documents is an observable list of FolderItems which has been bound correctly.
<TreeView ItemsSource="{Binding Documents}" />
Both DataTemplates show the file tree. But there is no binding (two-way or otherwise) for IsExpanded in the second case.
Not shown is DataTemplating for FileItem's which are the leaf nodes of FolderItem's..
The issue is that TreeViewItem is actually a wrapper that is used by the TreeView itself. It needn't (and shouldn't) be part of your DataTemplate, as what you have now is creating a TreeViewItem within a TreeViewItem (the outer one being created by the TreeView, and the inner one being part of the template).
This is why your style works, as it gets applied to all TreeViewItems, both the one that you're declaring (which ends up being meaningless) and the one created by the TreeView.
What you should do is replace your DataTemplate with this:
<HierarchicalDataTemplate ItemsSource="{Binding Folders}">
<StackPanel Orientation="Vertical">
<Image Source="{Binding IconSource}"
Width="16" Height="16" Margin="4,0,4,0"
VerticalAlignment="Center" />
<TextBlock Text="{Binding Title}"
VerticalAlignment="Center" />
<HierarchicalDataTemplate>
(Unfortunately I can't test at the moment, but that should at least get you headed in the right direction).
Leave your style as-is to continue binding the property.
I have a treeview inside of a dockpanel and all of the elements of the treeview are inside HierarchicalDataTemplates. Here is the code:
<DockPanel Margin="10,10,0,0" VerticalAlignment="Stretch" Grid.Row="0" Grid.RowSpan="5" Grid.Column="0">
<DockPanel.Resources>
<src:TreeViewFilter x:Key="MyList" />
<HierarchicalDataTemplate DataType="{x:Type src:TreeViewParent}" ItemsSource="{Binding Path=OrderAttributes}">
<TextBlock Text="{Binding Path=Name}" FontSize="24"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type src:OrderAttribute}" ItemsSource="{Binding Path=OrderAttributes}">
<TextBlock Text="{Binding Path=NameAndCount}" FontSize="16"/>
</HierarchicalDataTemplate>
</DockPanel.Resources>
<TreeView Name="treeView1" BorderThickness="2" ItemsSource="{Binding Source={StaticResource MyList}, UpdateSourceTrigger=PropertyChanged}" TreeViewItem.Selected="treeViewFilter"/>
</DockPanel>
As you can see, the DockPanel wraps around the TreeView. The icons I am trying to add would be in the second HierarchicalDataTemplate which binds to a string and displays as a textbox. Depending on the name of the "NameAndCount", I would choose an icon to be displayed next to it on the left.
Any ideas on a solution for my example? Or do I need to think about using different templates like StackPanel?
Add an Image-Control wherever you want, bind its Source to "NameAndCount" and use a IValueConverter to convert it to an image-path.
I have a data bound Listbox as shown below. I want the textblock that holds the data to wrap. And I have not been able to.
What is the problem here?
Here is my code:
<DataTemplate x:Key="policyLbTemplate">
<StackPanel>
<TextBlock Text="{Binding name}" FontWeight="Bold"/>
<TextBlock Text="{Binding description}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
<ListBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Policies}"
ItemTemplate="{StaticResource policyLbTemplate}"
HorizontalContentAlignment="Stretch" />
You need to add one property to the list box: ScrollViewer.HorizontalScrollBarVisibility="Disabled", then the text will wrap (otherwise it grows offscreen). I have done it in the following code and it works fine for me.
<ListBox Name="lstbIcons" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding UserActionName}" FontWeight="Bold"/>
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is most likely because there is nothing restricting the width of the TextBlock so that it is growing offscreen. Do Horizontal scrollbars appear?
See the following related questions and try the solutions described:
WP7 TextBlock inside a ListBox not wrapping text
Force TextBlock to wrap in WPF ListBox
windows phone 7 TextBlock TextWrapping not honored in listbox
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/37236ac6-05c3-4acc-baca-abc871ba64e0