How to change list value on certain position click? - xaml

I have a list of products in BindableLayout.
When i click on some product i want to change the value of the Active field and "reload" the list print1.
There is any way to change the values of the list from the xaml?
What is the best approach?
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public List<ProductStatus> ProductList => getProducts();
public MainPage()
{
InitializeComponent();
this.BindingContext = this;
}
public List<ProductStatus> getProducts()
{
return new List<ProductStatus>
{
new ProductStatus { Id="P13", Name = "Product X", Active = false },
new ProductStatus { Id="P17", Name = "Product Y", Active = false },
new ProductStatus { Id="P21", Name = "Product Z", Active = true },
};
}
}
public class ProductStatus
{
public string Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
}
MainPage.xaml
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout BindableLayout.ItemsSource="{Binding ProductList}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal" Spacing="1" Padding="20">
<StackLayout.Style>
<Style TargetType="StackLayout">
<Style.Triggers>
<DataTrigger Binding="{Binding Active}" Value="true" TargetType="StackLayout">
<Setter Property="BackgroundColor" Value="#f0f0f0"/>
</DataTrigger>
<DataTrigger Binding="{Binding Active}" Value="false" TargetType="StackLayout">
<Setter Property="BackgroundColor" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackLayout.Style>
<Label Text="{Binding Name}"/>
<Label HorizontalOptions="EndAndExpand">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding Active}" Value="true" TargetType="Label">
<Setter Property="TextColor" Value="Green"/>
<Setter Property="Text" Value="A"/>
</DataTrigger>
<DataTrigger Binding="{Binding Active}" Value="false" TargetType="Label">
<Setter Property="TextColor" Value="Red"/>
<Setter Property="Text" Value="I"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<Button Text="Save"/>
</StackLayout>

