ListBox ItemTemplate update using ToggleButton and attached Property - c#

I want to implement a ListBox with ToggleButton when I press then the template of ListBox should be updated. The template one will have only icons and one will have both icons and text as shown in the image.
I have created one attached property for this and binded it to the ToggleButton IsChecked property inside control template, but this attached property is not updating and my template is not changing.
First time when I load the application it is working.
<Style x:Key="NavigationListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="{StaticResource PrimaryDarkBrush}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="SelectedIndex" Value="0"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border Background="{TemplateBinding Background}"
BorderThickness="0"
Padding="0"
SnapsToDevicePixels="true">
<ScrollViewer Padding="{TemplateBinding Padding}"
Focusable="false">
<StackPanel>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<ToggleButton IsChecked="{Binding local:IsOpenInfo.IsOpen,Mode=TwoWay}" Style="{DynamicResource ToggleButtonstyle}" Foreground="White">
</ToggleButton>
</StackPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate" Value="{DynamicResource NavigationDataTemplate}"/>
<Style.Triggers>
<Trigger Property="local:IsOpenInfo.IsOpen" Value="True">
<Setter Property="ItemTemplate" Value="{DynamicResource NavigationDataTemplate1}"/>
</Trigger>
<Trigger Property="local:IsOpenInfo.IsOpen" Value="False">
<Setter Property="ItemTemplate" Value="{DynamicResource NavigationDataTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ToggleButtonstyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel Orientation="Horizontal">
<iconPacks:PackIconMaterialDesign x:Name="MenuItemIcon" VerticalAlignment="Center"
HorizontalAlignment="Center" Margin="12" Kind="ArrowBack"/>
<TextBlock Text="Collapse" VerticalAlignment="Center" HorizontalAlignment="Center" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="ArrowForward"/>
<Setter TargetName="txtBlock" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="ArrowBack"/>
<Setter TargetName="txtBlock" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="NavigationDataTemplate">
<iconPacks:PackIconMaterialDesign x:Name="MenuItemIcon" VerticalAlignment="Center"
HorizontalAlignment="Center" Margin="12"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="Home">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Home"/>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Email">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Email"/>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Cloud">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Cloud"/>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Collapse">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Mail"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="NavigationDataTemplate1">
<StackPanel Orientation="Horizontal">
<iconPacks:PackIconMaterialDesign x:Name="MenuItemIcon" VerticalAlignment="Center"
HorizontalAlignment="Center" Margin="12"/>
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="Home">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Home"/>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Email">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Email"/>
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="Cloud">
<Setter TargetName="MenuItemIcon" Property="Kind" Value="Cloud"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
The attached property class as below:
public class IsOpenInfo
{
public static bool GetIsOpen(DependencyObject obj)
{
return (bool)obj.GetValue(IsOpenProperty);
}
public static void SetIsOpen(DependencyObject obj, bool value)
{
obj.SetValue(IsOpenProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.RegisterAttached("IsOpen", typeof(bool), typeof(IsOpenInfo), new PropertyMetadata(true));
}
I am clueless why the attached property not updating when IsChecked property is changed and style triggers not working?
Is there any simple way to achieve this?
Anyone guide me to handle this case?

Your style is defined with TargetType set to ListBox, the triggers will resolve the value of your custom local:IsOpenInfo.IsOpen property for the ListBox instance that your style is applied to.
However, the ToggleButton is defined inside the ControlTemplate of the ListBox. Its IsChecked binding will not automatically set the value of the attached property on its templated parent control. It will be set on the ToggleButton itself. So practically, you are setting the value on the ToggleButton, but you are checking it on the ListBox in the style, so you are operating on different values.
You can make it work by explicitly binding to the TemplatedParent, which is a ListBox instance. Both the style and the toggle button will resolve the attached property value for the same ListBox instance.
<ToggleButton IsChecked="{Binding (local:IsOpenInfo.IsOpen), RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Style="{DynamicResource ToggleButtonstyle}"
Foreground="White"/>

Related

WPF Trigger not working

I am trying to use triggers to modify the property of my control.I have a RadioButton and a Border, I want to modify the border Background when the
RadioButton IsSelected. So here is my code:
<Border BorderBrush="{DynamicResource MaterialDesignDivider}">
<RadioButton IsChecked="{Binding Erase_IsSelected}" Content="E">
<RadioButton.Resources>
<Style TargetType="{x:Type RadioButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="{DynamicResource MaterialDesignSelection}" />
</Trigger>
</Style.Triggers>
</Style>
</RadioButton.Resources>
</RadioButton>
</Border>
This seems don't work, how should I fix it? Thanks!
Use a Style for the Border that binds to the IsChecked property of the RadioButton:
<Border BorderBrush="{DynamicResource MaterialDesignDivider}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=rb}" Value="True">
<Setter Property="Background" Value="{DynamicResource MaterialDesignSelection}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<RadioButton x:Name="rb" IsChecked="{Binding Erase_IsSelected}" Content="E" />
</Border>
A RadioButton style cannot change the property of a Border.
Add the OnChecked event in your xaml:
<RadioButton Checked="Radiobutton_OnChecked" Content="E">
<RadioButton.Resources>
<Style TargetType="{x:Type RadioButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="{DynamicResource MaterialDesignSelection}" />
</Trigger>
</Style.Triggers>
</Style>
</RadioButton.Resources>
</RadioButton>
this will create you a function in your .cs like this, where you can change the background:
private void Radiobutton_OnChecked(object sender, RoutedEventArgs e)
{
RadioButton rdb = (RadioButton) sender;
rdb.Background = new SolidColorBrush(Colors.Green);
}

Style Basedon not working with checkbox

I have a style defined in a resource dictionary to make my checkbox look like a button:
<Style TargetType="CheckBox" x:Key="CBCheckBoxButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Border Name="BackgroundBorder" Background="Black" CornerRadius="20">
<Grid>
<Rectangle x:Name="UpperRect" Margin="1" Grid.Row="0" RadiusX="20" RadiusY="20" Fill="{StaticResource GrayGradient}"/>
<TextBlock Name="ButtonContent" Text="{TemplateBinding Content}" Margin="3" VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="UpperRect" Property="Fill" Value="{StaticResource LightBlueGradient}" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="UpperRect" Property="Fill" Value="{StaticResource BlueGradient}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="BackgroundBorder" Property="Background" Value="DarkGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Effect" Value="{StaticResource ShadowEffect}" />
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontFamily" Value="Calibri"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="DarkGray"/>
<Setter Property="Effect" Value="{StaticResource DisableShadowEffect}" />
</Trigger>
</Style.Triggers>
</Style>
When used like so it works great:
<CheckBox Width="80" Height="80" Margin="10" Content="{Binding Y2LockAxisString}" Command="{Binding Y2LockDisableEnableCommand}" Style="{StaticResource CBCheckBoxButton}"/>
But I need to add a style so that different commands etc. are called depending on the state of the check box. So I used the basedon property:
<CheckBox Width="80" Height="80" Margin="10">
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource CBCheckBoxButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Command" Value="{Binding ExecuteX2LockDisable}" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Command" Value="{Binding ExecuteX2LockEnable}" />
</Trigger>
</Style.Triggers>
</Style>
</CheckBox>
But the checkbox looks like a default checkbox with none of the appearance elements applied to it.
Why isn't the appearance style working?
It seems you are adding the Style as Content, instead of Style.
Have you tried
<CheckBox Width="80" Height="80" Margin="10">
<CheckBox.Style>
<Style --- snip --->
</Style>
</CheckBox.Style>
</CheckBox>

Change fontweight on button in WPF treeview

I have a templated treeview that is working just as i want, but i have a context menu which allows for a tier 1 node to be marked as "default".
I have a datatrigger which reacts to a property in the viewmodel, which should change the fontweight to bold just to visually show that this is the default node. But the setter will not change the fontweight on the button no matter what!
However if i change another value, like the foreground color for example, it works just fine, font size also no problem.
Why is this, can anyone explain this? Here is some code if needed:
Trigger:
<HierarchicalDataTemplate ItemsSource="{Binding Children,Mode=TwoWay,NotifyOnSourceUpdated=True}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Image x:Name="nodeImg" Source="{Binding Image}" MaxHeight="16" MaxWidth="16"/>
<Button x:Name="nodeButton" Content="{Binding Name}" Command="{Binding Command}" Style="{StaticResource TreeMenuButton}"/>
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem},Path=IsExpanded}" Value="True">
<Setter TargetName="nodeImg" Property="Source" Value="{Binding ImageExpanded}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsDefaultConnection}" Value="True">
<Setter TargetName="nodeButton" Property="FontWeight" Value="Bold"></Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
Default style on button:
<Style x:Key="TreeMenuButton" TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="MinHeight" Value="23" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border"
CornerRadius="0"
BorderThickness="0"
Background="Transparent"
BorderBrush="Transparent">
<ContentPresenter Margin="2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'd be surprised if your code even built as you can't add a DataTrigger into a UIElement.Triggers collection. Try using a Style instead (this definitely works):
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsDefault}" Value="True">
<Setter Property="TextBlock.FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
If by some miracle your DataTemplate does work in a UIElement.Triggers collection, then try using the class name as well as the property name:
<Setter Property="TextBlock.FontWeight" Value="Bold" />

