Restrict the selection a WPF TreeView With Icons to Text only - wpf

I have a simple WPF TreeView with icons
<TreeView Name="TreeViewThings" ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Thing}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="2">
<Image Source="Thing.png" Width="16"
Height="16"
SnapsToDevicePixels="True"/>
<TextBlock Text="{Binding Path=Name}" Margin="5,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
When a node is selected, the whole StackPanel is selected (both the image and the text).
How can I restrict the selection to the Text Only?

Here is a little sth I found a while ago, when I googled for sth different:
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/208805b2-225f-4da3-abd7-0d3dfa92fede/
In that thread they also talk about rewriting the TreeView. You can do so like stated in this link:http://marlongrech.wordpress.com/2008/03/15/wpf-treeview-root-node/
you don't have to rewrite the TreeView class though, simply use the xaml from the latter link. you can see how to add the controltemplate if you download the source code.

Related

WPF -treeview items can't be selected at 4th level when using datatemplate within datatemplate

I tried to implement treeview with 4 levels using WPF/C#.Net 4.0.It loads all 4 levels but can't select 4th level and when selecting 3rd level it select group with 4th level.
Continent->Country->District->Artifacts is one structure but there is another one Continent->Products->Artifacts
Resource DataTemplates->
<DataTemplate x:Key="DistrictTemplates">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ArtifactName}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="CountryTemplate">
<TreeViewItem ItemsSource="{Binding Path=District}"
ItemTemplate=" {StaticResource DistrictTemplates}"
Header="{Binding Path=Code}">
</TreeViewItem>
</DataTemplate>
TreeView code->
<TreeView Name="treeExplorer" MouseDoubleClick="TreeView_MouseDoubleClick" SelectedItemChanged="treeExplorer_SelectedItemChanged">
<TreeViewItem Name="tviDefinition" IsExpanded="True" Header="Continent">
<TreeViewItem ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}" Header="Country" />
</TreeViewItem>
</TreeView>
there are some other treeItems as well.I can't use inline template inside the TreeView.Resources and also im confused if can use this HierarchicalDataTemplate sine i cant call Country.Districts.ArtifactName and got two hierarchies but I can call Country.Districts() and then Districts has code property and using code i can find Artifacts.And im using datatemplates inside usercontrol.resources
How would I be able to do this?
Here's something I've written that goes 4 levels deep. It's a TreeView which shows HL7 message structure. For a quick background HL7 is a field separated message. You have a message. Each message has a Segment. Each Segment has at least one field. A field could have multiple components. A component can have subcomponents. This tree displays the HL7 message structure, where each level is a part of the HL7 message format.
For example, if there is PID segment in the message the tree would like this:
PID
...PID.1
...PID.2
......PID.2.1
......PID.2.2
......PID.2.3
......PID.2.4
.........PID.2.4.1
.........PID.2.4.2
etc...
Here is the XAML:
<TreeView x:Name="hl7Structure" ItemsSource="{Binding Path=MessageSegments}" IsEnabled="True">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type MyNamespace:MessageSegment}" ItemsSource="{Binding Path=Fields}">
<TextBox x:Name="segmentName" BorderBrush="Transparent" BorderThickness="0" Text="{Binding Path=Name}" FocusVisualStyle="{x:Null}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type MyNamespace:MessageField}" ItemsSource="{Binding Path=Components}">
<TextBlock x:Name="fieldName" Text="{Binding Path=Name}" ToolTip="{Binding Path=Info}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type MyNamespace:MessageComponent}" ItemsSource="{Binding Path=Subcomponents}">
<TextBlock x:Name="componentName" Text="{Binding Path=Name}" ToolTip="{Binding Path=Info}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type MyNamespace:MessageSubcomponent}">
<TextBlock x:Name="subComponentName" Text="{Binding Path=Name}" ToolTip="{Binding Path=Info}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
Now the explanation of how it works. I have a base object that each HL7 message piece inherits. The ItemsSource of the TreeView is bound to that collection. Since there are 4 levels, where 3 show hierarchy and one that does not, there are 3 HierarchicalDataTemplates and 1 DataTemplate.
Think of it this way...The HL7 Message Segment, Field, and Components are tree nodes because they have children. The HL7 subcomponent is a leaf, because it has none. Each tree node gets a HierarchicalDataTemplate, but each leaf just gets a DataTemplate.
Each of HierarchicalDataTemplates know what object type to display by using the DataType property. Here is where I tell the control, the child type it's displaying. This allows me to use the base type collection and then display all the child types at their appropriate node levels.
Hope this helps.
Finally I managed to solve this.Thanks Josh and everyone.
Resource DataTemplates->
<DataTemplate x:Key="DistrictTemplates">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ArtifactName}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="CountryTemplate" DataType="Continent.Countries" ItemsSource="{Binding Path=District}" ItemTemplate="{StaticResource DistrictTemplates}">
<TextBlock Text="{Binding Path=Code}"/>
</HierarchicalDataTemplate>
TreeView code->
<TreeView Name="treeExplorer" MouseDoubleClick="TreeView_MouseDoubleClick" SelectedItemChanged="treeExplorer_SelectedItemChanged">
<TreeViewItem Name="tviDefinition" IsExpanded="True" Header="Continent">
<TreeViewItem ItemsSource="{Binding Path=Country}" ItemTemplate="{StaticResource CountryTemplate}" Header="Countries" />
</TreeViewItem>
</TreeView>