First if you want to display a list,it's better to use ListView or CollectionView,that's will be more convenient.
If you still want to use BindableLayout here,you could add a TapGestureRecognizer to your StackLayout.And let your ProductStatus implement INotifyPropertyChanged interface(for reload the data).
public partial class MainPage: ContentPage
{
public List<ProductStatus> ProductList => getProducts();
public ICommand TapCommand { get; set; }
public MainPage()
{
InitializeComponent();
TapCommand = new Command<ProductStatus>(OnTapCommand);
this.BindingContext = this;
}
private void OnTapCommand(ProductStatus obj)
{
var product = obj;
product.Active = !obj.Active;
}
public List<ProductStatus> getProducts()
{
return new List<ProductStatus>
{
new ProductStatus { Id="P13", Name = "Product X", Active = false },
new ProductStatus { Id="P17", Name = "Product Y", Active = false },
new ProductStatus { Id="P21", Name = "Product Z", Active = true },
};
}
}
public class ProductStatus :INotifyPropertyChanged
{
public string Id { get; set; }
public string Name { get; set; }
private bool active;
public bool Active {
set
{
active = value;
OnPropertyChanged("Active");
}
get
{
return active;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
in your xaml (set x:Name to your contentpage,then binding its command):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="root"
x:Class="EntryCa.MainPage">
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout BindableLayout.ItemsSource="{Binding ProductList}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal" Spacing="1" Padding="20">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand,Source ={x:Reference root} }"
CommandParameter="{Binding .}"></TapGestureRecognizer>
</StackLayout.GestureRecognizers>
<StackLayout.Style>
<Style TargetType="StackLayout">
<Style.Triggers>
<DataTrigger Binding="{Binding Active}" Value="true" TargetType="StackLayout">
<Setter Property="BackgroundColor" Value="#f0f0f0"/>
</DataTrigger>
<DataTrigger Binding="{Binding Active}" Value="false" TargetType="StackLayout">
<Setter Property="BackgroundColor" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackLayout.Style>
<Label Text="{Binding Name}"/>
<Label HorizontalOptions="EndAndExpand">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding Active}" Value="true" TargetType="Label">
<Setter Property="TextColor" Value="Green"/>
<Setter Property="Text" Value="A"/>
</DataTrigger>
<DataTrigger Binding="{Binding Active}" Value="false" TargetType="Label">
<Setter Property="TextColor" Value="Red"/>
<Setter Property="Text" Value="I"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<Button Text="Save"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
the effect like:

Related

WPF - MVVM - Combobox UserControl + ComboboxItem CustomControl

I am struggling to create a binding from a View + ViewModel to a Custom ComboboxItem. I am not sure it should work, I already had implemented complex UserControls and had to deal with setting the DataContext properly to make it work with MVVM, but this specific scenario doesn't work at all.
What I am trying to do:
Create a user control based on a Combobox. It will have Combobox behavior but it is customized. The combobox collapsed will show only a button without the Path, and when expanded (showing the dropdown list of combobox items), it will show more buttons customized.
This UserControl must work with MVVM (which is not working right now). If i set the ComboboxItem's content on the View hard coded they show correctly, but if I try to do a binding with viewmodel, it fails:
System.Windows.Data Error: 40 : BindingExpression path error: 'MessageName' property not found on 'object' ''MultiButtonControl' (Name='')'. BindingExpression:Path=MessageName; DataItem='MultiButtonControl' (Name=''); target element is 'TestComboBoxItem' (Name=''); target property is 'Content' (type 'Object')
I created :
A usercontrol MultiButtonControl containing a grid and a combobox with a specific style and a List called Children in the code behind (dependency property). I create a binding from the combobox on xaml with the Children property.
A custom control TestComboBoxItem.cs which extends from ComboBoxItem. And I created on Generic.xaml a style for that type.
DataContext:
I set the DataContext of the UserControl "MultiButtonControl.xaml" on the parent element -> Grid element as:
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
Currently working:
Right now the layout works fine. I can use my UserControl in the View, add the Children too. But the problem is the binding. I believe the problem is the DataContext. Because when I try to bind a property from ComboboxItem (TestComboBoxItem custom control) it fails, and if I set it hardcoded it works.
Let me give you the code:
View.xaml
<Window x:Class="Test2Manager.message.ModalMessageInsert"
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:Test2Manager.message"
xmlns:controls="clr-namespace:CSIncludes.controls;assembly=CSIncludes"
xmlns:wpf="clr-namespace:CSIncludes.wpf;assembly=CSIncludes"
mc:Ignorable="d"
WindowStyle="None"
AllowsTransparency="True"
WindowStartupLocation="CenterOwner"
WindowState="Maximized"
Background="#33000000"
Title="ModelMessageInsert"
Name="ModalWindow">
<Window.Resources>
<ResourceDictionary Source="/CSIncludes;component/Themes/Generic.xaml"/>
</Window.Resources>
<Grid Width="600" Height="400" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Gray">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"></TextBlock>
<Grid Grid.Row="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">Name:</TextBlock>
<TextBox Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Text="{Binding MessageName, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Effect>
<DropShadowEffect Color="Yellow" Direction="270" ShadowDepth="2" Opacity="1" BlurRadius="2"></DropShadowEffect>
</TextBox.Effect>
</TextBox>
<!--<controls:AudioControl Grid.Row="2" Grid.Column="2" RecordingMode="False" ReproduceAudioPath="C:\Fabio\Musicas\05 - On The Turning Away.mp3"></controls:AudioControl>-->
<controls:MultiButtonControl Grid.Row="2" Grid.Column="2" Width="170" ParentButtonText="{Binding MessageName}" ParentButtonImage="/CSIncludes;component/images/audio_play.png">
<controls:MultiButtonControl.Children>
<!--Command2="{Binding TestCommand}"-->
<wpf:TestComboBoxItem Content="{Binding MessageName}"></wpf:TestComboBoxItem>
<wpf:TestComboBoxItem Content="Fabio 2"></wpf:TestComboBoxItem>
<wpf:TestComboBoxItem>Fabio 3</wpf:TestComboBoxItem>
</controls:MultiButtonControl.Children>
</controls:MultiButtonControl>
<!--<ComboBox Style="{StaticResource CustomCombobox}">
<ComboBoxItem>Item1</ComboBoxItem>
<ComboBoxItem>Item2</ComboBoxItem>
<ComboBoxItem>Item3</ComboBoxItem>
<ComboBoxItem>Item4</ComboBoxItem>
<ComboBoxItem>Item5</ComboBoxItem>
</ComboBox>-->
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center" Margin="5">
<Button Width="65" Command="{Binding SaveCommand}" CommandParameter="{Binding ElementName=ModalWindow}">Save</Button>
<Button Width="65" Command="{Binding CancelCommand}" CommandParameter="{Binding ElementName=ModalWindow}" Margin="5,0,0,0">Cancel</Button>
</StackPanel>
</Grid>
View - code-behind setting DataContext to ViewModel
using Arbeit.wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Test2Manager.message
{
/// <summary>
/// Interaction logic for ModelMessageInsert.xaml
/// </summary>
public partial class ModalMessageInsert : Window
{
ViewModelModalMessageInsert vm;
public ModalMessageInsert(Test2Entities Context, Action UpdateList)
{
InitializeComponent();
vm = new ViewModelModalMessageInsert(Context, UpdateList);
DataContext = vm;
}
}
}
ViewModel
using CSIncludes.wpf;
using Test2EF;
using Test2Manager.database;
using Test2Manager.manager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Test2Manager.message
{
class ViewModelModalMessageInsert
{
private Test2Entities Context;
private MessageDAL MessageDAL;
private Action UpdateList;
private ModelMessage Model;
public ViewModelModalMessageInsert(Test2Entities Context, Action UpdateList)
{
this.Context = Context;
this.UpdateList = UpdateList;
MessageDAL = new MessageDAL(Context);
Model = new ModelMessage();
Model.ClientId = LoggedManager.ClientId;
MessageName = "Teste";
}
public string MessageName
{
get { return Model.Name; }
set
{
Model.Name = value;
}
}
}
}
MultiButtonControl.xaml
<UserControl x:Class="CSIncludes.controls.MultiButtonControl"
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:CSIncludes.controls"
xmlns:wpf="clr-namespace:CSIncludes.wpf"
mc:Ignorable="d"
d:DesignHeight="55" d:DesignWidth="300">
<UserControl.Resources>
<!--<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
CornerRadius="0"
Background="#FF3F3F3F"
BorderBrush="#FF97A0A5"
BorderThickness="1" />
</Grid>
</ControlTemplate>-->
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="CustomCombobox" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="30"/>
<Setter Property="Foreground" Value="White"/>
<!--<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></Setter>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!--Template="{StaticResource ComboBoxToggleButton}"-->
<ToggleButton
VerticalAlignment="Center"
Height="{Binding ParentButtonHeight}"
Name="ToggleButton"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#5F1E78"></Setter>
<Setter Property="BorderBrush" Value="#5F1E78"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border BorderBrush="#FF97A0A5" BorderThickness="1" HorizontalAlignment="Center" Width="{TemplateBinding ActualWidth}">
<Grid Background="{TemplateBinding Background}" HorizontalAlignment="Center" Width="{TemplateBinding ActualWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Margin="0,0,8,0" Foreground="White" Name="Text" Text="{Binding ParentButtonText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image Grid.Column="2" Name="Image" Width="16" Height="16" Source="{Binding ParentButtonImage}" />
</Grid>
<!--<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions>
<TextBlock Margin="10" Foreground="White" Grid.Column="0" Name="Text" Text="{Binding ParentButtonText}" HorizontalAlignment="Center" />
<Image Grid.Column="1" Name="Image" Source="{Binding ParentButtonImage}" />
</Grid>-->
</Border>
<!--<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Text" Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<!--<ToggleButton Content="aaa"
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>-->
<TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Height="{Binding ParentButtonHeight}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left"
VerticalAlignment="Center" Focusable="True" Background="White" Foreground="Black" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup Name="Popup" Placement="Top" PlacementTarget="{Binding ElementName=PART_EditableTextBox}" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" Width="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}" Height="{TemplateBinding ActualHeight}">
<Border x:Name="DropDownBorder" Background="White" BorderThickness="1" BorderBrush="#888888"/>
<ScrollViewer Margin="0,0,0,0" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="0"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,0,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<!--<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>-->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<ComboBox Style="{StaticResource CustomCombobox}" Focusable="False" x:Name="ComboBox" ItemsSource="{Binding Children}">
</ComboBox>
</Grid>
MultiButtonControl.xaml.cs
using CSIncludes.wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CSIncludes.controls
{
/// <summary>
/// Interaction logic for MultiButtonControl.xaml
/// </summary>
public partial class MultiButtonControl : UserControl
{
public MultiButtonControl()
{
InitializeComponent();
this.Loaded += UserControl_Loaded;
Children = new List<TestComboBoxItem>();
}
public static readonly DependencyProperty ParentButtonTextProperty = DependencyProperty.Register(
"ParentButtonText",
typeof(string),
typeof(MultiButtonControl));
public static readonly DependencyProperty ParentButtonImageProperty = DependencyProperty.Register(
"ParentButtonImage",
typeof(ImageSource),
typeof(AudioControl),
new UIPropertyMetadata(null));
public static readonly DependencyProperty ParentButtonHeightProperty = DependencyProperty.Register(
"ParentButtonHeight",
typeof(double),
typeof(MultiButtonControl));
public static readonly DependencyProperty ChildButtonHeightProperty = DependencyProperty.Register(
"ChildButtonHeight",
typeof(double),
typeof(MultiButtonControl));
public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register(
"Children",
typeof(List<TestComboBoxItem>),
typeof(MultiButtonControl));
public string ParentButtonText
{
get { return (string)GetValue(ParentButtonTextProperty); }
set { SetValue(ParentButtonTextProperty, value); }
}
public ImageSource ParentButtonImage
{
get { return (ImageSource)GetValue(ParentButtonImageProperty); }
set { SetValue(ParentButtonImageProperty, value); }
}
public double ParentButtonHeight
{
get { return (double)GetValue(ParentButtonHeightProperty); }
set { SetValue(ParentButtonHeightProperty, value); }
}
public double ChildButtonHeight
{
get { return (double)GetValue(ChildButtonHeightProperty); }
set { SetValue(ChildButtonHeightProperty, value); }
}
public List<TestComboBoxItem> Children
{
get { return (List<TestComboBoxItem>)GetValue(ChildrenProperty); }
set { SetValue(ChildrenProperty, value); }
}
void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (ParentButtonHeight == 0)
ParentButtonHeight = 35;
if (ChildButtonHeight == 0)
ChildButtonHeight = 25;
ComboBox.MaxDropDownHeight = ComboBox.Items.Count * ChildButtonHeight;
}
}
}
Generic.xaml
<Style x:Key="{x:Type wpf:TestComboBoxItem}" TargetType="{x:Type wpf:TestComboBoxItem}">
<Setter Property="ComboBoxItem.SnapsToDevicePixels" Value="true"/>
<Setter Property="ComboBoxItem.Foreground" Value="Black"/>
<Setter Property="ComboBoxItem.OverridesDefaultStyle" Value="true"/>
<Setter Property="ComboBoxItem.Height" Value="{Binding ChildButtonHeight}"/>
<Setter Property="ComboBoxItem.VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TestComboBoxItem}">
<Border Name="Border" Padding="5" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="#7E59F2"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TestComboBoxItem.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CSIncludes.wpf
{
public class TestComboBoxItem : ComboBoxItem
{
static TestComboBoxItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TestComboBoxItem), new FrameworkPropertyMetadata(typeof(ComboBoxItem)));
}
public static DependencyProperty CommandParameter2Property =
DependencyProperty.Register("CommandParameter2", typeof(object), typeof(TestComboBoxItem));
public static DependencyProperty Command2Property =
DependencyProperty.Register("Command2", typeof(ICommand), typeof(TestComboBoxItem));
public static DependencyProperty ItemTextProperty =
DependencyProperty.Register("ItemText", typeof(string), typeof(TestComboBoxItem));
public ICommand Command2
{
get { return (ICommand)GetValue(Command2Property); }
set { SetValue(Command2Property, value); }
}
public object CommandParameter2
{
get { return GetValue(CommandParameter2Property); }
set { SetValue(CommandParameter2Property, value); }
}
public string ItemText
{
get { return (string)GetValue(ItemTextProperty); }
set { SetValue(ItemTextProperty, value); }
}
}
}
Do you have any clue on how to fix that TestComboBoxItem bindings ? How would you do to make it work? After binding the Content, I will need to create bindings with ICommand too.

