DataTrigger doesn't work - c#

I have this in XAML in my style:
<DataTrigger Binding="{Binding Path=ButtonStyle}" Value="CutLeft">
<DataTrigger.Setters>
<Setter Property="CornerRadius" TargetName="border" Value="0 11 11 0" />
<Setter Property="CornerRadius" TargetName="border1" Value="0 10 10 0" />
</DataTrigger.Setters>
</DataTrigger>
And this XAML in my Window where i have the button:
<gui:MyCustomButton ButtonStyle="CutLeft"/>
And in MyCustomButton code:
public enum ButtonStyles {
CutLeft, CutRight, Circular, Normal
}
public partial class MyCustomButton
{
[DefaultValue(ButtonStyles.Normal)]
public ButtonStyles ButtonStyle { get; set; }
}
But it doesn't work! Any tips?

I think there are a couple of problems here.
The first is that you're not raising property change notifications for ButtonStyle. Either change ButtonStyle to a dependency property, or implement INotifyPropertyChanged on MyCustomButton, and raise the PropertyChanged event from the ButtonStyle setter. In this case, the dependency property approach is probably better, because at some point someone is going to want to set ButtonStyle through a Style, and only DPs can be styled.
The second is that your data trigger appears to be looking at the data context, which is probably not the control. (If you look in the Output window, you'll probably be seeing binding errors about not being able to find the 'ButtonStyle' property on some data object.) You can get around this by adding RelativeSource={RelativeSource Self} to your DataTrigger. However, if you change ButtonStyle to a DP as suggested, you should just be able to use a plain old Trigger, which automatically works against the properties of the control being styled:
<Style.Triggers>
<Trigger Property="ButtonStyle" Value="CutLeft">
<Setter Property="CornerRadius" TargetName="border" Value="0 11 11 0" />
</Trigger>
</Style.Triggers>
(Note also that you don't need to specify the Trigger.Setters or DataTrigger.Setters element. The XAML reader will fill that in for you.)

Related

Can I change the color of the text of my property when using mvvm?

I'm new here, so if there's anything I'm doing wrong or need to do differently, I'd love to hear it.
I have a property error message that I "binded" with the xmal. This property returns 2 strings. Either a message that something hasn't been entered or a calculation.
Can I give red with one and black with the other?
Thank you in advance,
this is the method
Property
xmal
You can use a DataTrigger inside a Style for the label.
<Label x:Name="lblFoutmelding" FontWeight="SemiBold" Content="{Binding Foutmelding" Grid.Row="3" Height="30" Margin="9 10 0 0">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Foutmelding}" Value="Gelieve de hoeveeleed in ta geven aub!">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
<Style>
</Label.Style>
</Label>
Note that I have removed the black into a style setter, since setting it directly on the Label would override any style or trigger setters.
Also note that it would be much better to have a separate "Severity" property in your VM and trigger on that, rather than triggering on specific strings.

Generic style with specific trigger values

I have several TextBlocks in my UserControl that I want to change to become Bold and have a Red font when a Property is triggered. The thing is that each of them are changed by different properties. I saw a solution here with Tags, but couldn't quite get it to work for me. I apologize if this is a duplicate, but I couldn't find any solution that solved my problem.
My Style looks like this:
<Style x:Key="TextBlockTrigger" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Tag" Value="true">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
And this is the TextBlock I'm trying to use it on:
<TextBlock Name="TextBlock1" x:Uid="TextBlock1" Text="This text should become bold and Red"
Style="{StaticResource TextBlockTrigger}" Tag="{Binding Path=TriggerProperty, UpdateSourceTrigger=PropertyChanged}"/>
I added a button with a codebehind function that reads the Tag, and a breakpoint shows that Tag is set to true, but the text is still regular black.
TriggerProperty is set by a function call in the View Constructor, after InitializeComponent:
public MyWindow()
{
InitializeComponent();
UpdateServerProperties();
}
public void UpdateServerProperties()
{
//other code
if(ServerValue == true)
{
TriggerProperty = true;
OnPropertyChanged("TriggerProperty");
}
}
It's a bit simplified, but the actual code is overly complicated, but results in the same thing. ServerValue gets a value, and I have confirmed TriggerProperty does get updated to true.
The Tag property has an object type. Xaml has no way of knowing that true represents a bool value, so it just assumes you meant it to be a string. Assuming you are setting Tag to a boolean value, your Trigger is evaluating Equals(true, "true"), so the condition fails.
Try using {x:Static} to point to some constant boolean value. I keep a KnownBoxes class around for this sort of thing:
public static class KnownBoxes
{
public static readonly object True = true;
public static readonly object False = false;
// ... more common values ...
}
These values are easily referenced from Xaml, e.g., {x:Static ns:KnownBoxes.True}.
Alternatively, you could use the element syntax:
<Trigger Property="Tag">
<Trigger.Value>
<s:Boolean xmlns:s="clr-namespace:System;assembly=mscorlib">True</s:Boolean>
</Trigger.Value>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
Or you could set Tag to the string "true", though that may cause some confusion when someone else works on your code :).
If you set TriggerProperty to true dynamically at runtime, the class where this property is defined should implement the INotifyPropertyChanged interface and raise the PropertyChanged event for the trigger to trigger.
If you set the Tag property to a hard-coded value of true, your sample markup should work as expected:
<TextBlock Name="TextBlock1" x:Uid="TextBlock1" Text="This text should become bold and Red"
Style="{StaticResource TextBlockTrigger}" Tag="true"/>
You should also set the Value property to a typed bool value in your Style:
<Style x:Key="TextBlockTrigger" TargetType="{x:Type TextBlock}"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<Style.Triggers>
<Trigger Property="Tag">
<Trigger.Value>
<s:Boolean>True</s:Boolean>
</Trigger.Value>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>

