I have this treeview with the main purpose to display open and closed windows in a building. For now the classes are "floor", which contains a list of rooms, "rooms" with a list of windows and the "roomwindow" class.
I want to display either open windows, closed windows or all windows by checking the corresponding radiobutton based on the "isOpen" boolean value of the roomwindow class.
Is there any way to temporarily hide or remove a "window" treeview item based on the boolean value of the room windows?
Or is the only possibility to iterate through the room window list and delete the windows?
The WindowCheck.xaml.cs
<Window x:Class="WpfApplication3.WindowCheck"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="WindowCheck" Height="350" Width="525" x:Name= "WindowCheckWindow">
<Grid Margin="104,113,60,0">
<Grid.Resources>
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Reverse="False"/>
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverterReverse" Reverse="True"/>
</Grid.Resources>
<RadioButton x:Name="rdbtn_open" Content="Open windows" HorizontalAlignment="Left" Height="43" Margin="99,-79,0,0" VerticalAlignment="Top" Width="118"/>
<RadioButton x:Name="rdbtn_closed" Content="Closed windows" HorizontalAlignment="Left" Height="43" Margin="256,-79,-30,0" VerticalAlignment="Top" Width="127" />
<RadioButton x:Name="rdbtn_all" Content="All windows" HorizontalAlignment="Left" Margin="-55,-79,0,0" VerticalAlignment="Top" Height="33" Width="96"/>
<TreeView ItemsSource="{Binding FloorList, ElementName=WindowCheckWindow, Mode=TwoWay}">
<!--RoomTemplate-->
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding RoomList}">
<TextBlock Text="{Binding FloorId}"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding RoomWindowList}">
<TextBlock Text="{Binding RoomId}" />
<!--Window-->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding WindowId}" >
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rdbtn_closed}" Value="True">
<Setter Property= "TextBlock.Visibility" Value="{Binding Path= IsOpen, Converter={StaticResource BoolToVisConverter}}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked, ElementName=rdbtn_open}" Value="True">
<Setter Property="TextBlock.Visibility" Value="{Binding Path=IsOpen,Converter={StaticResource BoolToVisConverterReverse}}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked, ElementName=rdbtn_all}" Value="True">
<Setter Property="TextBlock.Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding ="{ Binding IsOpen}" Value="True">
<Setter Property="Background" Value="Salmon"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsOpen}" Value="False">
<Setter Property="Background" Value="GreenYellow">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
The MainWindow.xaml.cs, where a test building is initialized:
public partial class MainWindow : Window
{
private Floor floor;
public MainWindow()
{
InitializeComponent();
InitializeBuilding();
}
public void InitializeBuilding()
{
floor = new Floor("FirstFloor");
List<RoomWindow> bathroomWindows = new List<RoomWindow>();
bathroomWindows.Add(new RoomWindow("Bathroom window 1", false));
//etc.
List<RoomWindow> livingroomWindows = new List<RoomWindow>();
livingroomWindows.Add(new RoomWindow("Livingroom window 1", false));
//etc.
Room livingRoom = new Room("Livingroom",livingroomWindows,floor);
Room bathRoom = new Room("Bathroom",bathroomWindows,floor);
floor.addRooms(livingRoom);
floor.addRooms(bathRoom);
}
//...
I added a custom BooleanToVisibilityConverter, which hides the windows based on the boolean value of "isOpen". This seems to work fine with only one problem. Now if some of the window items are collapsed there is still a blank space where the item used to be.
Related
I want to change Button formating whenever the item in ItemsControl bool property is true. However, this formatting is not working.
.cs:
<ItemsControl ItemsSource="{Binding MyList}">
<ItemsControl.ItemContainerStyle>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Selected}" Value="True">
<Setter Property="Button.BorderBrush" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding Selected}" Value="False">
<Setter Property="Button.BorderBrush" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource ButtonStyle}">
<StackPanel Orientation="Horizontal">
<Image Style="{StaticResource ImageStyle}" Source="{Binding Icon}"/>
<TextBlock Style="{StaticResource BrightText}" Text="{Binding Title}"
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've tried to add TargetType of Button to Style with Setter property only BorderBrush but that didn't help either.
I'm trying to collapse a CheckBox through Visibility if the selected item of a ComboBox is null or empty. The source is a list of objects having two string properties: Code and Name.
I'm using a trigger binded to the ComboBox text.
<ComboBox x:Name="VideoSub" SelectedItem="{Binding SubSelection, Mode=TwoWay}"
ItemsSource="{Binding Path=SubsSource}"
IsEnabled="{Binding HasItems, RelativeSource={RelativeSource Self}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Width="80" IsEnabled="{Binding ElementName=VideoSub, Path=IsEnabled}"
HorizontalAlignment="Right" Margin="0,10,0,0">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Text.Length, ElementName=VideoSub, UpdateSourceTrigger=PropertyChanged}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
It is more directly to use event SelectionChanged to control whether components are displayed.
I wrote some demo code, hope it helps:
MainWindow.xaml
<Window x:Class="TestWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">
<StackPanel>
<ComboBox Name="MyComboBox" SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem Content="Code"></ComboBoxItem>
<ComboBoxItem Content="Name"></ComboBoxItem>
</ComboBox>
<CheckBox Name="MyCheckBox" Visibility="Collapsed" />
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace TestWpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.MyComboBox.SelectedItem == null)
{
this.MyCheckBox.Visibility = Visibility.Collapsed;
}
else
{
this.MyCheckBox.Visibility = Visibility.Visible;
}
}
}
}
Furthermore, sometimes it's easier to use procedure (such as functions/methods) than definition (such as triggers etc) in programming :)
If you are using a DataTemplate, you should not trigger on elements inside it, but its data. If you want to hide the CheckBox when the SelectedItem is null, use the x:Null markup extension for comparison.
<DataTrigger Binding="{Binding SelectedItem, ElementName=VideoSub}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
As you bind Name to the TextBlock, you can add a DataTrigger to your style, that compares it to the static empty string. There is no need to reference the TextBlock itself.
<DataTrigger Binding="{Binding SelectedItem.Name, ElementName=VideoSub}" Value="{x:Static system:String.Empty}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
Note that you have to add an XML namespace for the String type.
.NET Core: xmlns:system="clr-namespace:System;assembly=System.Runtime"
.NET Framework: xmlns:system="clr-namespace:System;assembly=mscorlib"
Here the complete style that checks for null and emptyness.
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=VideoSub}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding SelectedItem.(local:PersonModel.Name), ElementName=VideoSub}" Value="{x:Static system:String.Empty}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
By the way, you probably do not need a DataTemplate at all, if you just want to display a property as text in the ComboBox. Set the DisplayMemberPath instead.
<ComboBox x:Name="VideoSub" SelectedItem="{Binding SubSelection, Mode=TwoWay}"
ItemsSource="{Binding Path=SubsSource}"
IsEnabled="{Binding HasItems, RelativeSource={RelativeSource Self}}"
DisplayMemberPath="Name"/>
Just bind to SelectedItem:
<CheckBox Width="80" IsEnabled="{Binding ElementName=VideoSub, Path=IsEnabled}"
HorizontalAlignment="Right" Margin="0,10,0,0">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=VideoSub}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
An object itself "having two string properties: Code and Name" cannot be "empty".
There either is an item selected or there isn't.
In my WPF MVVM project I use INotifyDataErrorinfo to handle validation within a DataGrid. I can successfully style error cells in my "Operator" column thus:
<DataGridTemplateColumn Header="Operator" Width="140">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Operator}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding OperatorId, Converter={StaticResource IsOperatorIdNullConverter}}" Value="False">
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Salmon"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<controls:AutoCompleteBox Text="{Binding Operator, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"
ItemsSource="{Binding Path=Data.OperatorNames, Source={StaticResource proxy}}"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="3"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
In my "OperatorType" column however this same technique doesn't work. Errors are being detected and the system default error styling is shown but my custom styling isn't. The code is:
<DataGridTemplateColumn Header="Operator type" Width="140">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding OperatorType.OperatorTypeName}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Salmon"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<controls:AutoCompleteBox ItemsSource="{Binding Path=Data.OperatorTypeNames, Source={StaticResource proxy}}"
ItemTemplate="{StaticResource AutoCompleteBoxItemOperatorTypeTemplate}"
SelectedItem="{Binding OperatorType, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
ValueMemberPath="OperatorTypeName"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="3"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The only differences as far as I can see are that:
The Text in "Operator" is using bound to a POCO (Operator) while for "OperatorType" it is bound to a POCO property (OperatorType.OperatorTypeName)
The AutoCompleteBox declarations are slightly different
I've tried numerous settings for the ToolTip DataContext but nothing seems to work.
Question
What do I need to change to get the "OperatorType" customised error styling to work?
Well, it was a bit of a journey, but the solution was to set a DataContext on the TextBlock:
<TextBlock DataContext="{Binding OperatorType}" Text="{Binding OperatorTypeName}">
This causes the Trigger to be pointed at the OperatorType POCO whereas the Text in the box is taken from OperatorType.OperatorTypeName.
How do you conditionally style treeviewitemson binded properties
I'm running into an issue where I want to style items in a Tree View, those that are valid selections vs hierarchy items.
I've tried to move my style tag into a TreeView.Resources and a TreeView.ItemContainerStyle tag but it still doesn't cause any formatting.
<TabItem Name="Queries" Header="Queries"
d:DataContext="{d:DesignInstance views:QuerySettingsView}">
<Grid>
<TreeView ItemsSource="{Binding QueryTreeModels}"
Width="500" Height="200" VerticalAlignment="Top"
HorizontalAlignment="Center" Margin="0,0,175,0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style >
<Setter Property="TreeViewItem.IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="TreeViewItem.IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsQuery}">
<Setter Property="TextBlock.Foreground" Value="Chartreuse" />
</DataTrigger>
<DataTrigger Binding="{Binding IsFolder}">
<Setter Property="TextBlock.Foreground" Value="Crimson" />
</DataTrigger>
</Style.Triggers>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</TabItem>
For DataTrigger you need to complete the condition by adding a value to DataTrigger bound field. like <DataTrigger Binding="{Binding IsQuery}" Value="true"> Refer the below code.
<TreeView x:Name="tree" Width="500" Height="200"
VerticalAlignment="Top" HorizontalAlignment="Center"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style >
<Setter Property="TreeViewItem.IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="TreeViewItem.IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsQuery}" Value="true">
<Setter Property="TreeViewItem.Foreground" Value="Chartreuse" />
</DataTrigger>
<DataTrigger Binding="{Binding IsFolder}" Value="true">
<Setter Property="TreeViewItem.Foreground" Value="Crimson" />
</DataTrigger>
</Style.Triggers>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding NodeName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I have a Treeview in my WPF application. On the fly, in run time, If the element of the Tree meets certain condition, It should change its Font color from Black To Red.!
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>
Code behind
private void treeView1_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if(selected Item meets certain condition)
{
//Change color of tree node
}
}
How can I change the color of particular Node and leave it in the same color SO that when expanded again It should be in RED.
Any help would be appreciated.
You could create a boolean property in the model which is true when the elements meets the condition. Then you bind the Foreground like so:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=BoolProp}" Value="False">
<Setter Property="Foreground" Value="Blue"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=BoolProp}" Value="True">
<Setter Property="Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
or with converter:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Foreground" Value="{Binding Path=BoolProp, Converter={StaticResource ResourceKey=TheKey}}"/>
</Style>
</TreeView.ItemContainerStyle>
Just change the Foreground:
TreeViewItem ti = (TreeViewItem)treeView1.SelectedItem;
ti.Foreground = Brushes.Red;
That is embedded into the template. You can only change the color by copying the default Aero-Style for the control and changing the hard-coded value.
Or by drilling down the visual tree on-load to change it that way.
To get the default style & tenmplate go through this MSDN
Can also check step wise EXAMPLE from here.