Style disabled components in datagrid

I'd like to get rid of the grey-fading out behavior of datagrid cells (Checkboxes, Timepicker, comboboxes etc) when they have been set to disabled.
So this is what I'm trying to do:
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{x:Null}" />
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
But it doesn't work.
Do I need to define the styles individually for the controls that's actually inside the datagridcell (Checkboxes, comboboxes etc)? What's a good way of making this work?
Firstly, the fact is that the property IsEnabled hard-coded in certain control, so setting properties such as Background impossible, because the color hardcoded in the control. For example - Background in: ComboBox, TextBox, etc. Therefore, in such cases, create styles and templates, overriding the default behavior of the control (in our case: IsEnabled=False behavior).
Secondly, what control in the assigned property DataTemplate IsEnabled like this:
<DataGridTemplateColumn x:Name="ComboBoxColumn" Header="ComboBox Header" Width="110">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEnabled="False" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Not to say that DataGridCell will be False, therefore, the trigger will not fire:
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
Hence the conclusion:
Determine the behavior of the control when it is enabled false. Styles for each control can be taken from MSDN.
Example style for CheckBox (link):
<Style x:Key="{x:Type CheckBox}" TargetType="CheckBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource CheckBoxFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Border x:Name="Border" Width="13" Height="13" CornerRadius="0" Background="{StaticResource NormalBrush}" BorderThickness="1" BorderBrush="{StaticResource NormalBorderBrush}">
<Path Width="7" Height="7" x:Name="CheckMark" SnapsToDevicePixels="False" Stroke="{StaticResource GlyphBrush}" StrokeThickness="2" Data="M 0 0 L 7 7 M 0 7 L 7 0" />
</Border>
</BulletDecorator.Bullet>
<ContentPresenter Margin="4,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" RecognizesAccessKey="True" />
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="CheckMark" Property="Data" Value="M 0 7 L 7 0" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<!-- Here set the some properties there IsEnabled will be false -->
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="Red" />
<Setter TargetName="Border" Property="BorderBrush" Value="Green" />
<Setter Property="Foreground" Value="Orange"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have defined the attached DependencyProperty for a column that shows whether it is turned off. Thereafter, each control in the template column is referenced like that:
<DataGridTemplateColumn x:Name="CheckBoxColumn" local:MyDependencyClass.IsEnabledColumn="False" Width="110" Header="CheckBox Header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Content="My CheckBox" IsEnabled="{Binding Source={x:Reference Name=CheckBoxColumn}, Path=(local:MyDependencyClass.IsEnabledColumn)}" IsChecked="False" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Set the property IsEnabledColumn, you can set for those controls that you want to disable (IsEnabled=False).
Listing of MyDependencyClass:
public class MyDependencyClass : DependencyObject
{
public static readonly DependencyProperty IsEnabledColumnProperty;
public static void SetIsEnabledColumn(DependencyObject DepObject, bool value)
{
DepObject.SetValue(IsEnabledColumnProperty, value);
}
public static bool GetIsEnabledColumn(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsEnabledColumnProperty);
}
static MyDependencyClass()
{
PropertyMetadata MyPropertyMetadata = new PropertyMetadata(false);
IsEnabledColumnProperty = DependencyProperty.RegisterAttached("IsEnabledColumn",
typeof(bool),
typeof(MyDependencyClass),
MyPropertyMetadata);
}
}
P.S. Don't worry about {x:Reference} warning message, it can be ignored.