How to change HirerachicalDataTemplate associated with the WPF TreeView programatically at run time?

I have a TreeView control on my WPF window. I am giving only relevant XAML from my window.
<Window.Resources>
<HierarchicalDataTemplate x:Key="HierarchicalTemplate" ItemsSource="{Binding SubOrgUnitItems}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding ImagePath}" Stretch="Fill" Width="16"/>
<TextBlock Text="{Binding OrgUnitName}" Name="treeText" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView Margin="10,35,10,10" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Auto"
IsTabStop="True" Name="orgTreeView" ItemsSource="{Binding}" ItemTemplate="{DynamicResource HierarchicalTemplate}" TabIndex="700" SelectedItemChanged="orgTreeView_SelectedItemChanged" />
When the Collection of Organisations is bound to DataContext of the TreeView, items are displayed with the OrgUnitName's value as a text at every node.
Now at run time I want to see some other property's value as a text at every node. e.g. OrgUnitCode instead of OrgUnitName. Both are properties declared in the view model class associated with the treeview.
How can i do it programatically at run time?
You should use HierarchicalDataTemplateSelector.
Define two different HierarchicalDataTemplate (as you did).
Inherite your custom selector class from DataTemplateSelector, override its SelectTemplate method and put there the logic of the selection. This method will return the correct template in each case.
Create a Resource(Custom Selector class) in the xaml file.
Set the TreeViews ItemTemplateSelector to the static selector resource.
See a simple example here: Link
I have achieved what I wanted to do, but unfortunately by some work around. Following thing worked for me but it may not be the correct answer to the problem.
I added one more HirerachicalDataTemplate and TreeView. The new template uses the OrgUnitCode property. The new tree view uses the new template.
<HierarchicalDataTemplate x:Key="HierarchicalTemplateUsingCode" ItemsSource="{Binding SubOrgUnitItems}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding ImagePath}" Stretch="Fill" Width="16"/>
<TextBlock Text="{Binding OrgUnitCode}" Name="treeText" />
</StackPanel>
</HierarchicalDataTemplate>
<TreeView Margin="10,35,10,10" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Auto"
IsTabStop="True" Name="orgTreeViewCode" ItemsSource="{Binding}" ItemTemplate="{DynamicResource HierarchicalTemplateUsingCode}" TabIndex="700" SelectedItemChanged="orgTreeViewCode_SelectedItemChanged" Visibility="Hidden"/>
At run time, when I want to see OrgUnitCode property value as a text at the node, I simply make new tree visible and hide the first one (mentioned in question). So making tree views visible/invisible help me to achieve what I wanted to do.

