xaml usercontrol multidatatrigger from parent control and itself (usercontrol) - xaml

So I'm trying to learn how to dynamically apply style changes to controls. I have not been able to get a user control to change its borderbrush and background based off a radio button in the main window and the usercontrol's text property. Basing it just off the usercontrol's text property does seem to work. So it appears that I'm doing something wrong with getting the radio button's isCheck property.
I've simplified from the original code but this still shows the issue.
MainWindow.xaml
<Window x:Class="UserControlTest.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"
xmlns:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<RadioButton x:Name="calcPace" TabIndex="1" Content="Pace" HorizontalAlignment="Left" Margin="34,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="41"/>
<RadioButton x:Name="calcDistance" TabIndex="2" Content="Distance" HorizontalAlignment="Left" Margin="80,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="61"/>
<RadioButton x:Name="calcTime" TabIndex="3" Content="Time" HorizontalAlignment="Left" Margin="146,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
Height="16" Width="42"/>
<local:TextBoxTime/>
</Grid>
</Window>
TextBoxTime.xaml (usercontrol):
<UserControl x:Class="UserControlTest.TextBoxTime"
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:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox x:Name="timeString" TabIndex="4" HorizontalAlignment="Left" Height="23" Margin="68,130,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="PaleGreen"/>
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=calcTime, Path=IsChecked}" Value="False"/>
<Condition Binding="{Binding ElementName=timeString, Path=Text}" Value=""/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Background" Value="Snow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
Currently I've added no code behind for either.
Thanks

Here's how I might do it:
public partial class RequireableTextBox : UserControl
{
public RequireableTextBox()
{
InitializeComponent();
}
#region IsRequired Property
public bool IsRequired
{
get { return (bool)GetValue(IsRequiredProperty); }
set { SetValue(IsRequiredProperty, value); }
}
public static readonly DependencyProperty IsRequiredProperty =
DependencyProperty.Register(nameof(IsRequired), typeof(bool), typeof(RequireableTextBox),
new PropertyMetadata(false));
#endregion IsRequired Property
#region Text Property
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(String), typeof(RequireableTextBox),
// Default must be "" not null, for the trigger to understand
new PropertyMetadata(""));
#endregion Text Property
}
XAML
<UserControl
x:Class="UserControlTest.RequireableTextBox"
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:local="clr-namespace:UserControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
IsTabStop="False"
>
<Grid>
<TextBox
x:Name="timeString"
HorizontalAlignment="Left"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged}"
>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="PaleGreen"/>
<Setter Property="Background" Value="White"/>
<Style.Triggers>
<!--
Seemed right to disable when unneeded; delete this trigger
if you'd rather not.
-->
<DataTrigger
Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="False"
>
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="True"
/>
<Condition
Binding="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value=""
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="Background" Value="Snow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</UserControl>
Usage:
<StackPanel>
<RadioButton x:Name="calcTime" GroupName="CalculationType">Calculate Time</RadioButton>
<RadioButton x:Name="calcDistance" GroupName="CalculationType">Calculate Distance</RadioButton>
<local:RequireableTextBox
IsRequired="{Binding IsChecked, ElementName=calcTime}"
/>
<local:RequireableTextBox
x:Name="DistanceValue"
IsRequired="{Binding IsChecked, ElementName=calcDistance}"
/>
<!-- Just tossed this in to demonstrate the Text property -->
<Label Content="{Binding Text, ElementName=DistanceValue}" Foreground="Gray" />
</StackPanel>

Related

Bind properties to styles defined at ResourceDictionary to eliminate code duplication

