WPF Bind to DependenyProperty - wpf

I have a DependenyProperty:
public static readonly DependencyProperty SelectedBackgroundProperty = DependencyProperty.Register("SelectedBackground", typeof(Brush), typeof(CustomizedMetroTabItem), new UIPropertyMetadata(null));
public Brush SelectedBackground
{
get { return (Brush)GetValue(SelectedBackgroundProperty); }
set { SetValue(SelectedBackgroundProperty, value); }
}
I want to bind my Background property to the value of the SelectedBackgroud DependenyProperty
so I did the following:
<Setter Property="Background" Value="{Binding SelectedBackground, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
I also tried
<Setter Property="Background" Value="{TemplateBinding SelectedBackground}"/>
But it doesnt work, why is that? (Ofcourse I set the SelectedBackground from the XAML)

Since a setter isn't part of the template, TemplatedParent won't work.
Use RelativeSource={RelativeSource Mode=Self} to access the current object from a style setter.

Related

How to use depenceny property in the style of tooltip to set the text?

In a button, I am using a dependency property to pass information from the view model to the style of the button, so I can set the color of the color according to some conditions.
The code for the button is this:
The style in my xaml file:
<Style x:Key="BotonesColorEstadosTemplate" TargetType="{x:Type Button}">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="dp:BotonesEstadosAttachedProperty.CodigoEstado" Value="0"/>
<Condition Property="dp:BotonesEstadosAttachedProperty.AccionHabilitada" Value="true"/>
</MultiTrigger.Conditions>
</MultiTrigger>
<ContentTemplate>
</ContentTemplate>
</Style>
The dependency properties:
public static class BotonesEstadosAttachedProperty
{
public static readonly DependencyProperty CodigoEstadoProperty =
DependencyProperty.RegisterAttached(
"CodigoEstado",
typeof(short),
typeof(BotonesEstadosAttachedProperty));
public static short GetCodigoEstado(DependencyObject obj)
{
return (short)obj.GetValue(CodigoEstadoProperty);
}
public static void SetCodigoEstado(DependencyObject obj, short value)
{
obj.SetValue(CodigoEstadoProperty, value);
}
public static readonly DependencyProperty AccionHabilitadaProperty =
DependencyProperty.RegisterAttached(
"AccionHabilitada",
typeof(bool),
typeof(BotonesEstadosAttachedProperty));
public static bool GetAccionhabilitada(DependencyObject obj)
{
return (bool)obj.GetValue(AccionHabilitadaProperty);
}
public static void SetAccionHabilitada(DependencyObject obj, bool value)
{
obj.SetValue(AccionHabilitadaProperty, value);
}
}
How to use in the button:
<Button Name="btnAlmacenesActualizar" Content="..." Height="23" Margin="3,0,0,0" Width="23"
ap:BotonesEstadosDependencyProperty.CodigoEstado="{Binding CodigoEstadoActualizarAlmacenes}"
ap:BotonesEstadosDependencyProperty.AccionHabilitada="{Binding EsAccionActualizarAlmacenesHabilitada}">
With this, I can use a property of my view model and pass to my style, that use the information in the trigger to set the color of the button.
Now, I would like to have a style for the tooltip, to have the default configuration for all the tooltips, and I would like to can pass the text of the tooltip at first, but later I would like to pass another variables. By the moment, to test, I would like to try with the text.
I have this style:
<Style TargetType="ToolTip" x:Key="ToolTipDefaultStyle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="dp:ToolTipAttachedProperty.Texto"/>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
My StackPanel with the tooltip:
<StackPanel Name="spTiposIva" Orientation="Vertical" Margin="5,0,0,0"
ap:ToolTipDependencyProperty.Texto="{Binding TiposIvaTooltip}">
<StackPanel.ToolTip>
<ToolTip Style="{StaticResource ToolTipDefaultStyle}"/>
</StackPanel.ToolTip>
</StackPanel>
But in this case the text that is shown is "dp:ToolTipAttachedProperty.Texto". So I would like if it is possible to do the same than with the button, use a dependency propety to pass information from view model to the style.
Thanks.
You are currently not binding the attached property, you are assigning a string as Value. For binding to attached properties, you need to use the binding markup extension and parentheses, e.g.:
<Setter Property="Text" Value="{Binding (dp:ToolTipAttachedProperty.Texto)"/>
However, in your case you need to refer to the parent control of ToolTip, which your property is attached to. Normally, you would do this with a RelativeSource binding and AncestorType, but this does not work here, because ToolTip is not within the same visual tree as the parent control.
Instead, you can access the control via the PlacementTarget property on the parent ToolTip.
<Setter Property="Text" Value="{Binding PlacementTarget.(dp:ToolTipAttachedProperty.Texto), RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
Please also check your XAML for typos. The attached properties type does not match on the StackPanel and in the ToolTip style: ToolTipDependencyProperty or ToolTipAttachedProperty?

