Is it possible to bind a property to value of a setter? - wpf

Is it possible to bind a property to value of a setter like this
<Setter Property="ToolTip" Value="{Binding updateToolTip}"/>

Yes you can bind provided that updateToolTip is publically \ internally found in the data context of the UI control for which the tooltip setter is targetted.

When you say its not working, do you mean it gives you an error or wrong content? It works for me: (it is the value of the property not the setter though)
XAML
<Button Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="ToolTip" Value="{Binding MySelectedItem}"/>
</Style>
</Button.Style>
</Button>
<ComboBox ItemsSource="{Binding ListOfValues}" SelectedItem="{Binding MySelectedItem}" Grid.Row="1"/>
Code:
public string MySelectedItem
{
get
{
return _selectedValueString;
}
set
{
_selectedValueString = value;
OnPropertyChanged("MySelectedItem");
}
}

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?

Style of a TextBox: Why is 'AcceptsReturn=true' not applied?

I have a custom control in WPF, which consists of a toggle button, a TextBlock and a TextBox. What I basically want to do is to show the TextBox when the toggle button is checked and the TextBlock otherwise. Furthermore I want allow defining to style properties on the control via dependency properties, which are applied to the TextBlock and the TextBox at runtime. The default template looks like this:
<Style TargetType="{x:Type views:EditableLabel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:EditableLabel}">
<Border Background="{TemplateBinding Background}">
<DockPanel Margin="0">
<telerik:RadToggleButton x:Name="PART_Toggle"
DockPanel.Dock="Right"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsInEditMode, Mode=TwoWay}">
<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ToggleImage}" Height="14" />
</telerik:RadToggleButton>
<TextBlock x:Name="PART_TextBlock"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}" >
</TextBlock>
<TextBox x:Name="PART_TextBox"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
</TextBox>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The custom control has two dependency properties for styles, one for the PART_TextBlock and one for PART_TextBox. The styles are assigned in the OnApplyTemplate method of the custom control and in the property change callbacks of the two dependency properties:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBlock = (TextBlock) GetTemplateChild("PART_TextBlock");
_textBox = (TextBox) GetTemplateChild("PART_TextBox");
_toggleButton = (RadToggleButton) GetTemplateChild("PART_Toggle");
ApplyStyles();
UpdateVisibilities();
}
private void ApplyStyles()
{
if (_textBlock != null) _textBlock.Style = TextBlockStyle;
if (_textBox != null) _textBox.Style = TextBoxStyle;
}
(The callbacks are not shown here, as they are trivial, just calling ApplyStyles().
I use the custom control like this:
<views:EditableLabel Text="{Binding SelectedToolbox.Description, Mode=TwoWay}"
CanEdit="{Binding SelectedToolbox.CanEdit}"
ToggleImage="../Resources/Images/edit-26.png">
<views:EditableLabel.TextBlockStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</views:EditableLabel.TextBlockStyle>
<views:EditableLabel.TextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="VerticalScrollBarVisibility" Value="Visible" />
</Style>
</views:EditableLabel.TextBoxStyle>
</views:EditableLabel>
Everything works as expected, except from the AcceptsReturn setter is not applied, which I find very strange. I've debugged ApplyStyles(): The style is assigned correctly and both setters are contained within the style.
TextWrapping and VerticalScrollBarVisibility are both set correctly:
while AcceptsReturn is not:
Any ideas, what might be the issue here?
The screenshot you posted suggests AcceptsReturn has a local value, i.e., a value set by explicitly calling the property setter or SetValue. Do you have any code in EditableLabel which explicitly sets the AcceptsReturn property? If so, the local value you set will take precedence over any style setters. You can avoid this by using SetCurrentValue to change the value while leaving the value source unchanged.
Secondly, rather assigning the style in your code behind, it is generally easier and more reliable to simply bind the style within the template, e.g.:
<TextBox x:Name="PART_TextBox" Style="{TemplateBinding TextBoxStyle}" ... />
You might try this first and see if you get better results.

How to bind DataGridTemplateColumn.Visibility to a property outside of DataGrid.ItemsSource?

I need to bind the Visibility of a DataGridTemplateColumn to a property outside of the DataGrid.ItemsSource,because i need to bind this column in the all the rows to one property inside the ViewModel,but as far as i know you just can bind that to something inside the ItemsSource or you should use ElementStyle and EditingElementStyle
I've Already tried this code:
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.ProjectPostVisibility
, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MvvmCommonControl:DataGrid}}"/>
And i'm Sure my binding is correct because it works fine when i bind the DataGridCell.Visibility like below:
<DataGridTemplateColumn Header="post">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Visibility" Value="{Binding DataContext.ProjectPostVisibility,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MvvmCommonControl:DataGrid}}"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn >
Your binding is correct, but it won't work with DataGridTemplateColumn directly because it's not in the visual tree. So it's not inherting DataContext.
You need to bind the DataGridTemplateColumn from code behind. Here is a demo that shows a way of doing it.
As mentionned in other answers, the column isn't part of the visual/logical tree and doesn't inherit from FrameworkElement meaning it has no DataContext. That's why your binding doesn't work.
However you can add a dummy (collapsed) FrameworkElement at a level where the DataContext is what you're looking for (so taking your example, it'd be at the DataGrid's level), collapse it and use it as the Source of your Binding with the x:Reference markup extension.
Here's an example :
<FrameworkElement x:Name="Proxy" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.ProjectPostVisibility, Source={x:Reference Name=Proxy}}"/>
</DataGrid.Columns>
</DataGrid>
Add this setter in the DataGridTemplateColumn.CellStyle and done:
<Setter Property="Visibility" Value="{Binding DataContext.isVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"/>
If you need more help look at my example below.
I want the Remove button to not be visible at the project level. First you have to make sure you have a isVisible property in your view model:
private System.Windows.Visibility _isVisible;
public System.Windows.Visibility isVisible
{
get { return _isVisible; }
set
{
if (_isVisible != value)
{
_isVisible = value;
OnPropertyChanged("isVisible");
}
}
}
Then:
if (isProj == false)
this.model.isVisible = Visibility.Visible;
else
this.model.isVisible = Visibility.Collapsed;
XAML:
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate >
<DataTemplate >
<Button x:Name="btnRemove" Content="X">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="FontWeight" Value="ExtraBold" />
<Setter Property="FontSize" Value="50" />
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="Red"/>
<Setter Property="Visibility" Value="{Binding DataContext.isVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"/>
</Style>
</DataGridTemplateColumn.CellStyle>

