Can I make a WPF style take precedence over a local value? - wpf

I have created a boolean attached property in my WPF project, and I want elements with this property set to true to display a certain way, regardless of any local local values that have been set in the XAML.
From the documentation, I understand that by default, local values take precedence over both style setters and style triggers. Is it possible to create a style trigger that takes precedence over local values?

Using the technique mentioned by Erti-Chris Eelmaa in his comment on my question, I ended up creating an animation which did the trick:
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="my:Ext.IsReadOnly" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Name="IsReadOnly">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(Button.IsEnabled)" Duration="0:0:0">
<DiscreteBooleanKeyFrame Value="False" KeyTime="0:0:0" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="IsReadOnly" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
This takes precedence over any local value for Button.IsEnabled.

Related

How to make a DevExpress grid row blink?

Trying to perform a simple red blinking effect for a single row in a DevExpress grid.
I've applied the following style on the grid's row:
<Style x:Key="AlertedRowStyle" TargetType="{x:Type dxg:GridRowContent}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Row.IsAlerted}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background"
To="Red"
Duration="0:0:0.500"
AutoReverse="True"
RepeatBehavior="Forever">
<ColorAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut" />
</ColorAnimation.EasingFunction>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background"
To="White"
Duration="0:0:0.500" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
It causes the following exception:
'System.Windows.Media.Animation.ColorAnimation' animation object cannot be used to animate property 'Background' because it is of incompatible type 'System.Windows.Media.Brush'.
Also tried to change the Storyboard.TargetProperty to Background.Color and got:
Cannot resolve all property references in the property path 'Background.Color'. Verify that applicable objects support the properties.
How can I solve this issue?
Storyboard.TargetProperty="Background.Color" is right. Try
<Style x:Key="AlertedRowStyle" TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
...
I guess Background is Null and so the Storyboard can not find something to animate.
Try
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
worked for me.
you can open a new thread. In this thread you use a loop. In this loop you can change the Backcolor of the row. In the loop you sleep the thread for 0.3 sec or something. So it should look like blinking.
regards

DataTrigger don't seems to fire

I want to create a datatrigger that makes my page blink (from transparent to red). So I created a DataTrigger that listens to a boolean flag within my viewmodel. This flag shall indicate whenever the user needs to be reminded. In that case, my page shall blink from transparent to red.
I was pretty sure that I have implemented the data trigger in a correct manner, but my app does nothing - no error, no blinking... So I must have something missed.
<Style x:Key="ReminderPage" TargetType="{x:Type ViewTemplates:TpApplicationBarView}" BasedOn="{StaticResource TpApplicationBarViewStyle}">
<Style.Triggers>
<!-- Reminder animation, when the time comes to remind the user -->
<DataTrigger Binding="{Binding IndicateReminderAnimation}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard x:Name="Blink">
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="True"
From="Transparent"
To="Red"
Duration="0:0:1"
RepeatBehavior="Forever">
</ColorAnimation >
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding IndicateReminderAnimation}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="False"
To="Transparent"
Duration="0:0:1">
</ColorAnimation >
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
So, what do I have done wrong?
Update: I can see the following message in the output window:
System.Windows.Media.Animation Warning: 6 : Unable to perform action because
the specified Storyboard was never applied to this object for interactive control.;
Action='Stop'; Storyboard='System.Windows.Media.Animation.Storyboard';
Storyboard.HashCode='61356140'; Storyboard.Type='System.Windows.Media.Animation.Storyboard';
TargetElement='System.Windows.Media.Animation.Storyboard'; TargetElement.HashCode='61356140';
TargetElement.Type='System.Windows.Media.Animation.Storyboard'
Update2: After googling arround I found out, that it is a problem with the UI Thread. So I made a dispatcher call whenever I set the bound property. But even with this trick, there's no color animation. But the error in the output window seems to be vanished. So, I'm searching for further ideas on how to fix the animation.
Update3: It seems to be a general problem setting the background color of the page. But it's really strange. The Page is placed in a NavigationFrame. Setting the background color of the navigation frame will change the color of the application, but setting the background color of the page (even without any animation) won't change anything.
I think you will have to set the animations Target, something like this -
Storyboard.TargetName="yourWindowName"
You may have already checked this, but make sure that correct object is set as your TpApplicationBarView's DataContext(having IndicateReminderAnimation property).
I found the bug - or better the two bugs.
1.) It seems not be possible to change the background color of a page that is placed within a Navigation Frame.
So first was to move the binding and event to the MainWindow itself (wpf window class)
2.) The Style that contains the data trigger did not work. After googling around I found a working solution for what I'm searching for.
<Storyboard x:Key="RemindUser" >
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="True"
From="Transparent"
To="{StaticResource WinAccentBackgroundColor}"
Duration="0:0:1"
RepeatBehavior="Forever">
</ColorAnimation >
</Storyboard>
<Storyboard x:Key="StopRemindUser">
<ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)"
AutoReverse="True"
To="Transparent"
Duration="0:0:1">
</ColorAnimation >
</Storyboard>
<Style x:Key="ReminderWindow" TargetType="{x:Type Metro:SnappedTransparentWindow}" BasedOn="{StaticResource TransparentWindow}">
<Style.Triggers>
<!-- Reminder animation, when the time comes to remind the user -->
<DataTrigger Binding="{Binding IndicateReminderAnimation}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource RemindUser}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource StopRemindUser}"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
The key was to split the binding and storyboard into different parts.

