TreeView WPF - Expand and Collapse all nodes on button click - wpf

I'm using a TreeView and the HierarchicalDataTemplate to show some data. Sometimes the quantity of this data is quite much, so I would like to implement a button, which collapses the TreeView completely and another button, which expands it.
My XAML of the TreeView looks like this for now:
<TreeView x:Name="constOutput" Margin="25,96,25,229" SelectedItemChanged="ConstOutput_SelectedItemChanged" ItemsSource="{Binding FoundConstants}">
<TreeView.Resources>
<Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<WrapPanel HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=SrcFile.Name, StringFormat='({0})'}" Foreground="#006666" FontSize="13" HorizontalAlignment="Right" Margin="10, 2, 0, 0"/>
<TextBlock Text="{Binding Path=Children.Count, StringFormat='[{0}]'}" FontSize="13" HorizontalAlignment="Right" Margin="10, 2, 0, 0"/>
</WrapPanel>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Constant}" ItemsSource="{Binding Children}">
<WrapPanel>
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=SrcFile.Name, StringFormat='({0})'}" Foreground="#006666" FontSize="13" Margin="10, 2, 0, 0"/>
<TextBlock Text="{Binding Path=Children.Count, StringFormat='[{0}]'}" FontSize="13" HorizontalAlignment="Right" Margin="10, 2, 0, 0"/>
</WrapPanel>
<HierarchicalDataTemplate.ItemTemplate>
Im also using a ControlTemplate for the TreeView.
I think, I need to loop through all the elements of the TreeView and set all of the nodes on IsExpanded=false. I just don't know, where I would set it.

Related

WPF: show all my object properties over my Combobox tooltip

So i have ComboBox binding with my Person object list and i want to show all my object properties over my Combobox ToolTip.
Currently this is what i try:
<ComboBox
materialDesign:HintAssist.Hint="Network inteface"
materialDesign:HintAssist.IsFloating="True"
ItemsSource="{Binding Persons}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
<ComboBox.ToolTip>
<ToolTip>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=Id}"/>
</StackPanel>
</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
And all i can see this is empty little line of ToolTip
As I understand your issue. You have some confusion. You want to show all information in tooltip of invidual item i.e. comboboxItem, instead of comboBox. In your posted code, you are trying to get information in tooltip of combobox.
For combobox item, Please go through following code
<ComboBox ItemsSource="{Binding Persons}" SelectedValuePath="Id">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.ToolTip>
<ToolTip>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Id}"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
and here is result for reference

Use CollectionViewSource with TabControl

I'm trying to group and display the items of an ObservableCollection, just by using XAML code. It works well using a simple CollectionViewSource and a ListBox[1].
Actually, I would prefer to display the group's content in a tabcontrol. Google led me to the following social.msdn article wich presents a workaround to display the groups as a TabControl using code behind:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e073f275-0826-4fca-b9da-e310ccf1713e/wpf-grouping?forum=wpf
However, as I'm using MVVM and must rely on xaml only, I can't get it to work. Actually, the CollectionViewSource populates the groups (the TabControl shows the correct tabItemHeaders), but clicking on any of these TabItems freezes the application. Here's what I've tried:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]: This peace of xaml does work as expected, but uses a wrappanel to display groups contents:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
I think there's something wrong with your binding your code should work.
To get the same items in both ListBoxes try to bind the second ListBox Itemssource to the first ListBox Itemssource like this :
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks to Joh who helped with the appropriate DataBinding. However, the reason was totally different, a quick and dirty solution is given below[1]:
Basically, I was missing that the above mentioned tab control is nested within an outer Tab Control in my main window. I am not toally sure if the following description is entirely correct[1], but to my mind the reason is the following:
The outer TabControl uses a style to display its content. This content applies to a ViewModel which holds the above mentioned observable collection, which in turn should be the ItemsSource of the CollectionViewSource that feeds the inner tabControl with the groups.
As I have defined this style only in the outer TabControl.Resources, and missed to define a separate style for the inner tab Control, the inner tabcontrol inherits the outer style and tries to display its data using the same content.
This content is again another inner tabControl, which calls another inner tabControl and so on.
[1] Defining an empty style in the inenr tabControl.Resources solved the problem:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
I would be happy if someone could confirm this idea or provide some links to well known issues with shared styles in nested controls.

