I have several buttons which will be sharing a common style and they all will have DataTriggers based on a bound property. I am trying to avoid writing out each datatrigger for each button, so I would like to know if there is a way to dynamically specify the path of a DataTrigger.Binding?
For example:
<DataTrigger
Binding="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},
Path={Binding RelativeSource={RelativeSource Self}, Path=Tag}}" Value="True">
<!--Do Something-->
</DataTrigger>
I know it may not be possible but I figured I would give it a shot. I am storing the property name as the Tag on each button and the property is supposed to be changed on click. So I want to use the value stored in the Tag name as the Path on the data trigger for the default style. Here is an example button that will bold some text:
<Button Content="B" Tag="IsBold" Click="Button_Click" Style="{StaticResource DefaultButton}"/>
And here is the style used:
<Style TargetType="{x:Type Button}" x:Key="DefaultButton">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="20"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Margin" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" Padding="2" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="BorderBrush" Value="Blue"/>
</Trigger>
<DataTrigger Binding="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path={Binding RelativeSource={RelativeSource Self}, Path=Tag}}" Value="True">
</DataTrigger>
</Style.Triggers>
</Style>
Obviously, the style is invalid at the moment because of the DataTrigger. Is there any way to make this sort of thing completely dynamic or will I have to bind each and every button?
I have tried using ValueConverters and, for what I need, I do not believe they will work since they only fire one time at initialization.
Update with some more information:
I have a data object that works as a sort of view. For all intents and purposes let's say this is all it has in it:
using System;
using mshtml;
using System.Windows.Controls;
using System.Windows.Media;
using System.Reflection;
using System.ComponentModel;
namespace Aspen.Visuals
{
public class HtmlFormattingExecutor : INotifyPropertyChanged
{
public HtmlFormattingExecutor(HTMLDocument doc)
{
this.doc = doc;
this.isBold = false;
}
private HTMLDocument doc;
private Boolean isBold;
public Boolean IsBold {
get {return this.isBold;}
set
{
if(this.isBold != value)
{
this.isBold = value;
this.Bold();
this.NotifyPropertyChanged("IsBold");
}
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private void Bold()
{
if (doc != null)
{
doc.execCommand("Bold", false, null);
}
}
}
That object is set as the data context for a window which has the previously shown Button and Style.
If you would like to see the window code I can show you, otherwise here is the code for the button_click event
private void Button_Click(Object sender, RoutedEventArgs e)
{
Button button = e.Source as Button;
if (button != null)
{
this.AlterButtonStyle(button.Tag as String);
button.Style = (Style)(Window.GetWindow(button).TryFindResource("CurrentStyle"));
}
}
private void AlterButtonStyle(String propertyName)
{
if (String.IsNullOrWhiteSpace(propertyName)) return;
PropertyInfo info = this.formatting.GetType().GetProperty(propertyName);
if (info == null || info.PropertyType != typeof(Boolean)) return;
info.SetValue(this.formatting, !(Boolean)info.GetValue(this.formatting, null), null);
}
The "CurrentStyle"
shown in the Button_Click event changes based on True or False in the Boolean property
Hope that helps.
Thanks!
{Binding}
forPath
, because it's not a dependency property. You probably can write code-behind that creates the bindings dynamically based on theTag
property you are using, or it's possible you could bind to a proxy object that can be dynamically updated. Without a good code example, providing specific details on those possibilities isn't possible. – Peter DunihoDependencyProperty
for storing the value your trigger is based on. It would be much easier for future developers to understand what is going on, and it would scale better if you ever wanted more than one dynamic value for your style. – Rachel<Window>
object using theSource
property of the binding, and trying to set thePath
property to another bound value ({Self}.Tag
in this case). So the code in your example would resolve to an attempt to bind toWindow.IsBold
if that kind of thing works. But it doesn't since you can only bind to aDependencyProperty
, and thePath
property of a binding is not a DependencyProperty. I highly recommend the custom DP for this :) – Rachel