I have the following XAML code:
<Window x:Class="LinkButton.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"
xmlns:local="clr-namespace:LinkButton"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainWindowVM}">
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10" />
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="ddk" />
<TextBlock Grid.Row="0" Grid.Column="1" >
<Hyperlink Command="{Binding Link}"
CommandParameter="{Binding}"
Foreground="Blue" >
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding Description01.Header}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" Text="dde" />
<TextBlock Grid.Row="1" Grid.Column="1">
<Hyperlink Command="{Binding Link}"
CommandParameter="{Binding}"
Foreground="Blue" >
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding Description11.Header}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
</Grid>
</Window>
And the C# Code code:
public class TestCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
#endregion
}
public class LongDescription
{
public string Header { get; }
public string Description { get; }
public LongDescription(string header, string description)
{
Header = header;
Description = description;
}
}
public class MainWindowVM
{
public ICommand Link => new TestCommand(ExecuteCommand1, CanExecuteCommand1);
public LongDescription Description11 => new LongDescription("cell11", "result cell11");
public LongDescription Description01 => new LongDescription("cell01", "result cell01");
public bool CanExecuteCommand1(object parameter)
{
return true;
}
public void ExecuteCommand1(object parameter)
{
MessageBox.Show("Executing command 1");
}
}
It is clear that I have duplicated code in XAML ( <Hyperlink.Inlines> etc). I want to refactor it so that the code duplication is eliminated. For that I am thinking of defining the style <Hyperlink.Inlines> in ResourceDictionary and then bind it to appropriate properties in MainWindowVM.
But I am unsure how to do it, any ideas?
You can easily move the Style in a ResourceDictionary like this
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Key is required to identify the Style -->
<Style x:Key="Bind01" TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Description01.Header}" />
</Style>
<Style x:Key="Bind11" TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Description11.Header}" />
</Style>
</ResourceDictionary>
And merge the Dictionary in your Window to use the Style
Merge
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="YourDictionaryHere"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10" />
</Style>
</ResourceDictionary>
</Window.Resources>
Use
<TextBox Style="{DynamicResource Bind01}"/>
Simplification
Instead of putting the variable Binding in a Style (or Dictionary), i suggest to write the variable Bindings directly into the Control and define the rest as Style.
More Specific: The following Markup displays a bound string as a Hyperlink which executes a ICommand when clicked.
<TextBlock>
<Hyperlink Command="{Binding Link}"
CommandParameter="{Binding}"
Foreground="Blue" >
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding Description11.Header}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
We could instead define a Style for a Button which looks (and does) the same, but the variable Binding can be set directly via Content.
Button Style
<Style x:Key="LinkStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock>
<Hyperlink Command="{Binding Link}" CommandParameter="{Binding}">
<Run Text="{TemplateBinding Content}"/>
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Apply style to Elements in Grid (replace TextBlock with styled Buttons)
<TextBlock Grid.Row="0" Grid.Column="0" Text="ddk" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description01.Header}"
Style="{DynamicResource LinkStyle}">
<TextBlock Grid.Row="1" Grid.Column="0" Text="dde" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description11.Header}"
Style="{DynamicResource LinkStyle}">
Screens (dashed Lines are Gridlines)
Edit
To set the Command of the Hyperlink we use the Command Property of the Button to set the Binding. Therefore we must add a TemplateBinding in our Style. Replace the "Hard Coded" Command with a TemplateBinding to the Button Command. Do the same for the Commandparameter.
<Hyperlink Command="{TemplateBinding Command}"
CommandParameter="{Templatebinding Commandparameter}"
Foreground="Blue" >
And set the Command and the CommandParameter in the styled Button
<TextBlock Grid.Row="0" Grid.Column="0" Text="ddk" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description01.Header}"
Command="{Binding YOURCOMMANDHERE}"
CommandParameter="{YOURPARAMETER}"
Style="{DynamicResource LinkStyle}">
<TextBlock Grid.Row="1" Grid.Column="0" Text="dde" />
<Button Grid.Row="1" Grid.Column="1"
Content="{Binding Description11.Header}"
Command="{Binding YOUROTHERCOMMANDHERE}"
CommandParameter="{YOUROTHERPARAMETER}"
Style="{DynamicResource LinkStyle}">

VB.net XAML datagrid cell colour change based on value

My XAML code:
<Window x:Class="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"
xmlns:local="clr-namespace:Datagrid_Binding"
mc:Ignorable="d"
Title="MainWindow" Height="8517" Width="1244">
<Grid>
<DataGrid x:Name="WaterfallDataGrid" CanUserSortColumns="False" ColumnWidth="60" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Load" Binding="{Binding Load}"></DataGridTextColumn>
<DataGridTextColumn Header="PF" Binding="{Binding PF}"></DataGridTextColumn>
<DataGridTextColumn Header="Spare" Binding="{Binding Spare}"></DataGridTextColumn>
</DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Load}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="Full Load">
<Setter Property="Background" Value="LightGreen"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid>
</Grid>
</Window>
My VB.net code.
Class MainWindow
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim dt3 As New DataTable("Waterfall")
dt3.Columns.Add("Load")
dt3.Columns.Add("PF")
dt3.Columns.Add("Spare")
'dt3.rows.add(New Object() {"full load", "0.8", "20%"})
dt3.Rows.Add("full load", "0.8", "20%")
WaterfallDataGrid.ItemsSource = dt3.DefaultView
End Sub
End Class
What I would like to do is when the cell has the text "full load" it changes its cell colour. I am getting an error at runtime which says "Items collection must be empty before using ItemsSource." Very puzzled with this. Some help appreciated.
The problem is that you defined the same column twice.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="WaterfallDataGrid" CanUserSortColumns="False" ColumnWidth="60" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Load" Binding="{Binding Load}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="full load">
<Setter Property="Background" Value="LightGreen"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="PF" Binding="{Binding PF}"></DataGridTextColumn>
<DataGridTextColumn Header="Spare" Binding="{Binding Spare}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

