I am trying to change the Path in WPF Treeview TextBlock dynamically via user selection i.e. dropdown. Upon user interaction the path should take predefined values i.e. Name, Type, Order.
<TreeView x:Name="Main" ItemsSource="{Binding Items, NotifyOnSourceUpdated=True}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Root}"
ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Name}" /> <--- Dynamically change this
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
In C# there is an BindingExpression helper class however I am not clear how to use it in ViewModel scenario
If you want to change the actual binding path, you need to do this programmatically. You cannot dynamically change the binding path in XAML.
A better option would be to change the value of the source property based on the selection in the ComboBox, or use a Style with triggers. Something like this:
<HierarchicalDataTemplate DataType="{x:Type models:Root}"
ItemsSource="{Binding Path=Children}">
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue}" Value="Name">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="{Binding Name}" />
</Setter.Value>
</Setter>
</DataTrigger>
...
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</HierarchicalDataTemplate>
Related
I have a ListBox with its ItemsSource bound to some UserControl-s and shown based on their current states(IsVisible)
Here is the code
<ListBox x:Name="sidebarList" Margin="0,0,10,10" ItemsSource="{Binding Modules, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0 5 0 5" Visibility="{Binding IsVisible, Mode=TwoWay}">
<TextBlock Text="{Binding Title}" FontWeight="Bold" />
<TextBlock Margin="0 5 0 0" MaxWidth="200" Foreground="Gray" Text="{Binding Detail}"
TextWrapping="WrapWithOverflow">
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="Collapsed">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
And now I'm trying to only get "IsVisible"/selected parts when a button is pressed. As of right now, the ListBox actually contains every option but just showing the IsVisible ones(at least that's what I think).
How do I only get the selected options? I have tried to use a new List with the same type as the UserControl-s type(which I placed in app.xaml.cs to call from anywhere) and add to that list when their state changes to IsVisible
but the List doesn't show up in my classes where I check them.
Sounds like what you really need is a view-model. Bind your Listbox to a view model that has an observable collection that only shows view-models of the items you want to present.
I can give you a more "correct" answer about how to do it with style/data-triggers but you should really just learn about MVVM and use a view-model to simplify.
For the 2nd day I'm scouring the web and have not found a solution.
Take an element like this:
<TreeView ItemsSource="{Binding Types}" Width="300">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Type}"
ItemsSource="{Binding SubTypes}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type SubType}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I use the Material NuGet library for base styles. Now however I need to disable the hover, select, etc. on the first level items and only allow the selection/hover for the subitems.
But everything I seem to find is about styling the contents of each item or styling everything globally.
A <- remove selection/hover (pref single click too but that's another topic)
A1 <- maintain original style, hover and select
A2 <- maintain original style, hover and select
A3 <- maintain original style, hover and select
B <- remove selection/hover (pref single click too but that's another topic)
B1 <- maintain original style, hover and select
B2 <- maintain original style, hover and select
B3 <- maintain original style, hover and select
Sounds like you don't really want each top-level item to act like a normal TreeViewItem. In that case, why not move the top-level items outside of the TreeView?
Basically, you'd have an ItemsControl of the top-level items, where the item template acts a bit like an Expander containing a TreeView of the items underneath it. You could style the top-level items to look however you like.
The downside would be that the trees under the top-level items would be virtualized individually, not as a whole. That is, they would not share containers. Unless you have a ton of top-level items, that probably won't be a big deal.
Example:
<ItemsControl xmlns:s="clr-namespace:System;assembly=mscorlib"
ItemsSource="{Binding Types}">
<ItemsControl.Resources>
<ControlTemplate x:Key="ExpanderButtonTemplate" TargetType="ToggleButton">
<Border Background="Transparent" Padding="3,2">
<ContentPresenter />
</Border>
</ControlTemplate>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<DockPanel LastChildFill="True">
<ToggleButton DockPanel.Dock="Top"
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Template="{StaticResource ExpanderButtonTemplate}">
<ContentPresenter ContentSource="Header" />
</ToggleButton>
<Border>
<ContentPresenter x:Name="contentSite" Visibility="Collapsed" />
</Border>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="contentSite" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<TreeView ItemsSource="{Binding SubTypes}" BorderThickness="0" Padding="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:Type}"
ItemsSource="{Binding SubTypes}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type models:SubType}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Click on one of the top-level items to expand the tree beneath it.
You can keep the TreeView and set properties or apply a style to your top-level items (assuming your TreeView isn't nested in another TreeView) by searching up the logical hierarchy for a TreeViewItem:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
<Setter Property="Background" Value="LightYellow" />
</DataTrigger>
For top-level items this binding spams warnings to debug output, but they can safely be ignored. A more sophisticated version of this trick would be to create an inheritable attached property TreeViewItemLevel that would be set to zero on the TreeView and to one more than the inherited value on TreeViewItems.
While setting a DataTrigger with a relative source binding works, it will produce binding errors.
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="{x:Null}">
By setting a FallbackValue of x:Null, you will not get any binding errors, but warnings.
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, FallbackValue={x:Null}}" Value="{x:Null}">
An alternative approach that will neither yield errors nor warnings, is to create a value converter that essentially does the same a RelativeSource binding, but will handle the errors, too. It returns true for any dependency object passed in, if it has an ancestor of type TreeViewItem, false otherwise.
public class IsRootTreeViewItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is null ? Binding.DoNothing : !HasTreeViewItemAncestor((DependencyObject)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
private static bool HasTreeViewItemAncestor(DependencyObject child)
{
var parent = VisualTreeHelper.GetParent(child);
return parent switch
{
null => false,
TreeViewItem _ => true,
_ => HasTreeViewItemAncestor(parent)
};
}
}
In your TreeViewItem style trigger, you can use the converter by binding to the item itself with Self.
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IsRootTreeViewItemConverter}}" Value="True">
This approach works for both statically assigned TreeViewItems and ItemsSource bindings.
Is it possible to select single words with the mouse in a WPF ListBox control? When yes, how can I do that?
All hints are welcome :)
If you define an ItemTemplate for your ListBox, you can use a TextBox to display each item (assuming that your items are plain strings):
<ListBox ItemsSource="{Binding YourCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" IsReadOnly="True" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
UPDATE >>>
I just tested it and had to make one change to set the Binding.Mode property to OneWay and it worked just fine. However, I noticed that the TextBox would stop each item from being selected, so added a Style to take care of that and styled the items a little bit too:
<ListBox ItemsSource="{Binding YourCollection}" Name="ListBox" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding ., Mode=OneWay}" IsReadOnly="True">
<TextBox.Style>
<Style>
<Setter Property="TextBox.BorderThickness" Value="0" />
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style>
<Style.Triggers>
<Trigger Property="ListBox.IsKeyboardFocusWithin" Value="True">
<Setter Property="ListBoxItem.IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I have a TreeView in my WPF application. In runtime I am binding data to the Treeview. Each node in a treeview is associated with path. I should change the color of the TreeView element If the file in the path associated with the element has an error. Say I need to change it to RED.
So Since I am binding it on fly after the treeview is entirely loaded I should traverse the tree again and I should check the path contains any error for every element in the TreeView.
How can I navigate the entire Tree element one by one from parent to root child and perform the error checking operation for each node.
Tree in XAML:
<TreeView Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" Name="treeView1"
VerticalAlignment="Stretch"
SelectedItemChanged="treeView1_SelectedItemChanged" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Top" BorderThickness="0,0,0,1" BorderBrush="LightGray">
<TreeViewItem Header="Head Tree" ItemsSource="{Binding MainComps}">
<TreeViewItem.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Foreground" Value="RED" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeViewItem.ItemContainerStyle>
<TreeViewItem.Resources>
<HierarchicalDataTemplate DataType="{x:Type TextBlock}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Head Tree" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MainCompViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Maincompname}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:FeatureViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FeatureName}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:CompViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Component}" />
</StackPanel>
</DataTemplate>
</TreeViewItem.Resources>
</TreeViewItem>
</TreeView>
In code behind:
I have treeview selection changed event.
How can I have AfterTreeViewLoaded event. Something like this.
Void TreeviewLoaded()
{
//Identify error. Change the color
}
Thanks in advance..
When you create tree items you should have some list of root items and bind the list to your TreeView.ItemsSource.
Then at any time you can check the paths and set boolean HasError (the property should exist in your ItemViewModel). Tree item's backround can be changed by style, like this:
<Setter Property="Background" Value="{Binding HasError, Converter={StaticResource HasErrorToBackroundConverter}" />
UPD:
Please see the following article http://blog.clauskonrad.net/2011/04/how-to-make-hierarchical-treeview.html.
Your ItemViewModel is just like the Folder from the article.
The list of root items is m_folders. Like the Folder contains a FullPath, your ItemViewModel will contain Path alongside HasError property.
When you need to check the path, you recursively pass m_folders, read the path from it, check it and set HasError.
I have an hierarchy like Band - Record, showing on a TreeView. I would like to show in the hierarchy a message like 'No records' when the band doesn't have any records.
I'm trying to use the TargetNullValue, but it isn't working. The band has an ObservableCollection, and if it is null or it has an null value inside it doesn't show the TargetNullValue.
Here is the XAML I'm using:
<TreeView ItemsSource="{Binding Bands, TargetNullValue='No bands'}" >
<TreeView.ItemContainerStyle>
<!--
This Style binds a TreeViewItem to a TreeViewItemViewModel.
-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type ViewModels:BandViewModel}"
ItemsSource="{Binding Children, TargetNullValue='No bands'}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BandName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type ViewModels:RecordViewModel}"
ItemsSource="{Binding Children, TargetNullValue='No records'}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RecordName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Try using the styling solution as per this question