User control parameter in wpf mvvm

I want to create a user control with a bool parameter that defines a dynamic behavior, using MVVM pattern, so I can use the user control in another view that way :
<local:MyUserControl BoolParam={Binding aBoolBinding} />
About the coding of the user control, the xaml should use the value of BoolParam to do something like this :
...
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Trigger>
<DataTrigger Binding="{referenceToBoolParam}" Value="False" >
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
...
Am I supposed to define a property
public bool BoolParam { get; set; }
in the code-behind of the user control, and to code the logic associated to this parameter in the ViewModel of the user control ?
Actually I am a bit confused about it, what is the good practice ?
BoolParam should be a dependency property for you to be able to bind something to it. You define this in the code-behind of MyUserControl:
public bool BoolParam
{
get { return (bool)this.GetValue(BoolParamProperty); }
set { this.SetValue(BoolParamProperty, value); }
}
public static readonly DependencyProperty BoolParamProperty = DependencyProperty.Register(
"BoolParam", typeof(bool), typeof(MyUserControl), new PropertyMetadata(false));
You could then set the DataContext of the UserControl to an instance of a view model that contains a public bool property called aBoolBinding and bind to this one as usual.
View Model:
private bool _b;
public bool aBoolBinding
{
get
{
return _b;
}
set
{
_b = value;
NotifyPropertyChanged();
}
}
View:
<local:MyUserControl BoolParam="{Binding aBoolBinding}" />
This is how data binding works. A target property in the views is bound to a source property of a view model.
Edit:
This binds to the BoolParam property of the UserControl from a TextBox style defined in the UserControl:
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding BoolParam, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="False" >
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

DataContext not binding in Style.Trigger