TreeView with buttons

I have a TreeView where each node has an icon and a descriptive text. But I do not want any node can be selected. Instead, I want that each node act as a button. It run a command when the user press it. But it may not look like a button or hyperlink
This is what I tried so far. The problem is that the text is blue and the text underlined. In addition, sometime the node is selected and therefore blue.
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:ListGroupViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Text}" FontWeight="Bold" ></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type vm:ListNodeViewModel}">
<TextBlock>
<Hyperlink TextDecorations="{x:Null}" Command="{Binding ClickCommand, Mode=OneTime}">
<StackPanel Orientation="Horizontal">
<Image Margin="0,2,2,0" Source="{Binding Icon}" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</Hyperlink>
</TextBlock>
</DataTemplate>
</TreeView.Resources>
You should override hyperlink style:
<Style x:Key="HyperlinkStyle" TargetType="Hyperlink">
<Setter Property="Foreground"
Value="Black"/>
<Setter Property="TextDecorations"
Value="{x:Null}"/>
</Style>
<DataTemplate DataType="{x:Type vm:ListNodeViewModel}">
<TextBlock>
<Hyperlink Command="{Binding ClickCommand, Mode=OneTime}"
Style="{StaticResource HyperlinkStyle}">
<StackPanel Orientation="Horizontal">
<Image Margin="0,2,2,0" Source="{Binding Icon}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</Hyperlink>
</TextBlock>
</DataTemplate>
To hide tree item selection you could override SystemColors.HighlightBrushKey, which tree view uses to highlight items:
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>

WPF Treeview HierarchicalDataTemplate Drag and drop

I have a treeview in wpf that is built using the xaml below. It is a well structured data source, and I am having a lot of trouble dragging and dropping. I have tried several methods, all to no avail. Can anyone tell me what the standard procedure is for doing this type of thing?
<TreeView x:Name="_treeView" ItemsSource="{Binding}" Grid.Row="0" Grid.Column="0">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type Logic:Statement}"
ItemsSource="{Binding Path=PagedChildren}">
<TextBlock Text="{Binding StatementName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type Logic:StatementPage}"
ItemsSource="{Binding Path=Children}">
<WrapPanel>
<TextBlock Text="Page: "/>
<TextBlock Text="{Binding PageIndex}"/>
</WrapPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type Logic:StatementFund}">
<Border HorizontalAlignment="Left" VerticalAlignment="Top" BorderBrush="Black" BorderThickness="2" CornerRadius="25">
<WrapPanel Margin="30 0 30 0" Width="150" Height="150" >
<StackPanel>
<TextBlock Text="Fund"/>
<WrapPanel>
<TextBlock Text="Fund: "/>
<TextBlock Text="{Binding FundNumber}"/>
</WrapPanel>
<WrapPanel Margin="10 0 0 0">
<TextBlock Text="{Binding ColumnIndex}"/>
</WrapPanel>
</StackPanel>
</WrapPanel>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type Logic:StatementPreviousCycle}">
<Border HorizontalAlignment="Left" VerticalAlignment="Top" BorderBrush="Black" BorderThickness="2" CornerRadius="25">
<WrapPanel Margin="30 0 30 0" Width="150" Height="150" >
<StackPanel>
<TextBlock Text="Previous Cycle"/>
<WrapPanel>
<TextBlock Text="Fund: "/>
<TextBlock Text="{Binding FundNumber}"/>
</WrapPanel>
<WrapPanel Margin="10 0 0 0">
<TextBlock Text="{Binding ColumnIndex}"/>
</WrapPanel>
</StackPanel>
</WrapPanel>
</Border>
</DataTemplate>
</TreeView.Resources>
</TreeView>
i use the techniques on this site for a general drag and drop.
tree view can get messy, if you want to know which node you are preivewMouseDown'ing on, to then use as your drag item you can end up walking the visual tree. there is some code to do that here. another way is to subclass treeview, and treeviewitem, then you can override the preview mouse down on each tree view item, and tell your derived parent treeview about it, which could set the tree view item to be the selected item.