Trying to bind column headers to properties in my C# code

I currently have:
<Window x:Class="Client_SCM.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"
xmlns:local="clr-namespace:Client_SCM"
mc:Ignorable="d"
Title="Swords Call Monitor 2.0" Height="350" Width="474">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="5"
ShowGridLines="True">
<DataGrid x:Name="dataGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="5"
AlternatingRowBackground="Aqua" Loaded="dataGrid_Loaded" AutoGenerateColumns="False"/>
</Grid>
And I'm trying to implement something like this into it:
<DataGridTextColumn Binding="{Binding WhateverIWantToDisplay}" >
<Setter Property="Background" Value="Green" />
<Style.Triggers>
<DataTrigger Binding="{Binding Foo}" Value="1">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
<DataTrigger Binding="{Binding Foo}" Value="2">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Foo}" Value="2">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
The error I'm getting is "The property 'Content' can only be set once".
Any help would be appreciated!
Try using Header instead of Binding on your DataGridTextColumn.
<Window x:Class="Client_SCM.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"
xmlns:local="clr-namespace:Client_SCM"
mc:Ignorable="d"
Title="Swords Call Monitor 2.0" Height="350" Width="474">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" ShowGridLines="True">
<DataGrid x:Name="dataGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5"
AlternatingRowBackground="Aqua" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="{Binding WhateverIWantToDisplay}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

Using Pivot.ItemTemplate to avoid replication

The following XAML works OK:
<Page ...
<Page.Resources>
<SolidColorBrush x:Key="brushHeaderBkgr" Color="Blue" />
<SolidColorBrush x:Key="brushContentBorder" Color="Aquamarine" />
<SolidColorBrush x:Key="brushContentBkgr" Color="Blue" />
<SolidColorBrush x:Key="brushContentList" Color="Red" />
<Style TargetType="Border" x:Key="ContentFrame">
<Setter Property="BorderBrush" Value="{StaticResource brushContentBorder}" />
<Setter Property="BorderThickness" Value="6" />
<Setter Property="Padding" Value="6" />
<Setter Property="Background" Value="{StaticResource brushContentBkgr}" />
</Style>
<Style TargetType="ListView" x:Key="ContentList" >
<Setter Property="Background" Value="{StaticResource brushContentList}" />
</Style>
</Page.Resources>
<Grid>
<Pivot Title="Whatever" Margin="10">
<PivotItem Header="Header1" >
<Border Style="{StaticResource ContentFrame}">
<ListView x:Name="Content" Style="{StaticResource ContentList}" />
</Border>
</PivotItem>
<PivotItem Header="Header2" >
<Border Style="{StaticResource ContentFrame}">
<ListView x:Name="Content" Style="{StaticResource ContentList}" />
</Border>
<PivotItem Header="Header3" >
<Border Style="{StaticResource ContentFrame}">
<ListView x:Name="Content" Style="{StaticResource ContentList}" />
</Border>
</Pivot>
</Grid>
</Page>
Since all item pages have same structure, I wanted to avoid replication by using Pivot.ItemTemplate. Here is the modified XAML:
<Page ...
<Page.Resources>
<SolidColorBrush x:Key="brushHeaderBkgr" Color="Blue" />
<SolidColorBrush x:Key="brushContentBorder" Color="Aquamarine" />
<SolidColorBrush x:Key="brushContentBkgr" Color="Blue" />
<SolidColorBrush x:Key="brushContentList" Color="Red" />
<Style TargetType="Border" x:Key="ContentFrame">
<Setter Property="BorderBrush" Value="{StaticResource brushContentBorder}" />
<Setter Property="BorderThickness" Value="6" />
<Setter Property="Padding" Value="6" />
<Setter Property="Background" Value="{StaticResource brushContentBkgr}" />
</Style>
<Style TargetType="ListView" x:Key="ContentList" >
<Setter Property="Background" Value="{StaticResource brushContentList}" />
</Style>
</Page.Resources>
<Grid>
<Pivot Title="Whatever" Margin="10">
<Pivot.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource ContentFrame}">
<ListView x:Name="Content" Style="{StaticResource ContentList}" />
</Border>
</DataTemplate>
</Pivot.ItemTemplate>
<PivotItem Header="Header1" />
<PivotItem Header="Header2" />
<PivotItem Header="Header3" />
</Grid>
</Page>
However this doesn't work, the items don't appear. Why?
You can't set ItemTemplate and add PivotItems in this way. You need to add the data source to the pivot via the ItemSource and use binding in your item templates.
Look at this code.
MainPage.xaml:
<Pivot x:Name="Pivot" Title="Whatever" Margin="10">
<Pivot.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource ContentFrame}">
<ListView ItemsSource="{Binding ListViewSource}" Style="{StaticResource ContentList}" />
</Border>
</DataTemplate>
</Pivot.ItemTemplate>
<Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
MainPage.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
List<PivotModel> items = new List<PivotModel>();
items.Add(new PivotModel() { Header = "Header1", ListViewSource = Enumerable.Range(10, 10).ToList() });
items.Add(new PivotModel() { Header = "Header2", ListViewSource = Enumerable.Range(20, 10).ToList() });
items.Add(new PivotModel() { Header = "Header3", ListViewSource = Enumerable.Range(30, 10).ToList() });
items.Add(new PivotModel() { Header = "Header4", ListViewSource = Enumerable.Range(40, 10).ToList() });
Pivot.ItemsSource = items;
}
PivotModel.cs:
public class PivotModel
{
public string Header { get; set; }
public List<int> ListViewSource { get; set; }
}