Datagrid MouseOver and Selected States when using arrow keys

I have a wpf datagrid. I have added styling to show a mouseover color on a row.
What I am trying to achieve is when the mouseover appears, and a user starts using the arrow keys to navigate up and down, the mouseover needs to disappear and only the row that the user used arrow keys to get to, is the highlighted one.
The issue is the mouse cursor has been left on the grid while the user navigates with the arrow keys and the row under the cursor holds the highlight as well as the row the went to using arrows.
Here is my sample xmal:
<DataGrid AutoGenerateColumns="True" Height="277" HorizontalAlignment="Left" Margin="0,311,0,0" Name="dataGrid1"
VerticalAlignment="Top"
Width="478" ItemsSource="{Binding Path=Persons}"
RowHeight="20"
RowHeaderWidth="35" Grid.ColumnSpan="2" >
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Thanks
You'll need to set some kind of flag when the user hits an Arrow key so that the background only changes if IsMouseOver and IsUsingArrowKeys is false. You might even be able to use the Mouse Visibility as a condition instead of using a flag
I'm not positive the exact syntax, but it should be something like this
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!-- May need to reference RelativeSource here, not sure -->
<Condition Property="IsMouseOver" Value="False" />
<Condition Binding="{Binding IsUsingArrowKeys}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
I would suggest highlighting the row based on thier focus triggers.
Something like this:
<EventTrigger RoutedEvent="GotFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dataGrid1" Storyboard.TargetProperty="Background" Duration="0:0:0.1" To="Green"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dataGrid1" Storyboard.TargetProperty="Background" Duration="0:0:0.1" To="White"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
And giving focus to them manually, something like this:
private void Btn_Click(object sender, RoutedEventArgs e)
{
dataGrid1.Focus();
}
So when another row gets focus, the current row loses focus & automatically falls back to non highlighted color background.

WPF: Selecting the Target of an Animation

