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.
Related
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.
Ok. So I have a TabControl object in my xaml that has an ItemsSource value of ItemsSource={Binding OpenTabs} where OpenTabs is an ObservableCollection of type ClosableTab (public ObservableCollection<ClosableTab> OpenTabs { get; set; }) which extends TabItem. I found ClosableTab from here and then have adapted it's view for my own needs.
Primarily I have added a property (and sorry for the confusion in names here) isProperty. This is for a real estate program. Then in my xaml I have the following lines:
<DataTemplate x:Key="PropertyTemplate">
<Grid>
<TextBlock Text="{Binding address}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TennantTemplate">
<Grid>
<TextBlock Text="{Binding name}"/>
</Grid>
</DataTemplate>
//... That's in <Windows.Resources>
<TabControl ItemsSource="{Binding OpenTabs}" Grid.Column="1" x:Name="Tabs">
<TabControl.Resources>
<DataTemplate x:Key="DefaultTab">
<ContentControl>
<ContentControl.Triggers>
<DataTrigger Binding="{Binding isProperty}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource PropertyTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding isProperty}" Value="False">
<Setter Property="ContentTemplate" Value="{StaticResource TennantTemplate}" />
</DataTrigger>
</ContentControl.Triggers>
</ContentControl>
</DataTemplate>
</TabControl.Resources>
</TabControl>
I've done some research and found that this is what I need to do if I want to have a certain DataTemplate dependant on the property in ClosableTab called isProperty.
It's not giving me what I want. Can someone please explain to me what I'm doing wrong here? And tell me what I should do? And/or possibly give me an alternative method? I can't think of what I need to change to get the functionality that I need. Thanks in advance.
You need to set DataType on DataTemplate to get it applied automatically to underlying data objects in case you are defining DataTemplate under Resources section.
<DataTemplate DataType="local:ClosableTab">
<ContentControl>
<ContentControl.Triggers>
<DataTrigger Binding="{Binding isProperty}" Value="True">
<Setter Property="ContentTemplate"
Value="{StaticResource PropertyTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding isProperty}" Value="False">
<Setter Property="ContentTemplate"
Value="{StaticResource TennantTemplate}" />
</DataTrigger>
</ContentControl.Triggers>
</ContentControl>
</DataTemplate>
Make sure to declare local namespace at root level to the one where ClosableTab is declared.
OR
Instead of adding DataTemplate in resources, set it explicitly as ItemTemplate of TabControl.
<TabControl>
<TabControl.ItemTemplate>
<DataTemplate x:Key="DefaultTab">
.....
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
UPDATE
Ideal case would be to have single DataTemplate and apply dataTrigger on TextBlock instead.
<TabControl ItemsSource="{Binding OpenTabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding address}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding isProperty}"
Value="False">
<Setter Property="Text" Value="{Binding name}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
I have a GridViewColumn with Combobox BAsed on ComboBox selected I want to dynamically put UI elements.Below given is the code.OperatorList has three values "Between","After","Before".Based on this selection the template has to be loaded.If "Between" Multiple TextBox else Single textbox.This part works fine.But to get the content of this and store in Model Class for each row is where i am encountering problem.
<StackPanel>
<ComboBox Grid.Column="0" ItemsSource="{Binding Path=OperatorList}" IsEditable="True"
IsSynchronizedWithCurrentItem="True"
SelectedValue="{Binding ReferenceOperatorSelected}" />
<UserControl x:Name="MyControl">
</UserControl>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ReferenceOperatorSelected}" Value="Between">
<Setter TargetName="MyControl" Property="ContentTemplate" Value="{StaticResource MultipleTextBoxTemplate}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ReferenceOperatorSelected}" Value="After">
<Setter TargetName="MyControl" Property="ContentTemplate" Value="{StaticResource SingleTextBoxTemplate}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ReferenceOperatorSelected}" Value="Before">
<Setter TargetName="MyControl" Property="ContentTemplate" Value="{StaticResource SingleTextBoxTemplate}"></Setter>
</DataTrigger>
</DataTemplate.Triggers>
<DataTemplate x:Key="MultipleTextBoxTemplate">
<StackPanel>
<TextBox Text="{Binding Path=BetweenValue1,Mode=TwoWay,NotifyOnSourceUpdated=True}" MinWidth="40"></TextBox>
<TextBox Text="{Binding Path=BetweenValue2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" MinWidth="40"></TextBox>
</StackPanel>
</DataTemplate>
In my Model class I have two properties BetweenValue1 and BetweenVAlue2 ...These are not updated when i key in value in textboxes..
You have not set the Content for your UserControl. just try doing
<UserControl x:Name="MyControl" Content="{Binding}"/>
I'm trying to change the text of a textblock based on a datatrigger. The datatrigger looks like this:
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowFileExtensionsProperty}" Value="true">
<Setter Property="Text" Value="{Binding Path=Name}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ShowFileExtensionsProperty}" Value="false">
<Setter Property="Text" Value="{Binding Path=NameWithoutExtension}" />
</DataTrigger>
</Style.Triggers>
</Style>
This code is found within the xaml file of the class "MyView" The cs file of this class contains the following code:
public static readonly DependencyProperty ShowFileExtensionsProperty =
DependencyProperty.Register("ShowFileExtensions", typeof(bool), typeof(MyView), new UIPropertyMetadata(false));
public bool ShowFileExtensions
{
get { return (bool)GetValue(ShowFileExtensionsProperty); }
set
{
SetValue(ShowFileExtensionsProperty, value);
updateViewCollection();
}
}
The textblock is displayed within a listview, that has been bound to a list of objects, these objects contain the property "Name" and "NameWithoutExtension".
The problem is that the text in the textblock always stays blank. Somehow the datatrigger is never triggered. Any idea what is wrong with the datatriggers?
PS: The full code follows
<UserControl x:Class="com.xploreplus.Filedrops.explorer.listview.FiledropsFileList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:l="clr-namespace:com.xploreplus.Filedrops.explorer.listview.views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style x:Key="Extension" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowFileExtensionsProperty, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" Value="true">
<Setter Property="Text" Value="brol2" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ShowFileExtensionsProperty, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" Value="false">
<Setter Property="Text" Value="blabla" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<ListView Name="viewComponent">
<ListView.View>
<GridView>
<GridViewColumn Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Source="{Binding Path=Icon}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource Extension}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Extension" DisplayMemberBinding="{Binding Path=Extension}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
As the textblocks are shown in listview then the DataContext of textblock will the ItemDataContext if not set explicitly. That is why your bindings are not working. You will have to provide the relativesource to binding
Try updating the bindings like
<DataTrigger Binding="{Binding Path=ShowFileExtensions, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" Value="true">
used {x:Type Control} assuming this is the type of your view else you will have to change to your view type.
Hope this helps
Thanks
I am having following DataGrid Code
<UserControl x:Class="abc.WPFApp.UCGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPFtoolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:local="clr-namespace:abc.WPFApp">
<UserControl.Resources>
<!--Restrict editing based on IsVariable-->
<Style x:Key="CheckBoxCellStyle" TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsVariable}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</UserControl.Resources>
<Grid>
<WPFtoolkit:DataGrid x:Name="UCdataGridView" ItemsSource="{Binding}"
CellStyle="{StaticResource defaultCellStyle}"
RowStyle="{StaticResource defaultRowStyle}"
ColumnHeaderStyle="{StaticResource defaultDataGridColumnHeader}"
SelectionUnit="FullRow"
IsSynchronizedWithCurrentItem="True"
RowBackground="White"
AlternatingRowBackground="AliceBlue"
AutoGenerateColumns="False" SelectionMode="Extended" RowHeaderWidth="20"
CanUserAddRows="True" CanUserDeleteRows="True" CanUserReorderColumns="False"
CanUserResizeColumns="True" AllowDrop="True" KeyUp="UCGridKeyUp" >
<WPFtoolkit:DataGrid.Columns>
<WPFtoolkit:DataGridCheckBoxColumn x:Name="dgChkRepeatingData" Binding="{Binding Path=MasterDataFlag}" MaxWidth="135" MinWidth="80"
Header="Repeating data" Visibility="Collapsed" IsReadOnly="{Binding (IsVariable)}"
EditingElementStyle="{StaticResource CheckBoxCellStyle}"
>
</WPFtoolkit:DataGridCheckBoxColumn>
<WPFtoolkit:DataGridCheckBoxColumn MaxWidth="100" Header="Max Element" x:Name="dgChkMaxElement"
Binding="{Binding Path=MaxElement}" MinWidth="70" Visibility="Collapsed"
EditingElementStyle="{StaticResource CheckBoxCellStyle}">
</WPFtoolkit:DataGridCheckBoxColumn>
<WPFtoolkit:DataGridCheckBoxColumn MaxWidth="100" Header="In For Loop" x:Name="dgChkInForLoop"
Binding="{Binding Path=InForLoop}" MinWidth="70" Visibility="Collapsed"
EditingElementStyle="{StaticResource CheckBoxCellStyle}">
</WPFtoolkit:DataGridCheckBoxColumn>
<WPFtoolkit:DataGridTextColumn x:Name="dgXPath" Binding="{Binding Path=XPath}" Header="XPath" Width="500"
Visibility="Collapsed" IsReadOnly="{Binding Path=IsVariable}"
EditingElementStyle="{StaticResource TextBoxCellStyle}"/>
</WPFtoolkit:DataGrid.Columns>
</WPFtoolkit:DataGrid>
This user control Grid comprised if three Columns which should be mutually exclusive.
I want to achieve this by creating Triggers in XAML itself , How can we do this ?
You can use radiobuttons with a checkbox style to achieve mutually exclusive behavior. If you have any problem binding radiobuttons: radiobutton-binding
<RadioButton IsChecked="{Binding VMProperty}" GroupName="IsSelectedGroup">
<RadioButton.Style>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=RadioButton}}" Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</RadioButton.Style>
</RadioButton>
But, i agree with Jaster. If you have each row binded to a ViewModel, the place to put this logic restriction is the ViewModel.
i am trying by creating such Style for 3rd column "InFor" But it is not at all working
<Style x:Key="mutualCheckInForLoop" TargetType="{x:Type WPFtoolkit:DataGridCell}" BasedOn="{StaticResource {x:Type WPFtoolkit:DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=dgChkRepeatingData,Path=IsChecked}" Value="True">
<Setter Property="CheckBox.IsChecked" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=dgChkMaxElement,Path=IsChecked}" Value="True">
<Setter Property="CheckBox.IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>