WPF: Animating a StaticResource updates other controls that use that resource - wpf

I have two TextBoxes that use the same StaticResource for their foreground colour.
When I apply an animation that changes the colour of the first TextBox, the colour on the second TextBox is also changed.
This does not happen if I don't use a StaticResource, so I am guessing that the animation is changing the colour of the brush defined in the resource, rather than the foreground colour on the first TextBox.
Here is the code I am using;
<Page.Resources>
<SolidColorBrush x:Key="TextBrush"
Color="Black" />
<Storyboard x:Key="Glow"
TargetProperty="Foreground.Color"
Storyboard.TargetName="txt1">
<ColorAnimation To="Blue"
Duration="0:0:0.1" />
</Storyboard>
<Storyboard x:Key="Normal"
TargetProperty="Foreground.Color"
Storyboard.TargetName="txt1">
<ColorAnimation To="Yellow"
Duration="0:0:0.1" />
</Storyboard>
</Page.Resources>
<StackPanel>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource Glow}" />
</EventTrigger>
<EventTrigger RoutedEvent="StackPanel.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource Normal}" />
</EventTrigger>
</StackPanel.Triggers>
<TextBlock Name="txt1"
Foreground="{StaticResource TextBrush}">Text One</TextBlock>
<TextBlock Name="txt2"
Foreground="{StaticResource TextBrush}">Text Two</TextBlock>
</StackPanel>
Is there anyway around this?
Matt

By using a single StaticResource in the binding, changing the Foreground in your animation will change the resource itself. This behavior is by design, as anything else would require full copies of resources, which would very much reduce the usefulness and benefits of using StaticResource in the first place.
The easy workaround, of course, is to not use a StaticResource here, or to use a separate resource per TextBox.

Related

Adding content/text in rectangle WPF

This is my code for rectangle :
<Rectangle x:Name="rect1" Grid.Column="1" Fill="#FF5C626C" HorizontalAlignment="Left" Height="50" Margin="36,171,0,0" StrokeThickness="2" VerticalAlignment="Top" Width="358" RadiusX="30" RadiusY="50">
<Rectangle.Triggers>
</Rectangle.Triggers>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline >
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="#FF767C84" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Rectangle.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ParallelTimeline >
<ColorAnimation Storyboard.TargetProperty="Fill.Color" To="#FF5C626C" />
</ParallelTimeline>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
My question is how do i add a text/content in the rectangle ?
One might suggest using a code block, but if u go through my code, youu'll notice that the rectangle changes it's color on mouseover.So if i put a textblock over the rectangle,the mouseover doesn't work properly(as the textblock covers the entire rectangle).
Another suggestion would be to use a border.But i am not sure about this as i need to find the code to apply mouse over effect on a border.
The next suggestion might be to use a button instead.I would've but my rectangle has corner radius and is a bit round-shaped which, if i use a button, would be hard to achieve.
So how do i add content/text inside the rectangle?
If you feel you definitely have to use a rectangle, put it in a grid and add a TextBlock element above it.
By setting the TextBlock's IsHitTestVisible property to False all hit-testing (mouse events) will be ignored on it and fall through to your rectangle.
<Grid>
<Rectangle (...your attributes here...)>
(...your rectangle code here...)
</Rectangle>
<TextBlock Text="Hello World!" IsHitTestVisible="False" />
</Grid>

EventTrigger on a TextBlock that sets a Border - Cannot resolve all property references

I have a Custom control in WPF in which I define a large ItemsControl Template.
In there, I have a Grid and in one column of that grid, I got a TextBlock and in another column I have a Border.
I want to highlight the Border when the mouse enters the TextBlock.
I tried several scenarios:
first an EventTrigger in the TextBlock's Style, but I learned that you can't do that, then an EventTrigger within the TextBlock's Triggers section, and now I just put it in the DataTemplate.Triggers of my ItemsControl, but I keep getting the error:
"Cannot resolve all property references in the property path 'Border.BorderBrush.Color'. Verify that applicable objects support the properties."
Here is the code that causes trouble:
<DataTemplate.Triggers>
<EventTrigger SourceName="mytxtblock" RoutedEvent="TextBlock.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="myborder"
Storyboard.TargetProperty="Border.BorderBrush.Color"
Duration="0:0:1"
To="White" />
<ThicknessAnimation Storyboard.TargetProperty="Border.BorderThickness"
Duration="0:0:1"
From="0"
To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</DataTemplate.Triggers>
I think I'm missing something about the way i refer to the Color property of my Border, any insight?
Thanks!
EDIT: I figured out that declaring a SolidColorBrush in Resources and then using that value allows me to get rid of the
Storyboard.TargetProperty="Border.BorderBrush.Color" that changes to Storyboard.TargetProperty="Border.BorderBrush",
but now the compiler tells me that the color i declared (i tried Green and Transparent) is not a valid value for "To"...
Try
<ColorAnimation
Storyboard.TargetName="myborder"
Storyboard.TargetProperty="BorderBrush.(SolidColorBrush.Color)"
Duration="0:0:1"
To="White" />
but you have to declare a BorderBrush
BorderBrush="whatever"
or
<Border.BorderBrush>
<SolidColorBrush Color="whatever" />
</Border.BorderBrush>
in your "myborder" too.
On your ColorAnimation there are two properties:
Storyboard.TargetName="myborder"
Storyboard.TargetProperty="Border.BorderBrush.Color"
It implies, that myborder has a property called Border. I think that causes your error.