How to combine my seemingly redundant XAML

I have 8 different XAML DataTemplates that are all very similar. Here are 2 of them:
<DataTemplate x:Key="ConflictFieldStringCellContentTemplate">
<StackPanel>
<TextBlock Text="{Binding ClientVersion.Value}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}" />
<Label Background="LightGray" Height="1" Margin="0, 4, -4, 2"></Label>
<TextBlock Text="{Binding ServerVersion.Value}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ConflictFieldStringArrayCellContentTemplate">
<StackPanel>
<TextBlock Text="{Binding ClientVersion.Value, Converter={StaticResource stringArrayToCommaDelimitedStringConverter}}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}"/>
<Label Background="LightGray" Height="1" Margin="0, 4, -4, 2"></Label>
<TextBlock Text="{Binding ServerVersion.Value, Converter={StaticResource stringArrayToCommaDelimitedStringConverter}}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}"/>
</StackPanel>
</DataTemplate>
As you can see, the only difference is that they use a different Converter for the Binding of the Text property of the TextBlock. Is there any way for me to factor out the commonalities of these two DataTemplates? I have 6 more and updating them is getting very tedious, because everything is identical except for the Converter for the Binding of the Text property.
Is there a way to somehow factor this out into one template which can be parameterized somehow? Something like this would be cool (pseudo-code):
<DataTemplate x:Key="BaseCellContentTemplate">
<StackPanel>
<TextBlock Text="{??}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}" />
<Label Background="LightGray" Height="1" Margin="0, 4, -4, 2"></Label>
<TextBlock Text="{Binding ServerVersion.Value}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ConflictFieldStringCellContentTemplate" BaseTemplate="BaseCellContentTemplate">
<??>{Binding ClientVersion.Value}</??>
</DataTemplate>
<DataTemplate x:Key="ConflictFieldStringArrayCellContentTemplate" BaseTemplate="BaseCellContentTemplate">
<??>{Binding ClientVersion.Value, Converter={StaticResource stringArrayToCommaDelimitedStringConverter}}</??>
</DataTemplate>
If there is only one value, and you want to do it purely with templates, you might do:
<DataTemplate x:Key="VersionDisplayTemplate">
<StackPanel>
<TextBlock Text="{TemplateBinding Tag}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}" />
<Label Background="LightGray" Height="1" Margin="0, 4, -4, 2"></Label>
<TextBlock Text="{TemplateBinding Content}"
Foreground="{Binding Path=Mismatch, Converter={StaticResource mismatchBoolToBrushConverter}}"/>
</StackPanel>
</DataTemplate>
Now you can use it as:
<DataTemplate x:Key="ConflictFieldStringCellContentTemplate">
<ContentPresenter
Tag="ABC"
Content="{Binding ClientVersion.Value}"
ContentTemplate="{StaticResource VersionDisplayTemplate}"
/>
</DataTemplate>
<DataTemplate x:Key="ConflictFieldStringArrayCellContentTemplate">
<ContentPresenter
Tag="XYZ"
Content="{Binding ClientVersion.Value, Converter={StaticResource stringArrayToCommaDelimitedStringConverter}}"
ContentTemplate="{StaticResource VersionDisplayTemplate}"
/>
</DataTemplate>
One path you can try is to create a new User Control.
This User Control should contain the StackPanel, and this StackPanel should contain the TextBox, Label and TextBox.
You could implement the TextConverters as dependency properties.
The final set of DataTemplates would look like this:
<DataTemplate x:Key="ConflictFieldStringCellContentTemplate">
<local:VersionDisplayControl
ClientVersionTextConverter="{StaticResource stringArrayToCommaDelimitedStringConverter}" />
</DataTemplate>
<DataTemplate x:Key="ConflictFieldStringArrayCellContentTemplate">
<local:VersionDisplayControl
ClientVersionTextConverter="{StaticResource stringArrayToCommaDelimitedStringConverter}"
ServerVersionTextConverter="{StaticResource stringArrayToCommaDelimitedStringConverter}" />
</DataTemplate>
This is assuming that the User Control is able to access the source version information from some globally available source.
If not, the VersionDisplayControl will have to expose another public property, probably called VersionSource.

Resources