Is it possible to reload XAML properties of a UserControl?

I have some frameworkElements inside XAML, and I define some properties like background, and cursor.
In code behind, I change these properties, and when an event triggers, I want to reload these initial properties defined in XAML. Is this possible or I need to redifine manually in code behind?
Thanks.
A control defined in XAML is essentially defining an instance. Once you have the instance, the object is just like every other object you deal with. Having access to the instance defined in XAML within your code behind is akin to creating a new object in the code behind and then adjusting its properties at run time.
When you want the property value to change; you don't revert your property changes, you simply change them to what you desire.
I would suggest looking into DataTriggers for making temporary changes based on some value. This will change the value of a property while a specific condition is true, and revert it to its original value when the condition is false.
For example, here's a style that will change the cursor to a Wait cursor while loading, and change the background to Red if it is invalid.
<Style TargetType="{x:Type local:MyUserControl}">
<Setter Property="Cursor" Value="Arrow" />
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="True">
<Setter Property="Cursor" Value="Wait" />
</DataTrigger>
<DataTrigger Binding="{Binding IsValid}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
Of course, you'll have to define the IsLoading and IsValid properties behind your UserControl, and set them to true/false at the appropriate times in your code-behind.

Ignore IDataErrorInfo notifications

Is there a collection that I can use in my application where I can ignore the DataErrors? Currently, my BusinessObject implements the IDataErrorInfo interface, but I have a readonly control that I do not want to receive those notifications.
I tried using a DataTemplate with a TextBlock that has the property ValidatesOnDataErrors=False, but this didn't work.
Any ideas?
You could set the control's Validation.ErrorTemplate to null using a style.
<Style TargetType="Control">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Also, to clarify, the "ValidatesOnDataErrors" property should be used on the binding not the control itself.
The other alternative is to wrap your bound objects in some view model/adapter that doesn't implement IDataErrorInfo.

Trigger on ContextMenu.IsOpen in XAML

Here's what I'm trying to do:
<Style x:Key="TreeViewItemStyle">
<Setter Property="TreeViewItem.ContextMenu" Value="{StaticResource ContextMenu}" />
<Style.Triggers>
<Trigger Property="TreeViewItem.ContextMenu.IsOpen" Value="True">
<Setter Property="TreeViewItem.BitmapEffect">
<Setter.Value>
<OuterGlowBitmapEffect GlowColor="Yellow" GlowSize="2"/>
</Setter.Value>
</Setter>
</Trigger>
</Style>
...
But it is obviously not working because Property="TreeViewItem.ContextMenu.IsOpen" is not recognized. Any suggestions to what I need to change?
You can bind to the IsOpened property of the context menu using a DataTrigger:
<DataTrigger Binding="{Binding ContextMenu.IsOpen, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
Unfortunately, since all of the items in TreeView share the same ContextMenu, that will highlight all of them at once. There doesn't seem to be a property that lets you find out which FrameworkElement opened the ContextMenu.
You could handle the ContextMenuOpening and ContextMenuClosing events on the TreeViewItem, since those will bubble up from the control that handled the click and pass through the right TreeViewItem. If you want to do it in XAML, you could use an EventTrigger to start a one-frame animation that changes your property. The cleanest option may be to write an attached behavior that handles the ContextMenuOpening and ContextMenuClosing events and sets an attached property to true when the context menu is open.

Categories