I want to change the background color of our apps main window when a property changes. We have a business date that can be changed and I want to change the window background when it has changed from expected. I've set up a property to tell this. But can I set a style datatrigger on a window that changes itself? Or would I need to do this in the app.xaml?
I ended up kind of doing what Drew suggested. Except I didn't use a Dependency Property.
<Window.Resources>
<SolidColorBrush x:Key="windowBGBrush" Color="Green"/>
<SolidColorBrush x:Key="windowBGBrushBusinessDateChanged" Color="Red"/>
</Window.Resources>
<Window.Style >
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="{DynamicResource windowBGBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusinessDateChanged}" Value="true">
<Setter Property="Background" Value="{DynamicResource windowBGBrushBusinessDateChanged}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Style>
IsBusinessDateChanged is a property on my Viewmodel that gets set by a service. I'm not sure why this was so hard.
If you're exposing a custom property on the Window just make sure it's defined as a DependencyProperty and then you should be able to use a regular trigger in the style to react to the property. Like so:
<Window.Style>
<Style TargetType="{x:Type MyWindow}">
<Style.Triggers>
<Trigger Property="MyProperty" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Style>
Here's a solution with a converter approach:
XAML:
<Window x:Class="StackOverflowTests.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" x:Name="window1" Width="300"
xmlns:local="clr-namespace:StackOverflowTests">
<Window.Resources>
<local:DateToColorConverter x:Key="DateToColorConverter" />
</Window.Resources>
<Window.Background>
<SolidColorBrush Color="{Binding ElementName=textBoxName, Path=Text, Converter={StaticResource DateToColorConverter}}" />
</Window.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox x:Name="textBoxName" Margin="5"></TextBox>
</Grid>
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace StackOverflowTests
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
public class DateToColorConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime date;
if (DateTime.TryParse(value.ToString(), out date))
{
if (date == DateTime.Today)
return Colors.Green;
else
return Colors.Red;
}
else
{
return Colors.Gold;
}
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
}
Maybe it's better to just bind the background with the property. You need to set the datasource of the window to the object and may need a valueconverter.
Related
In a WPF project, I have a user control (Valve.xaml) that defines a Polygon shape.
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}"/>
</Grid>
I am displaying the Valve user control in a window xaml (FFG.xaml) file, like such:
<Window
<!-- removed other namespaces for brevity -->
xmlns:cl="clr-namespace:FFG.Controls;assembly=PID.Controls">
<Grid>
<cl:Valve x:Name="valve201A"></cl:Valve>
</Grid>
</Window>
I am setting the DataContext of FFG.xaml to class FFG_ViewModel.cs, and it contains an instance of the Valve_Model class. Valve_Model essentially represents the valve that is drawn on the window in FFG.xaml.
public class FFG_ViewModel : ViewModelBase {
public Valve_Model Valve201A { get; set; }
// There are other properties and methods, but I've removed them for brevity also
}
Here is the Valve_Model class:
public class Valve_Model INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private bool _isValveOpen { get; set; }
public bool IsValveOpen {
get {
return _isValveOpen;
}
set {
_isValveOpen = value;
OnPropertyChanged("IsValveOpen");
}
}
#region INotifyPropertyChanged
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null) {
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
}
QUESTION:
What I want is for the Style property in the Valve.xaml to change when the IsValveOpen property changes.
So if the valve is open then it would be:
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}"/>
and when the property is changed to false then I need the style of the polygon to be changed to:
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Closed}"/>
How do I go about implementing this exactly?
You could use an IMultiValueConverter.
First, let's simplify the use case. Basicly you want to swap Styles based on a given state object, which I'll represent by a ToggleButton. The fact that you're wrapping everything in a UserControl also has no influence on the underlying concept.
Demo:
Starting a fresh project
Declaring our Resources
Feeding the Window and the state to the Converter.
MainWindow.xaml
<Window
...
>
<Window.Resources>
<local:ToStyleConverter x:Key="ToStyleConverter"/>
<Style x:Key="Valve_Open" TargetType="{x:Type Polygon}">
<Setter Property="Fill" Value="Red"/>
</Style>
<Style x:Key="Valve_Closed" TargetType="{x:Type Polygon}">
<Setter Property="Fill" Value="Green"/>
</Style>
</Window.Resources>
<DockPanel>
<ToggleButton x:Name="butt" DockPanel.Dock="Bottom">Switch</ToggleButton>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Stretch="Uniform">
<Polygon.Style>
<MultiBinding Converter="{StaticResource ToStyleConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type Window}}"/>
<Binding ElementName="butt" Path="IsChecked"/>
</MultiBinding>
</Polygon.Style>
</Polygon>
</DockPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class ToStyleConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (values[0] is Window)
{
Window win = (Window)values[0];
if ((bool)values[1])
return win.FindResource("Valve_Open");
if (!(bool)values[1])
return win.FindResource("Valve_Closed");
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object values, Type[] targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Changing to any specific use case means:
Pointing the relativesource binding to the Control that holds the Resources (Styles)
Using the second binding to add the state to the Converter (DP/INPC)
Implementing Converter logic
You can (should as much as I know) use a DataTrigger, within a ControlTemplate. Assuming that these two are your Styles:
<Window.Resources>
<Style TargetType="Polygon" x:Key="Valve_Open">
<Setter Property="Fill" Value="Red"/>
</Style>
<Style TargetType="Polygon" x:Key="Valve_Close">
<Setter Property="Fill" Value="Green"/>
</Style>
</Window.Resources>
You should add this style to the resources:
<Style x:Key="changeStyle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20" Style="{StaticResource Valve_Open}" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Valve201A.IsValveOpen}" Value="true">
<Setter TargetName="pValve" Property="Style" Value="{StaticResource Valve_Close}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and use them in your views:
<Control DataContext="{Binding}" Style="{StaticResource changeStyle}" />
Instead of setting the actual Style property to a new value, you could add a DataTrigger to the Style itself that changes the properties of the Polygon based on the value of the IsValveOpen source property.
Valve.xaml:
<Grid>
<Polygon Name="pValve" Points="0,50 0,20 50,50 50,20">
<Polygon.Style>
<Style TargetType="Polygon">
<!-- Copy the setters from the Valve_Closed style here -->
<Setter Property="Fill" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsValveOpen}" Value="True">
<!-- Copy the setters from the Valve_Open style here -->
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Polygon.Style>
</Polygon>
</Grid>
FFG.xaml:
<Grid>
<cl:Valve x:Name="valve201A" DataContext="{Binding Valve201A}" />
</Grid>
I am wondering if it is possible to associate Styles for certain controls with a custom window in WPF.
Here's the scenario - I have created a custom window, and have defined styles for a number of controls that I will use in this window. These are contained in a portable class library.
The catch is that I only want the controls to use the style from my library when they are used in the custom window (there are several different windows in the application).
I understand that I can assign the styles a key, and load them from my portable library in my application's app.xaml using pack syntax, for example:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Custom.Application.Library.Controls;component/Styles/CheckBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
And then add and style the control within my custom window as such:
<CheckBox x:Name="checkBox" Style="{StaticResource SpecialCheckBox}"
But what I would really like to do is define they styles in my class library without a key, as in this:
<Style TargetType="{x:Type CheckBox}">
Instead of this:
<Style x:Key="SpecialCheckBox" TargetType="{x:Type CheckBox}">
So that when this checkbox is used in my custom window it automatically inherits the style. If I define the style like this, and load it into my app.xaml, the problem is obviously that ALL checkboxes will inherit this style, not just checkboxes used in my custom window.
So, what I'm trying to find out is if there is any way to associate a style resource explicitly with a custom window, so that I can define the styles without a key, and have them by default inherit the "Special" style when used in my custom window, but use the WPF defaults in any other windows of the application. Does anyone have experience with this?
For clarity here is the code of my custom window:
XAML:
<!-- Window style -->
<Style TargetType="{x:Type Controls:CCTApplicationWindow}">
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="ResizeMode" Value="CanResizeWithGrip"/>
<Setter Property="MinWidth" Value="500"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Controls:CCTApplicationWindow}">
<Border BorderBrush="#FF999999">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}" Value="Maximized">
<Setter Property="BorderThickness" Value="7"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="29"/>
<RowDefinition />
</Grid.RowDefinitions>
<Controls:CCTApplicationHeader Grid.Row="0"
Margin="0"
Title="{TemplateBinding Title}"
DragMoveCommand="{TemplateBinding DragMoveCommand}"
MaximizeCommand="{TemplateBinding MaximizeCommand}"
MinimizeCommand="{TemplateBinding MinimizeCommand}"
CloseCommand="{TemplateBinding CloseCommand}"/>
<Grid Background="White" Grid.Row="1" Margin="0">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Grid>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
CS:
public partial class CCTApplicationWindow : Window
{
public static readonly DependencyProperty MaximizeCommandProperty = DependencyProperty.Register("MaximizeCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public static readonly DependencyProperty MinimizeCommandProperty = DependencyProperty.Register("MinimizeCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public static readonly DependencyProperty DragMoveCommandProperty = DependencyProperty.Register("DragMoveCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
public CCTApplicationWindow()
{
MaximizeCommand = new DelegateCommand(MaximizeExecute);
MinimizeCommand = new DelegateCommand(MinimizeExecute);
CloseCommand = new DelegateCommand(CloseExecute);
DragMoveCommand = new DelegateCommand(DragMoveExecute);
}
static CCTApplicationWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CCTApplicationWindow), new FrameworkPropertyMetadata(typeof(CCTApplicationWindow)));
}
public DelegateCommand MaximizeCommand
{
get
{
return (DelegateCommand)GetValue(MaximizeCommandProperty);
}
set
{
SetValue(MaximizeCommandProperty, value);
}
}
public DelegateCommand MinimizeCommand
{
get
{
return (DelegateCommand)GetValue(MinimizeCommandProperty);
}
set
{
SetValue(MinimizeCommandProperty, value);
}
}
public DelegateCommand CloseCommand
{
get
{
return (DelegateCommand)GetValue(CloseCommandProperty);
}
set
{
SetValue(CloseCommandProperty, value);
}
}
public DelegateCommand DragMoveCommand
{
get
{
return (DelegateCommand)GetValue(DragMoveCommandProperty);
}
set
{
SetValue(DragMoveCommandProperty, value);
}
}
private void MaximizeExecute(object obj)
{
if (this.WindowState != WindowState.Maximized)
{
this.WindowState = WindowState.Maximized;
}
else
{
SystemCommands.RestoreWindow(this);
}
}
private void MinimizeExecute(object obj)
{
SystemCommands.MinimizeWindow(this);
}
private void CloseExecute(object obj)
{
SystemCommands.CloseWindow(this);
}
private void DragMoveExecute(object obj)
{
DragMove();
}
}
Yes, you can do this, but you shouldn't! You've tagged this question as MVVM and yet your architecture design breaks MVVM entirely. The whole point of MVVM is that view logic is contained within the view model layer; your view models are the ones that should be keeping track of the logical hierarchy and they are the ones that should be exposing properties to the views to control their appearance. To put it another way, just because XAML is flexible enough and powerful enough to implement such logic doesn't mean it's the best place to actually do it!
To answer your question though, yes, this can be done with a DataTrigger binding to the parent with ObjectToTypeConverter. Here's an example of setting the TextBlock background to CornflowerBlue, unless its immediate parent is a Grid in which case it should be set to PaleGoldenrod:
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<converters:ObjectToTypeConverter x:Key="ObjectToTypeConverter" />
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="CornflowerBlue" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Parent, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource ObjectToTypeConverter}}" Value="{x:Type Grid}">
<Setter Property="Background" Value="PaleGoldenrod" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<Grid Width="100" Height="32" HorizontalAlignment="Left">
<TextBlock Text="TextBox A" /> <!-- Gets a PaleGoldenrod background -->
</Grid>
<Canvas Width="100" Height="32" HorizontalAlignment="Left">
<TextBlock Text="TextBox B" /> <!-- Gets a CornflowerBlue background -->
</Canvas>
</StackPanel>
And here's the converter code. It's worth pointing out that if you're happy to simply check that a parent of a given type exists somewhere in the hierarchy (as opposed to the immediate parent) then you don't even need this, you can just attempt to bind to RelativeSource with AncestorType set to the relevant parent type.
// based on http://stackoverflow.com/questions/8244658/binding-to-the-object-gettype
[ValueConversion(typeof(object), typeof(Type))]
public class ObjectToTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == null ? null : value.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new InvalidOperationException();
}
}
But again, I implore you, if you really do want to adhere to MVVM then do not do it like this! This is exactly the kind of problem that "proper" MVVM was designed to solve.
Simplest way is to create a separate ResourceDictionary for your Custom window. And use it either using XAML or load it using Code.
I have an editor view that can be used for multiple edited objects. The view model for multiple objects provides a property like Field1Multiple of type bool for each field that needs to be handled. In this case, it's only ComboBox controls for now. Whenever multiple differing values shall be indicated for that field, a certain style should be applied to that control which is defined in App.xaml. That style changes the background of the control to visualise that there is no single value that can be displayed here.
I've tried with this XAML code:
<ComboBox
ItemsSource="{Binding Project.Field1Values}" DisplayMemberPath="DisplayName"
SelectedItem="{Binding Field1}">
<ComboBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Field1Multiple}" Value="true">
<Setter
Property="ComboBox.Style"
Value="{StaticResource MultiValueCombo}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
But it doesn't work because I cannot set the Style property from inside a Style. If I use triggers directly on the control, there may only be EventTriggers, no DataTriggers, the compiler says.
How can I set the control's style based on a binding value? Or, how can I set a certain style for a control if a binding value is true?
(EDIT to full solution)
You can use converter:
public class AnyIsMultipleToStyle : IValueConverter
{
public Style NormalStyle { get; set; }
public Style MultiStyle { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
IList<SampleClass> list= value as IList<SampleClass>;
if (list!=null)
{
if (list.Any(i => i.Multi))
return MultiStyle;
}
}
return NormalStyle;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And in your xaml:(You indicate normal style and multistyle to converter)
<Window.Resources>
<Style x:Key="MultiValueCombo" TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Olive" />
</Style>
<Style x:Key="NormalCombo" TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Red" />
</Style>
<my:AnyIsMultipleToStyle x:Key="AnyIsMultipleToStyle1" MultiStyle="{StaticResource MultiValueCombo}" NormalStyle="{StaticResource NormalCombo }" />
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding Items, ElementName=root}" >
<ComboBox.Style>
<Binding Converter="{StaticResource AnyIsMultipleToStyle1}" Path="Items" ElementName="root" >
</Binding>
</ComboBox.Style>
</ComboBox>
</Grid>
I want to change the color of a WPF control depending on the state of a bool, in this case the state of a checkbox.
This works fine as long as I'm working with StaticResources:
My control
<TextBox Name="WarnStatusBox" TextWrapping="Wrap" Style="{DynamicResource StatusTextBox}" Width="72" Height="50" Background="{Binding ElementName=WarnStatusSource, Path=IsChecked, Converter={StaticResource BoolToWarningConverter}, ConverterParameter={RelativeSource self}}">Status</TextBox>
My converter:
[ValueConversion(typeof(bool), typeof(Brush))]
public class BoolToWarningConverter : IValueConverter
{
public FrameworkElement FrameElem = new FrameworkElement();
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
bool state = (bool)value;
try
{
if (state == true)
return (FrameElem.TryFindResource("WarningColor") as Brush);
else
return (Brushes.Transparent);
}
catch (ResourceReferenceKeyNotFoundException)
{
return new SolidColorBrush(Colors.LightGray);
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
}
The problem is that I have several definitions of the Resource "WarningColor" dependant on setting day mode or night mode. These events does not trig the WarningColor to change.
Is there a way to make the return value dynamic or do I need to rethink my design?
You cannot return something dynamic from a converter, but if your only condition is a bool you can easily replace the whole converter with a Style using Triggers:
e.g.
<Style TargetType="TextBox">
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=WarnStatusSource}" Value="True">
<Setter Property="Background" Value="{DynamicResource WarningColor}" />
</DataTrigger>
</Style.Triggers>
</Style>
If now the resource with that key is changed the background should change as well.
The way to return a dynamic resource reference is pretty simple using a DynamicResourceExtension constructor and supplying it a resource key.
Usage:
return new DynamicResourceExtension(Provider.ForegroundBrush);
Definition of the Provider class should contains the key:
public static ResourceKey ForegroundBrush
{
get
{
return new ComponentResourceKey(typeof(Provider), "ForegroundBrush");
}
}
And the value for the key would be declared in the resource dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:theme="clr-namespace:Settings.Appearance;assembly=AppearanceSettingsProvider">
<Color x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type theme:Provider}, ResourceId=ForegroundColor}">#FF0000FF</Color>
<SolidColorBrush x:Key="{ComponentResourceKey {x:Type theme:Provider}, ForegroundBrush}" Color="{DynamicResource {ComponentResourceKey {x:Type theme:Provider}, ForegroundColor}}" />
</ResourceDictionary>
This way, the converter would dynamically assign a DynamicResource to the bound property depending on the resource key supplied.
I have a style template (below) that does not update when my Tag binding updates. The data itself updates and I receive no binding errors so expect everything is bound correctly it is just that the style doesn't seem to update at all. I have notifypropertychanged events in all the right places afaik so doubt this is the issue.
Thanks
<Style x:Key="CompareTemplate" TargetType="TextBlock">
<!--Setter Property="Foreground" Value="#FF760000" /-->
<Setter Property="Foreground" Value="#FFBCBCBC" />
<Style.Triggers>
<Trigger Value="True" Property="Tag">
<Setter Property="Foreground" Value="#FF007602" />
</Trigger>
<Trigger Value="False" Property="Tag">
<Setter Property="Foreground" Value="#FF760000" />
</Trigger>
</Style.Triggers>
</Style>
And I use this template like so:
<TextBlock Style="{DynamicResource CompareTemplate}" Tag="{Binding UnitComparer.CommsID, FallbackValue=True}" Text="Comms ID:" />
Tag is of type object. I think that your Viewmodel assings a bool to it. WPF is able to convert between strings and objects but seemingly not between bool and object. One solution is to use a IValueConverter to change the bool to a string:
<Window x:Class="BindToTagSpike.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindToTagSpike"
Title="Window1" Height="300" Width="300">
<StackPanel>
<StackPanel.Resources>
<local:ObjectToString x:Key="ObjectToString"/>
<Style x:Key="CompareTemplate" TargetType="TextBlock">
<Style.Triggers>
<Trigger Value="True" Property="Tag">
<Setter Property="Foreground" Value="Red" />
</Trigger>
<Trigger Value="False" Property="Tag">
<Setter Property="Foreground" Value="YellowGreen" />
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock Style="{StaticResource CompareTemplate}"
Name="TaggedTextBlock"
Tag="{Binding TagValue,
Converter={StaticResource ObjectToString}}"/>
<Button Click="Button_Click">Change Style</Button>
</StackPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Data;
using System.ComponentModel;
namespace BindToTagSpike
{
public partial class Window1 : Window, INotifyPropertyChanged
{
public Window1()
{
InitializeComponent();
tagValue = false;
TaggedTextBlock.Text = "Test";
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TagValue=!TagValue;
}
private bool tagValue;
public bool TagValue
{
get { return tagValue; }
set
{
tagValue = value;
if (PropertyChanged != null)
PropertyChanged(this,new PropertyChangedEventArgs("TagValue"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public class ObjectToString : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
Sometime when your template / style is not being applied as expected, WPF might not think the TargetType might matches the control type. Try the code below and see if that helps at all:
<Style x:Key="CompareTemplate" >
<!--Setter Property="Control.Foreground" Value="#FF760000" /-->
<Setter Property="Control.Foreground" Value="#FFBCBCBC" />
<Style.Triggers>
<Trigger Value="True" Property="Control.Tag">
<Setter Property="Control.Foreground" Value="#FF007602" />
</Trigger>
<Trigger Value="False" Property="Control.Tag">
<Setter Property="Control.Foreground" Value="#FF760000" />
</Trigger>
</Style.Triggers>
</Style>
Cheers,
Berryl