XAML UserControl - Set Background based on Trigger

I have a UserControl that contains an Expander :
<UserControl x:Class="Client.DevicesExpander"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="devicesExpander" >
<Expander Margin="1" Name="expander" FontSize="11" BorderThickness="1" BorderBrush="DarkRed" Foreground="Black" Header="{Binding ElementName=devicesExpander, Path=Header}" FontWeight="Bold" MouseDoubleClick="Expander_MouseDoubleClick" Expanded="Expander_Expanded" IsExpanded="{Binding ElementName=devicesExpander, Path=IsExpanded}" Background="{Binding ElementName=devicesExpander, Path=Background}">
<StackPanel Name="_devicesPanel">
<ListBox BorderThickness="0,1,0,0" Name="_devicesList" FontWeight="Normal" MouseDoubleClick="DevicesList_MouseDoubleClick" Background="{Binding ElementName=devicesExpander, Path=Background}" />
</StackPanel>
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
</UserControl>
Ans basically all I'd like to do is change the Expander and StackPanel's Background color based on the IsExpanded of the UserControl (or Expander).
I've added three Dependancy Properties to the control :
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(DevicesExpander));
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(DevicesExpander));
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(System.Windows.Media.Brush), typeof(DevicesExpander));
But my code does not work. The IsExpanded property from the usercontrol does work as the property changes accordingly (when the expander expands) when checked from within the window where the usercontrol is placed.
How can I change the background color of the Expander based on the UserControl.IsExpanded property ?
Thanks !
EDIT:
I have in the meanwhile done the following:
<UserControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Expander Margin="1" Name="expander" FontSize="11" BorderThickness="1" BorderBrush="DarkRed" Foreground="Black" Header="{Binding ElementName=devicesExpander, Path=Header}" FontWeight="Bold" MouseDoubleClick="Expander_MouseDoubleClick" Expanded="Expander_Expanded" IsExpanded="{Binding ElementName=devicesExpander, Path=IsExpanded}" Background="Transparent">
<StackPanel Name="_devicesPanel">
<ListBox BorderThickness="0,1,0,0" Name="_devicesList" FontWeight="Normal" MouseDoubleClick="DevicesList_MouseDoubleClick" Background="Transparent" />
</StackPanel>
</Expander>
and removed the BackgroundProperty dependancy property. I actually thought this could work, but alas ...
I have managed to solve my problem. Herewith the solution (showing only the essential code) ...
My Dependancy Property is created as follows:
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(DevicesExpander));
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
and my XAML is :
<UserControl x:Class="TestApp.DevicesExpander"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="devicesExpander">
<Expander>
<Expander.Style>
<Style TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="True">
<Setter Property="Background" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=devicesExpander, Path=IsExpanded}" Value="False">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
</Expander>
</UserControl>
The solution in the end was to remove the Backgroud property from the element and specify a DataTrigger for IsExpanded=True and IsExpanded=False. It thus seems like the Background property when specified in the element properties overrides anything that the triggers tried to set.
Hope this helps someone !
Herewith another solution to the same problem, this time using a IValueConverter ...
My XAML:
<UserControl.Resources>
<local:BackgroundConverter x:Key="backgroundConvertor" />
</UserControl.Resources>
<Expander>
<Expander.Style>
<Style TargetType="Expander">
<Setter Property="Background" Value="{Binding ElementName=devicesEntry, Path=IsExpanded, Converter={StaticResource backgroundConvertor}}" />
</Style>
</Expander.Style>
</Expander>
and my code for the value convertor :
public class BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "White" : "Transparent";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string color = value as string;
if (color == "White")
return true;
return false;
}
}
I think though that I prefer the XAML only solution, although the IValueConverter option does make for slightly better reading of the XAML ...
(The DependancyProperty is still created exactly the same way)