Button click changes visibility of a DataGrid with a Trigger in WPF

Hi i am trying to find some way to when a button is clicked changes the visibility of other control, like a DataGrid with a Trigger in XAML.
The button only changes the visibility of the DataGrid to Visible, it does other things in Code Behind, but this is something that i think that can be done in a Style with a Trigger.
I tried to find a solution and it seems to be possible to do but i can't understand how.
Thanks in advance.
<Button Content="Button!">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.Target="{x:Reference dataGrid}"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
{x:Reference dataGrid} references a DataGrid with the name dataGrid, alternatively you could just use Storyboard.TargetName. You would normally use the Storyboard.Target property if you do binding or references to resources.
Just a suggestion, but how about, for something more understandable, having a Checkbox enabling/disabling the DataGrid display? This is what I usually do:
<DockPanel LastChildFill="True">
<CheckBox DockPanel.Dock="Right" VerticalAlignment="Center" x:Name="DisplayBox"
Content="Display grid" Margin="4" IsChecked="False"/>
<DataGrid Visibility="{Binding ElementName=DisplayBox, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
</DockPanel>
And of course, you'll have to implement the appropriate converter

Animating text color

I need to animate the text color of a custom control between two colors, which are read from two Brush properties of the custom control. My resources look like this:
<SolidColorBrush x:Key="TextBrush">{TemplateBinding Foreground}</SolidColorBrush>
<SolidColorBrush x:Key="AltTextBrush">{TemplateBinding ForegroundAlt}</SolidColorBrush>
Right now, I am trying to animate using a ColorAnimation:
<ColorAnimation Storyboard.TargetName="MyControlText" Storyboard.TargetProperty="Foreground" To="{StaticResource AltTextBrush}" Duration="00:00:00.3000000" />
The ColorAnimation seems to want a Color object, rather than the Brush I am trying to pass. I think I can write an IValueConverter to get the color from the brush, but before I do that, I want to see if there is a simpler way to do the job. Here are my questions:
-- Is there a simple way to animate between two brush resources, or do I need to extract the color for animation?
-- If I need to extract the colors, is an IValueConverter best practice?
-- And finally, amI headed down the right road, or is there a simpler solution to this problem?
Thanks for your help.
Tried with using a Binding and it seems to be working like this
To="{Binding Source={StaticResource TextBrush}, Path=Color}"
Here's a xaml example
<Window.Resources>
<SolidColorBrush x:Key="TextBrush">Black</SolidColorBrush>
<Storyboard x:Key="blinkAnimation" Duration="0:0:5" >
<ColorAnimation Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"
Storyboard.TargetName="TitleTextBlock"
To="{Binding Source={StaticResource TextBrush}, Path=Color}"
AutoReverse="True"
Duration="0:0:2"/>
</Storyboard>
</Window.Resources>
<Grid Background="Black" Name="grid">
<TextBlock x:Name="TitleTextBlock"
Background="Black"
Text="My Text"
FontSize="32"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Foreground="White">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<StaticResource ResourceKey="blinkAnimation"/>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Grid>

Binding a Storyboard property relative to the target of the storyboard

I have a storyboard which targets an element, and binds one of its own properties to a property on another element:
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.X"
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ActualWidth}"
To="0"
Duration="0:0:5"/>
</Storyboard>
This storyboard works when the storyboard is stored in the resources of the window which holds the storyboard target. The 'From' value is correctly bound to the ActualWidth of the host Window instance.
However, I need to store the storyboard in my application level resources. From here, the storyboard does not seem to be able to target the window to determine the 'From' property. This is understandable as from inside <Application.Resources>, the binding won't be able to find an 'ancestor' of type Window.
I guess I need to be able to bind the 'From' value, relative to the target of the animation, rather than relative to the storyboard's DoubleAnimation.
Is this possible, and if so, how?
Here is the sample MainWindow.xaml:
<Window.Resources>
<!--This works : Storyboard correctly sets 'From' property to 'ActualWidth' of window-->
<Storyboard x:Key="localStoryBoard">
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.X"
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ActualWidth}"
To="0"
Duration="0:0:5"/>
</Storyboard>
</Window.Resources>
<StackPanel>
<Button
RenderTransformOrigin="0,1"
HorizontalAlignment="Left"
Content="Click me">
<Button.RenderTransform>
<TranslateTransform/>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource centralStoryBoard}"/>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
And here is an example app.xaml:
<Application x:Class="WpfApplication3.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!--Storyboard doesn't work at all-->
<Storyboard x:Key="centralStoryBoard">
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.X"
From="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ActualWidth}"
To="0"
Duration="0:0:5"/>
</Storyboard>
</Application.Resources>
</Application>
This won't work, as the eventtrigger refers to the app.xaml version. If you change it to the local resource version, you can see it works.
The example doesn't work because when you put your storyboard to resources it has no Window ancestor. What actually RelativeSource do is search element tree backwards waiting for the node with AncestorType appear and then binds it.
When put in the Window.Resources there's actual Window back in the tree and it binds it correctly. When put to the application resources there's no Window back in the tree because it's not connected to the Window whatsoever.
If you really want to put your storyboard to the application resources you should consider giving up the idea with binding. Instead, you can check out this answer with code for a clipper, I think this is what you need - https://stackoverflow.com/a/59376318/11178539.

Resources