So I have some code similar to the following: (Forgive any typos-- I tried to simplify in the SO editor for the post)
<my:CustomContentControl>
<my:CustomContentControl.Style>
<Style TargetType="{x:Type my:CustomContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentView}" Value="MyCustomView">
<Setter Property="Content">
<Setter.Value>
<my:CustomView DataContext="{Binding DataContextForMyCustomView"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</m:CustomContentControl.Style>
</my:CustomContentControl>
The problem is that whenever the DataTrigger occurs, the setter does set the Content property to my:CustomView, but it does not bind DataContext. If I move the same code outside of the trigger the DataContext binding works just fine.
Any ideas? If this is a limitation of some sorts, is there any work around?
Update:
I received the following error in the output window:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=DataContextForMyCustomView; DataItem=null; target element is 'CustomView' (Name='customView'); target property is 'DataContext' (type 'Object')
The error you posted makes it sound like your custom control is in an object that doesn't have a DataContext, such as a DataGridColumn.Header.
To get around that, you can create a Freezeable object in your .Resources containing the binding you're looking for, then bind your my:CustomView.DataContext to that object
<my:CustomContentControl.Resources>
<local:BindingProxy x:Key="proxy"
Data="{Binding DataContextForMyCustomView, ElementName=MyControl}" />
</my:CustomContentControl.Resources>
...
<my:CustomView DataContext="{Binding Source={StaticResource proxy}}"/>
Here's the code for a sample Freezable object copied from here:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy), new UIPropertyMetadata(null));
}
Also, you really should use ContentTemplate instead of Content to avoid an exception if more than one object applies that style :)
I solved a similar problem by putting the UserControl into the resources and then changing the Content with that.
e.g. from my own code (different names, same concept)
<ContentControl Grid.Column="1"
Margin="7,0,7,0">
<ContentControl.Resources>
<mapping:Slide11x4MappingView x:Key="Slide11X4MappingView" DataContext="{Binding MappingViewModel}"/>
<mapping:MicrotubeMappingView x:Key="MicrotubeMappingView" DataContext="{Binding MappingViewModel}"/>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Acquirer.Sorter.TrayType}" Value="{x:Static mapping:TrayType.SLIDES11X4}">
<Setter Property="Content" Value="{StaticResource Slide11X4MappingView}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Acquirer.Sorter.TrayType}" Value="{x:Static mapping:TrayType.VIALS}">
<Setter Property="Content" Value="{StaticResource MicrotubeMappingView}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

UserControl Custom Properties for Triggers

I am writing a custom user control, called MyUserControl. I have many DependecyProperties for it, which I use in the MainWindow where several MyUserControl are defined multiple times. What I would like to know is how can I create custom Properties that the Triggers/Properties for a style would fire?
For example, if I have a custom property BOOL IsGoing and a custom property MyBackgroung(the background of the UserControl), both defined as :
public bool IsGoing
{
get { return (bool)this.GetValue(IsGoingProperty); }
set { this.SetValue(IsGoingProperty, value); }
}
public static readonly DependencyProperty IsGoingProperty = DependencyProperty.RegisterAttached(
"IsGoing", typeof(bool), typeof(MyUserControl), new PropertyMetadata(false));
public Brush MyBackground
{
get { return (Brush)this.GetValue(MyBackgroundProperty); }
set { this.SetValue(MyBackgroundProperty, value); }
}
public static readonly DependencyProperty MyBackgroundProperty = DependencyProperty.Register(
"MyBackground", typeof(Brush), typeof(MyUserControl), new PropertyMetadata(Brushes.Red));
and if I define my UserControl in MainWindow.xaml, how can I access the Triggers and set MyBackground, depending on whether or not the IsGoing property is true/false?
I tried many things, but in essence, I'm trying to achieve something like:
<custom:MyUserControl MyBackground="Green" x:Name="myUC1" Margin="120.433,0,0,65.5" Height="50" Width="250" VerticalAlignment="Bottom" HorizontalAlignment="Left" >
<Style>
<Style.Triggers>
<Trigger Property="IsGoing" Value="True">
<Setter Property="MyBackground" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</custom:MyUserControl>
I hope my explanation is good enough for you to understand. I've been working on this for a couple of days now, and I can't seem to find the solution.
Thanks for the help!!!
Adrian
Your style should just need to be used as UserControl.Style and have the correct TargetType, also default values you intend to change via trigger need to be moved into the style due to precedence:
<custom:MyUserControl.Style>
<Style TargetType="custom:MyUserControl">
<Setter Property="MyBackground" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsGoing" Value="True">
<Setter Property="MyBackground" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</custom:MyUserControl.Style>
Whether this actually does anything depends on how you use the properties in the control definition.

WPF: Binding to a (observable) Dictionary