xaml usercontrol multidatatrigger from parent control and itself (usercontrol)

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>

DataGrid with binding for rows and just some columns?

How do I get a datagrid like this with templates for rows as well as for repeatable cells?
DataGrid is bound to a property called Blocks, which is a BindingList of Block type, defined in the view model:
<DataGrid x:Name="dgBlocks" DockPanel.Dock="Left"
Margin="20,10,10,20" AutoGenerateColumns="False"
ItemsSource="{Binding Blocks, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
CanUserAddRows="False"
SelectionUnit="Cell" SelectionMode="Single"
EnableColumnVirtualization="False"
EnableRowVirtualization="False"
SelectedIndex="-1" MinRowHeight="10"
LoadingRow="dg_LoadingRow"
SelectionChanged="dgBlocks_SelectionChanged"
>
<DataGrid.Resources>
<helper:BindingProxy x:Key="proxy" Data="{Binding}" />
<!--todo: change to symbol’s ‘ReadOnly’ property instead of block’s ‘Permission’ -->
<Style x:Key="CellPermissionStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding Permission, Converter={StaticResource BlockPermissionToBrushConverter}}" />
<Setter Property="IsEnabled" Value="{Binding Permission, Converter={StaticResource BlockPermissionToBoolConverter}}" />
</Style>
</DataGrid.Resources>
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Name="txtBlockRowHeader"
Text="{Binding Path=Header,
RelativeSource={RelativeSource AncestorType=DataGridRow}}">
</TextBlock>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.Columns>
<DataGridTemplateColumn>
<!-- Selection checkboxes -->
<DataGridTemplateColumn.Header>
<StackPanel>
<CheckBox HorizontalAlignment="Center" IsThreeState="True" ToolTip="Select All" Margin="10,0,0,0">
<CheckBox.IsChecked>
<Binding Path="DataContext.SelectAll"
RelativeSource="{RelativeSource AncestorType={x:Type UserControl}}"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"
/>
</CheckBox.IsChecked>
</CheckBox>
</StackPanel>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="5,0,0,0" IsChecked="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="50">
<DataGridTemplateColumn.Header>
<TextBlock Text="Repeat" HorizontalAlignment="Center" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<telerik:RadNumericUpDown Name="nudRepeatBlock" ValueFormat="Numeric" Width="40"
ToolTip="Repeat block number of times" IsInteger="True" IsEditable="True"
Minimum="1" Maximum="100" UpdateValueEvent="PropertyChanged"
Value="{Binding Repeat, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="40" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" CanUserReorder="False" CanUserResize="False"
Visibility="{Binding Data.Encoding, Converter={StaticResource Encoding8b10bToCollapsedConverter}, Source={StaticResource proxy}}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</DataGridTextColumn.HeaderStyle>
</DataGridTextColumn>
<!--todo: change… -->
<DataGridTextColumn Header="[0]" Binding="{Binding .Symbols[0].SymbolText, UpdateSourceTrigger=LostFocus}" CellStyle="{StaticResource CellPermissionStyle}" />
<DataGridTextColumn Header="[1]" Binding="{Binding .Symbols[1].SymbolText, UpdateSourceTrigger=LostFocus}" CellStyle="{StaticResource CellPermissionStyle}" />
<!-- … -->
<DataGridTextColumn Header="[15]" Binding="{Binding .Symbols[15].SymbolText, UpdateSourceTrigger=LostFocus}" CellStyle="{StaticResource CellPermissionStyle}" />
</DataGrid.Columns>
</DataGrid>
Each Block object has a property called Symbols, defined as a BindingList of Symbol type, and some other properties. Symbols get shown in columns with headers [0], 1, etc.
Other block’s properties shown in other columns. E.g. IsSelected for checkbox, Reapeat, Name.
public interface ISymbol
{
int Index { get; set; }
bool ReadOnly { get; set; }
string SymbolText { get; set; }
}
public class BlockBase : ObservableObject, IDataErrorInfo
{
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value == _isSelected)
return;
_isSelected = value;
OnPropertyChanged("IsSelected");
RaiseSelectionChangedEvent();
}
}
public string Name { get; set; }
public virtual BindingList<ISymbol> Symbols { get; set; }
public BlockPermission Permission { get; set; }
public int Repeat { get; set; }
}
public class Symbol : ObservableObject, ISymbol
{
DisplayFormat Format { get; set; }
public int Index { get; set; }
public virtual bool ReadOnly { get; set; }
public virtual string SymbolText
{
get { return (Format == DisplayFormat.Binary) ? _binSymbol : _hexSymbol;
}
set
{
// … validate and set _binSymbol & _hexSymbol values
OnPropertyChanged("SymbolText");
}
}
Now, I need to bind each symbol’s cell 'IsEnabled' property to Symbol's 'ReadOnly' property.
I tried to define this in DataGrid.Resources as CellPermissionStyle, but I don’t know how to access a Symbol on this level.
Also, it would be nice to replace all repeating DataGridTextColumn defenitions for symbols with a template.
Would somebody help me?
My researches just confirmed that unfortunately there is no way to do this with templates or styles applicable to a subset of columns. It is also imposible to send a parameter to a style or template as Mishka suggested :(.
So, I've just ended up with 'copy-paste-modify' cell style for each of my 16 columns like that:
<DataGridTextColumn Header="[0]" Binding="{Binding .Symbols[0].SymbolText, UpdateSourceTrigger=LostFocus}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding .Symbols[0].ReadOnly, Converter={StaticResource SymbolReadOnlyToBrushConverter}}" />
<Setter Property="IsEnabled" Value="{Binding .Symbols[0].ReadOnly, Converter={StaticResource SymbolReadOnlyToEnabledConverter}}" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
...
<DataGridTextColumn Header="[15]" Binding="{Binding .Symbols[15].SymbolText, UpdateSourceTrigger=LostFocus}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding .Symbols[15].ReadOnly, Converter={StaticResource SymbolReadOnlyToBrushConverter}}" />
<Setter Property="IsEnabled" Value="{Binding .Symbols[15].ReadOnly, Converter={StaticResource SymbolReadOnlyToEnabledConverter}}" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
Yes, I know, this XAML looks ugly, but it works for me without a risky redesign of existing code base.
I'd be happy if someone could suggest a better, more elegant way doing this in XAML.

Close UWP flyout with listview item click

I'm trying to close a flyout on a listview item click. The problem is that during runtime, the CallMethodAction can't find the hide method of the flyout menu. How can I fix this?
<Flyout x:Name="UnitFlyout">
<ListView x:Name="ArmyUnitListView" ItemsSource="{Binding Source={StaticResource ArmyUnitCollection}}" SelectionMode="Single" >
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True" HeaderTemplate="{StaticResource ArmyListDataGroupTemplate}" />
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding AddUnitCommand}" CommandParameter="{Binding SelectedItem, ElementName=ArmyUnitListView}" />
<core:CallMethodAction TargetObject="UnitFlyout" MethodName="Hide"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
</Flyout>
I made a demo and reproduced the problem.The reason lies on the ElementName binding to popups.
ElementName bindings do not work within Flyout’s and other popups
Please see this Case.
I found this blog, which offers a workaround to fix this problem. And I've tried it with a demo,which works fine.
In your case, you can copy the FlyoutHelpers (in blog) class to your project; And add IsFlyoutOpen and SendCommand to your ViewModel like below:
public class MainPageViewModel : ViewModelBase
{
public RelayCommand SendCommand { get; set; }// bind this to your xaml
public List<String> MyData { get; set; }
private bool isFlyoutOpen;
public bool IsFlyoutOpen// bind this to your xaml
{
get { return isFlyoutOpen; }
set { this.Set(() => IsFlyoutOpen, ref isFlyoutOpen, value); }
}
public MainPageViewModel()
{
SendCommand = new RelayCommand(() =>
{
// Doing processing...
IsFlyoutOpen = false;
});
MyData = new List<string> { "winffee", "123", "this Data" };//this is sample data
}
}
And Bind the commands and properties to your xaml:
<Flyout x:Name="UnitFlyout"
local:FlyoutHelpers.Parent="{Binding ElementName=myBtn}"
local:FlyoutHelpers.IsOpen="{Binding IsFlyoutOpen,Mode=TwoWay}">
<ListView x:Name="ArmyUnitListView" SelectionMode="Single" ItemsSource="{Binding MyData}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged" SourceObject="{Binding ElementName=ArmyUnitListView}">
<!--<core:InvokeCommandAction Command="{Binding AddUnitCommand}" CommandParameter="{Binding SelectedItem, ElementName=ArmyUnitListView}" />-->
<core:InvokeCommandAction Command="{Binding SendCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
</Flyout>
Here is my entire Demo: FlyoutSample

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)