I am trying to create a simple (I think) animation effect based on a property change in my ViewModel. I would like the target to be a specific textblock in the control template of a custom control, which inherits from Window.
From the article examples I've seen, a DataTrigger is the easiest way to accomplish this. It appears that Window.Triggers doesn't support DataTriggers, which led me to try to apply the trigger in the style. The problem I am currently having is that I can't seem to target the TextBlock (or any other child control)--what happens is which the code below is that the animation is applied to the background of the whole window.
If I leave off StoryBoard.Target completely, the effect is exactly the same.
Is this the right approach with the wrong syntax, or is there an easier way to accomplish this?
<Style x:Key="MyWindowStyle" TargetType="{x:Type Window}">
<Setter Property="Template" Value="{StaticResource MyWindowTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ChangeOccurred}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard BeginTime="00:00:00" Duration="0:0:2" Storyboard.Target="{Binding RelativeSource={RelativeSource AncestorType=TextBlock}}"
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<ColorAnimation FillBehavior="Stop" From="Black" To="Red" Duration="0:0:0.5" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Update
Should have also mentioned that I tried to name the TextBlock and reference it via StoryBoard.TargetName (as Timores suggested), and got the error "TargetName property cannot be set on a Style Setter."
EDIT: I have overseen the fact that the TextBlock is in the ControlTemplate of your custom Window/Control. I do not think that it is possible to target a control within the ControlTemplate from a Storyboard outside of this ControlTemplate. You could however define a property on your custom Window which you then databind to your ChangeOccurred property, and then add the trigger to your ControlTemplate which will now get triggered by the custom Control's property rather than the Window's ViewModel's property (of course, indirectly it is triggered by the ViewModel because ChangeOccurred is bound to the property of the custom Window which in turn triggers the animation - uh, complex sentence, hope you understand). Is this an option? Could you follow? ;-)
Maybe some code helps:
public class MyCustomWindow : Window
{
public static readonly DependencyProperty ChangeOccurred2 = DependencyProperty.Register(...);
public bool ChangeOccurred2 { ... }
// ...
}
And some XAML:
<local:MyCustomWindow ChangeOccurred2="{Binding ChangeOccurred}" ... >
<!-- Your content here... -->
</local:MyCustomWindow>
<!-- Somewhere else (whereever your ControlTemplate is defined) -->
<ControlTemplate TargetType="{x:Type local:MyCustomWindow}">
<!-- your template here -->
<ControlTemplate.Triggers>
<Trigger Property="ChangeOccurred2" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard BeginTime="00:00:00" Duration="0:0:2"
Storyboard.TargetName="txtWhatever"
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<ColorAnimation FillBehavior="Stop"
From="Black" To="Red"
Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Note: I named the Window's property ChangeOccurred2 because I wanted it to be distinguishable from the ViewModel's ChangeOccurred property. Of course, you should choose a better name for this property. However, I am missing the background for such a decision.
My old answer:
So, you want to animate a TextBlock which is in the content of a (custom) Window?!
Why do you want to set the style on the Window, and not on the TextBlock itself? Maybe you should try something like this (did not test this!):
<local:MyCustomWindow ... >
<!-- ... -->
<TextBlock x:Name="textBlockAnimated" ... >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ChangeOccurred}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard BeginTime="00:00:00" Duration="0:0:2"
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<ColorAnimation FillBehavior="Stop"
From="Black" To="Red"
Duration="0:0:0.5"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- ... -->
</local:MyCustomWindow>
The {Binding ChangeOccurred} might not be sufficient. You might have to add a DataContext to the TextBlock, or add a RelativeSource or something.
Is the TextBlock in the MyWindowTemplate ?
If so, give the TextBlock a name and use Storyboard.TargetName to reference it.
See another question in SO

Begin Storyboard on more than one target

I have a subclassed ListBox with a SelectedItemChanging dependency property that is set to a Storyboard. When the selected item is changed, I want to run this Storyboard on each item in the ListBox.
How is this possible with a single instance of Storyboard?
Storyboards can be keyed and run from multiple triggers, and it works great as long as it's set up properly. If I am understanding you correctly, you're hoping to apply the storyboard to each individual ListBoxItem. In which case, why not make a style, and on that style's triggers, fire the storyboard.
Excuse my pseudocode.
<Storyboard x:Key="MyEnterStoryboard">
<!-- Do Enter Work -->
</Storyboard>
<Storyboard x:Key="MyExitStoryboard">
<!-- Do Exit Work -->
</Storyboard>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="SelectedItemChanging" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyEnterStoryboard}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MyExitStoryboard}"/>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
WPF Storyboards have a Clone method. Silverlight doesn't have this but thought I'd post it just in case someone stumbles across this post looking for a WPF solution.

Resources