I'm using this ObservableCollection-Class within my Project: Link
I want to Bind a RibbonMenuButton to a ObservableDictionary<string,bool>:
<r:RibbonMenuButton ItemsSource="{Binding MyDictionary}">
<r:RibbonMenuButton.ItemContainerStyle>
<Style TargetType="{x:Type r:RibbonMenuItem}">
<Setter Property="IsCheckable" Value="true"/>
<Setter Property="Header" Value="{Binding Path=Key}"/>
<Setter Property="IsChecked" Value="{Binding Path=Value}"/>
</style>
</r:RibbonMenuButton.ItemContainerStyle>
</r:RibbonMenuButton>
But I get exceptions because the Value-Properties of the internal IDictionary-KeyValuePairs are readonly. Any Idea how to solve this?
I thought about something like:
<Setter Property="IsChecked" Value="{Binding Source=MyDictionary[{Binding Path=Key}]}"/>
But this won't work 'cause of {Binding} in {Binding}...
This doesn't work, because your dictionary isn't treated as a dictionary but as an IEnumerable<KeyValuePair<string, bool>>. So each RibbonMenuItem is bound to a KeyValuePair<string, bool> with readonly properties Key and Value.
You can do two one things:
1. Use an ObservableCollection<Tuple<string, bool>> instead of the dictionary and bind IsChecked to Item2.
2. Create a little helper class that contains a IsChecked property and change your dictionary to contain that class as the value and bind IsChecked to Value.IsChecked.
I would go with answer two, because the needed changes and possible side effects are smaller.
My answer assumes that you want to have a two way binding on IsChecked. If not, go with the answer of slugster.
WPF binding is two-way by default. Make it one-way and see if that solves your issue.
<r:RibbonMenuButton ItemsSource="{Binding MyDictionary}">
<r:RibbonMenuButton.ItemContainerStyle>
<Style TargetType="{x:Type r:RibbonMenuItem}">
<Setter Property="IsCheckable" Value="true"/>
<Setter Property="Header" Value="{Binding Key, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Value, Mode=OneWay}"/>
</style>
</r:RibbonMenuButton.ItemContainerStyle>
</r:RibbonMenuButton>
Here is a reference for you: MSDN Windows Presentation Foundation Data Binding: Part 1 (specifically check the section Binding Mode close to the bottom of the page)
If You want to bind MenuItems to Dictionary<string, bool> without using a helper class, like the accepted answer suggests, here is the minimal-change solution (no need to add anything else):
define a Click event inside the ItemContainerStyle whose ClickEventHandler will update the dicitonary.
declare a dictionary and initialize it inside the UserControl's / Window's constructor
In code:
MainWindow.xaml:
<MenuItem Header="_My settings" ItemsSource="{Binding MySettings}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="IsCheckable" Value="true"/>
<Setter Property="Header" Value="{Binding Key, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Value, Mode=OneWay}"/>
<!-- this is the main line of code -->
<EventSetter Event="Click" Handler="MySettings_ItemClick"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
// properties...
// Declaration of the dictionary
public Dictionary<string, bool> MySettings{ get; set; }
public MainWindow()
{
InitializeComponent();
// Initialize the dictionary
MySettings = new Dictionary<string, bool>()
{
{ "SettingOne", true}
// Other pairs..
};
}
// other things..
// ClickEvent hanlder
private void MySettings_ItemClick(object sender, RoutedEventArgs e)
{
MenuItem clickedItem = (sender as MenuItem);
MySettings[clickedItem.Header as string] = clickedItem.IsChecked;
}
} // end of MainWindow class
That's it! You're all set!
Credits to slugster and his answer for XAML code for OneWay binding :)
As a general solution to this problem of binding to dictionaries I created an UpdateableKeyValuePair and return that instaed of the usual KeyValuePair. Here is my class:
public class UpdateableKeyValuePair<TKey,TValue>
{
private IDictionary<TKey, TValue> _owner;
private TKey _key;
public UpdateableKeyValuePair(IDictionary<TKey, TValue> Owner, TKey Key_)
{
_owner = Owner;
_key = Key_;
}
public TKey Key
{
get
{
return _key;
}
}
public TValue Value
{
get
{
return _owner[_key];
}
set
{
_owner[_key] = value;
}
}
}

Resources