Bind two elements' Visibility to one property

I have two Menu Item elements - "Undelete" and "Delete" who have complementary visibility: when one is shown, the other one is hidden.
In the code of the ViewModel I have a dependency property FilesSelectedCanBeUndeleted defined as below:
private bool _filesSelectedCanBeUndeleted;
public bool FilesSelectedCanBeUndeleted
{
get
{
return _filesSelectedCanBeUndeleted;
}
set
{
_filesSelectedCanBeUndeleted = value;
OnPropertyChanged("FilesSelectedCanBeUndeleted");
}
}
the XAML for the Undelete button looks like below:
<MenuItem Header="Undelete" Command="{Binding UndeleteCommand }"
Visibility="{Binding Path=FilesSelectedCanBeUndeleted,
Converter={StaticResource BoolToVisConverter}}" >
As you can see the Visibility of the Undelete is bind to the FilesSelectedCanBeUndeleted
property ( with the help of a BooleanToVisibilityConveter).
Now my question is, how can I write the XAML to bind the Visibility of the Delete button to the "NOT" value of the FilesSelectedCanBeUndeleted property?
Thanks,
Here is an example of a custom IValueConverter, that allows you to reverse the visibility logic. Basically, one MenuItem will be visible when your view-model property is true, and the other would be collapsed.
So you'd need to define two instances of the converter like so:
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<local:BooleanToVisibilityConverter x:Key="ReversedBooleanToVisibilityConverter" IsReversed="true" />
You can use apply the datatrigger to you menuitem to avoid another property in your viemodel like this -
<MenuItem Header="Delete"
Command="{Binding DeleteCommand }">
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding FilesSelectedCanBeUndeleted}" Value="False">
<Setter Property="Visibility"
Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
Create new property on your ViewModel and just Negate 'FilesSelectedCanBeUndeleted' and then bind to it.
I did something like this a while ago with a simple negation...
private bool _filesSelectedCanBeUndeleted;
public bool FilesSelectedCanBeUndeleted{
get{
return _filesSelectedCanBeUndeleted;
}
set{
_filesSelectedCanBeUndeleted = value;
OnPropertyChanged("FilesSelectedCanBeUndeleted");
// You have also to notify that the second Prop will change
OnPropertyChanged("FilesSelectedCanBeDeleted");
}}
public bool FilesSelectedCanBeDeleted{
get{
return !FilesSelectedCanBeUndeleted;
}
}
Xaml could look like this then ....
<MenuItem Header="Delete"
Command="{Binding DeleteCommand }"
Visibility="{Binding Path=FilesSelectedCanBeDeleted, Converter={StaticResource BoolToVisConverter}}" >

WPF binding based on comparison

There is a relatively simple thing I'm trying to achieve but I'm unsure how to do it. Basically, I have a CLR class as follows:
class SomeClass
{
public SomeEnum Status;
}
public enum SomeEnum { One, Two, Three };
I've got a DataGrid that I'm binding an ObservableCollection<SomeClass> programmatically through the code-behind. In this DataGrid I have a DataGridTemplateColumn containing two buttons, as follows:
<toolkit:DataGridTemplateColumn Header="Actions">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="ActionOne" />
<Button Content="ActionTwo" />
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
What I want to do is bind the IsEnabled property of these buttons to a comparison based on the value of {Binding Path=Status}. For example, in pseudocode:
ActionOne.IsEnabled = BoundValue.Status != SomeEnum.Two
ActionTwo.IsEnabled = BoundValue.Status == SomeEnum.One || BoundValue.Status == SomeEnum.Two
Is there anyway to do this in XAML? The alternative would be just to write a value converter for each button, but since the content and other details of the button may vary, too, I don't want to end up writing like 6 value converters.
Cheers!
Why not expose additional Properties in SomeClass that performs the comparison logic?
ex:
public bool ActionOneEnabled
{
get { return Status != SomeEnum.Two; }
}
Then you can easily bind the Button's IsEnabled to the appropriate Property.
Don't forget to include an OnPropertyChanged("ActionOneEnabled") in your setter for Status - so that when your Status changes your Properties based on Status are re-evaluated.
You could do this using a DataTrigger in conjunction with a Converter like below. However, Bryan's solution has the benefit of not using multiple converters and it looks like that was one of your concerns so his answer might be better for your scenario.
<Button>
....
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Status, Converter={StaticResource yourConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
Another option would be to not use the DataTrigger and add the binding directly in the IsEnabled property:
<Button
IsEnabled="{Binding Path=Status, Converter={StaticResource yourConverter}}"
...
/>

Resources