WPF DataBinding Not Updating Property [duplicate] - c#

This behavior seems incredibly odd to me, and I assume I am doing something wrong to get it. I have a ContentControl that uses a DataTemplete to render an TabControl. I want an image to display when there are no tabs open, and hide when there are. But here is the problem:
<Image Name="image1" Stretch="Uniform" Visibility="Hidden" Source="/Affinity;component/Images/affinity_logo.png">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, ElementName=tabcontrolworkspaces}"
Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
This doesn't work... sort of.
I have tested this on Visiblity and Margin (just to be sure). This trigger will alter the property, unless that property is defined in the Image tags. If it is, the trigger will not update that property. So, if I don't define a visibility for the image, and the trigger hides it, it works. The problem is, the default is Visible and the trigger needs to show it when value=0 and hide it otherwise.
Why won't the trigger override properties that are explicitly defined? Isn't that its purpose?

This is the normal Dependency Property Value Precedence. Setting it on Image is at #3, while in the Style trigger is at a lower precedence of #6.
You can do this instead:
<Image Name="image1" Stretch="Uniform" Source="/Affinity;component/Images/affinity_logo.png">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, ElementName=tabcontrolworkspaces}"
Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>

Set your Visibility in the Style in addition to in the Trigger
I've encountered this weird behavior with DataTriggers many times, where sometimes DataTrigger Setters won't take effect unless the Setter is also defined in the Style.
Won't work
<Image Visibility="Collapsed">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding Something}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Image.Style>
</Image>
Will work
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Something}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Image.Style>
</Image>
Edit: See the accepted answer for an explanation on why this doesn't work. It has to do with the order in which dependency properties are determined, where properties defined in the <Tag> always take precedence over triggered values.

When a trigger is true it changes the value to the desired value. When it is no longer true it returns the value to the previous value. It won't change it to a value it wans't.
This means that if the original value is visible, and you change it to visible, when the trigger is no longer active, the value will revert back to visible.

Related

Better Binding for DataTrigger

I have a TextBlock and I want to set the property Visibility to Collapsed when the TextBlock has no text. I wonder me, for sure there should be a better way to check if the Lenght of the property Text is equal than 0.
<TextBlock Name="TextBlockHeader" Foreground="White" FontSize="18" FontWeight="Bold" Text="{Binding Header}" Margin="0,0,0,25">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TextBlockHeader, Path=Text.Length}" Value="0">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Here I have to define a name for the TextBlock and I can reference it in the Datatrigger Binding="{Binding ElementName=TextBlockHeader, Path=Text.Length}"
But how can I achieve the same without having to define a name for the TextBlock?
You would usually use Triggers instead of DataTriggers, and compare the Text property to either null or an empty string.
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
As the TextBlock class seems to coerce the Text property value to be non-null, it may be sufficient to have only the second Trigger for an empty string.

How I can dynamically change a WPF control into another?

I need to do a component where checkboxes change into radiobuttons when certain property changes. I have no idea how to do that kind of change in xaml. The checkbox is in a datatemplate as shown below. Now I just need some kind of logic to change it into a radiobutton.
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected.Value, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DataTemplate>
Especially when there are a lot of instances, the solution of having both controls within the DataTemplate and changing visibility might not be ideal considering performance and memory usage. In this case, a DataTemplateSelector might do the trick - see this tutorial
you can put both CheckBox and RadioButton in one container (StackPanel for example) in DataTemplate and just collapse one of them according to your condition using DataTrigger.
i hope it'll help you. sorry for my pure english.
<DataTemplate>
<StackPanel>
<CheckBox Content="CheckBoxHeader">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Condition}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<RadioButton Content="RadioButtonHeader">
<RadioButton.Style>
<Style TargetType="RadioButton">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Condition}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>
</StackPanel>
</DataTemplate>

Trigger doesn't work on ToggleButton.IsChecked property

Here is a simple XAML with trigger that should change ToggleButton content when it is checked. But for some reason it doesn't work. I have a silly feeling that I missed something extra small. Appreciate your help
<ToggleButton Content="<">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value=">" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
You must move Content="<" from ToggleButton to setter of Style.
Example:
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="<" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value=">" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
Because local value has higher precedence order over Style setters and triggers:
Property system coercion.
Active animations, or animations with a Hold behavior.
3. Local value.
TemplatedParent template properties.
Implicit style.
6. Style triggers.
Template triggers.
8. Style setters.
...
For more information, please see:
MSDN: Dependency Property Value Precedence
You are overriding the Content set by the Trigger by setting the Content attribute at the control level. You want to set it using a Setter within the Style instead:
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="<" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value=">" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>

Dynamically changing content of ContentControl via trigger

I need to flip between two views dynamically based on a boolean flag in my ViewModel.
I thought it would be as simple as:
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{StaticResource View1}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsShowingView2}">
<Setter Property="Content" Value="{StaticResource View2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
However, View2 never gets displayed, even if IsShowingView2 is always true.
Any ideas anyone? All the examples I can find seem to be altering the ContentTemplate instead, but I have no need to do that. I just want different content.
You're not actually setting a value for the DataTrigger
<DataTrigger Binding="{Binding IsShowingView2}" Value="True">
<Setter Property="Content" Value="{StaticResource View2}" />
</DataTrigger>
Also check for binding errors in the output window.

DataTemplate check if Binding exist

I had make an Button Style as DataTemplate in a ResourceDictionary. Here a small part:
<Style TargetType="{x:Type Button}">
<Setter Property="Focusable" Value="False"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border">
...
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" TargetName="border" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In this Template a have a binding to a Propertie IsSelected. This Propertie is in one situation there and in a other not.
Is it possible to Check in Xaml if the binding path exist, then use it in other case forget it?
Now i had BindingExpression in the Debug output and i want to eliminate this.
The more pertinent question is: why do you have a DataTrigger in your ControlTemplate? This creates a dependency between the control and its data context, which is why you're experiencing this issue when your data context does not match the control template's expectations.
Are you certain you cannot use a more suitable mechanism? For example, could you instead use a style for those buttons where IsSelected should affect the Background?
<Style x:Key="SpecialButtonStyle" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<Button DataContext="relevant data context" Style="{StaticResource SpecialButtonStyle}"/>
Or, even better, could you define a data template for the specific data class you have that has the IsSelected property? This data template could automatically use the correct Button style.

Categories

Resources