Attached Behavior in Trigger Causes Build Error

I have a Style with a control template and I am having trouble getting it to compile. I am trying to trigger an attached behavior. If I put it in the control template triggers it works fine...but if I put it in the textbox triggers I get a build error that says:
Cannot find the static member 'SelectAllProperty' on the type
'TextBoxBehavior'
Here is my code:
<Style x:Key="RenamingTextBox" TargetType="{x:Type TextBox}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<TextBlock x:Name="block" Visibility="Visible" Text="{TemplateBinding Text}" Margin="0"/>
<TextBox x:Name="box" Visibility="Collapsed" Text="{TemplateBinding Text}" Margin="0">
<TextBox.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.Setters>
<Setter TargetName="box" Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<!-- This next line gives an error even though it is the same format as the one below -->
<Setter Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>
</Trigger.Setters>
</Trigger>
</TextBox.Triggers>
</TextBox>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsRenaming}" Value="true">
<DataTrigger.Setters>
<Setter TargetName="block" Property="TextBox.Visibility" Value="Collapsed" />
<Setter TargetName="box" Property="TextBox.Visibility" Value="Visible" />
<!-- Uncommenting below works fine -->
<!--<Setter TargetName="box" Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>-->
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Any ideas as to why one gives a build error and the other doesn't?
Nevermind, I needed to put the triggers for the textbox in a style instead:
<TextBox x:Name="box" Visibility="Collapsed" Text="{TemplateBinding Text}" Margin="0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<Setter Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Categories