WPF: get the content of a GroupBox to fill available space

I'm facing an irritating problem with WPF GroupBox, hope someone can help me out. Basically the problem is this: I have a listview inside a GroupBox, but no matter what I do I can't seem to be able to make it fill the GroupBox.
Here is the basic code:
<GroupBox Grid.Row="2" Header="Field" Visibility="{Binding ElementName=radioUnbound, Path=IsChecked, Converter={StaticResource bool2vis}}" Margin="0" VerticalContentAlignment="Stretch">
<ListView ItemsSource="{Binding ElementName=nnf1, Path=UnboundFields}" x:Name="listUnbound" SelectionChanged="listSelectionChanged" VerticalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" Margin="2"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</GroupBox>
I tried encasing the list inside Grids, StackPanels, DockPanel, etc... but no matter what I try I always invariably end up with this:
I tried your code in XamlPad it works as you would expect it. Make sure you don't have global styles that set your ListView or GroupBox appearance.
You can clear global styles by putting this in the resources section of the GroupBox's parent control:
<Style TargetType="GroupBox" />
<Style TargetType="ListView" />

WPF Treeview. No updating

I have a tree view with bind to some ObservableCollection, which is filled with some asyncronous function. The problem is that it's not getting updated on UI after the asyncronous command worked and updated the source collection (added some child nodes or something like that). My XAML looks like this:
<StackPanel>
<StackPanel.Resources>
<HierarchicalDataTemplate x:Key="CheckBoxItemTemplate"
ItemsSource="{Binding Children, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<CheckBox Focusable="False" IsChecked="{Binding IsChecked, Mode=TwoWay}"
VerticalAlignment="Center" />
<ContentPresenter Content="{Binding Node.Caption, Mode=OneWay}" />
</StackPanel>
</HierarchicalDataTemplate>
</StackPanel.Resources>
<TreeView Style="{DynamicResource FormItem}" ItemsSource="{Binding Nodes, Mode=TwoWay}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}" >
</TreeView>
</StackPanel>
Any suggestions?
Are you sure you're updating your UI on the correct thread?
Sorry for bothering you, guys. It was a silly mistake of mine. I just didn't set DataContexts of one control and of the window using this control. So it turned out that they had different contexts, because my ViewModel isn't a singleton. I should have been more careful about it.

Force TextBlock to wrap in WPF ListBox

I have a WPF listbox which displays messages. It contains an avatar on the left side and the username and message stacked vertically to the right of the avatar. The layout is fine until the message text should word wrap, but instead I get a horizontal scroll bar on the listbox.
I've Googled and found solutions to similar issues, but none of them worked.
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding Path=FriendsTimeline}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="DarkBlue" BorderThickness="3" CornerRadius="2" Margin="3" >
<Image Height="32" Width="32" Source="{Binding Path=User.ProfileImageUrl}"/>
</Border>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=User.UserName}"/>
<TextBlock Text="{Binding Path=Text}" TextWrapping="WrapWithOverflow"/> <!-- This is the textblock I'm having issues with. -->
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Contents of the TextBlock can be wrapped using property TextWrapping.
Instead of StackPanel, use DockPanel/Grid.
One more thing - set ScrollViewer.HorizontalScrollBarVisibility property to Disabled value for the ListBox.
Updated Hidden to Disabled based on comment from Matt. Thanks Matt.
The problem might not be located in the ListBox. The TextBlock won't wrap, if one of the parent controls provides enough space, so that it hasn't the need to wrap. This might be caused by a ScrollViewer control.
If you want to prevent TextBlock to grow, and you want it to just fit in the size of the listbox, you should set the width of it explicitly.
In order to change it dynamically, it means not a fix value, but you need to bind it to its proper parent element in the visual tree. You can have something like this:
<ListBox ItemsSource="{Binding MyItems}" Name="MyListBox">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Width"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ScrollContentPresenter}, Path=ActualWidth}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If it does not work, try to find the proper elements (which has to be binded to what) with the Live Visual Tree in Visual Studio.

Resources