WPF blinking foreground label [duplicate] - wpf

This question already has answers here:
Blinking TextBlock
(3 answers)
Closed 6 years ago.
I'm trying to make the foreground of a label blink. I tried the following code but I get the following exception and I don't know how to solve it.
'System.Windows.Media.Animation.ColorAnimation' animation object cannot be used
to animate property 'Foreground' because it is of incompatible type
'System.Windows.Media.Brush'.
Using this XAML:
<Label Content="{Binding Path=SendingAlert, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Foreground="Transparent"
HorizontalAlignment="Right">
<Label.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSending, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard
Storyboard.TargetProperty="Foreground"
Duration="0:0:0.5">
<ColorAnimation From="Transparent" To="Red" AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<!--<DataTrigger Binding="{Binding IsSending}" Value="False">
<Setter Property="Foreground" Value="Transparent"/>
</DataTrigger>-->
</Style.Triggers>
</Style>
</Label.Style>
</Label>
and where
public bool IsSending
{
get { return !CanDoActions; }
}
private string _sendingAlert = "sending";//string.Empty;
public string SendingAlert
{
get { return _sendingAlert; }
set
{
_sendingAlert = value;
OnPropertyChanged(() => SendingAlert);
}
}
Any idea of how to fix this?

The Foreground property is of type Brush, which is different from an object of type Color
You can use a ColorAnimiation to animate an object of type Color, but not of type Brush, so to animate your foreground brush, you need to set the property to the Brush.Color, like this:
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"

Related

WPF Source Name property cannot be set within Style. Triggers section Error

I'm getting an error with the following details:
Source Name property cannot be set within Style. Triggers section
<Rectangle Margin="121,163,0,248" HorizontalAlignment="Left" Width="33" Height="34">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Blue"></Setter>
<Style.Triggers>
<EventTrigger SourceName="myButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Fill.Color" To="Orange"
Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
BeginTime="0:0:0">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
I want to change the rectangle fill color with Color Animation tag when click on button.
Like it says, you cannot use source name in a style like that.
You can use a data trigger instead. Set say a bool property in your viewmodel from your button's command of click.
Then start your storyboard with a datatrigger binding that bool property and comparing value.
You can probably easily Google datatrigger and storyboard but here's a so question includes an example.
WPF Data Triggers and Story Boards
Btw.
Routed events are rarely very useful IME. Binding icommand is way more practical. Usually.
Edit:
Here's a quick and dirty sample using a togglebutton. Since this approach uses binding it can reference controls by name. Binding is resolved at run time.
<Grid>
<Rectangle>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Blue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=StartStop}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="ColourStoryboard">
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Fill.Color" To="Orange"
Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="ColourStoryboard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<ToggleButton Content="Start Stop"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Name="StartStop"/>
</Grid>
</Window>
Paste the grid inside a mainwindow, spin it up. When you click the togglebutton it sets ischecked true so the rectangle animates to orange and back to blue. When you click the button again, ischecked becomes false and the animation stops.
You could instead write code in a button handler that set a bound property which is in the datacontext and bind the datatrigger to that bound property. That's what the markup in the link is doing with IsBusy.

MultiDataTrigger vs DataTrigger with multibinding

I encountered a situation where I can easily achieve the same functionality by using a MultiDataTrigger or, alternately, using a DataTrigger with a MultiBinding. Are there any substantive reasons to prefer one approach over the other?
With MultiDataTrigger:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=SomePath}" Value="SomeValue"/>
<Condition Binding="{Binding Path=SomeOtherPath, Converter={StaticResource SomeConverter}}" Value="SomeOtherValue"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyStoryboard}"/>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
With MultiBinding:
<DataTrigger Value="foo">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource fooConv}"/>
<Binding Path=SomePath/>
<Binding Path=SomeOtherPath/>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyStoryboard}"/>
</DataTrigger.EnterActions>
</DataTrigger>
Multibinding requires a converter for all but the rarest circumstances (using StringFormat).
MultiTrigger only requires a converter to get your binding results into booleans.
I would like to elaborate a little bit more.
For me, MultiBinding and MultiDataTrigger are fundamentally different and although in some situations you can use both to achieve the same functionality, it feels kind of like a hack to make both work the same way.
MultiDataTriggers should be used when you need more than one condition to be met separately so that you can do an action (set a property value, begin an animation etc). For example, you need A to be true and B to be false. Both of these conditions can by themselves be interpreted separately. It's the case of this question.
MultiBindings, on the other hand, should be used when you need more than one parameter to calculate a single output of your choice. This output would need to be of some value for you to set the property. For example, you will only change the property value if A equals B. This makes sense when you use the same style on multiple controls and A is a property of the control (say, the Text property of a TextBlock) and B is a single property from the View Model named "SelectedText". So a problem we might be trying to solve is this: among all the TextBlocks on my View, set the foreground of the one with the same Text as the property SelectedText from my View Model to blink (color changing animation).
In your example, I would use a MultiDataTrigger since your conditions can be evaluated separately. Otherwise your MultiValueConverter would only check for your second condition, ignoring the first one and would serve no real purpose for being a MultiDataTrigger really.
I'll leave the XAML for the example where I'd use the DataTrigger with MultiBinding that I mentioned above: (I assume you are using the MVVM pattern)
<Style TargetType="{x:Type TextBlock}" x:Key="SelectedTextStyle">
<Setter Property="FontFamily" Value="Segoe UI Light"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource StringsToBooleanConverter}">
<Binding Path="SelectedText"/> <!--This is a property of the View Model-->
<Binding RelativeSource="{RelativeSource Self}" Path="Text"/> <!--This is the Dependency Property 'Text' of the TextBlock control-->
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color" Duration="0:0:2" From="Black" To="DarkOrange" AutoReverse="True" FillBehavior="HoldEnd" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Foreground.Color" Duration="0:0:0" From="DarkOrange" To="Black" FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>

Making a WPF Label (or other element) flash using animation

I have a label that I only make visible based on one of my ViewModel Properties. Here is the XAML:
<Label HorizontalAlignment="Center" VerticalAlignment="Center"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
FontSize="24" Width="200" Height="200" >
<Label.Content >
Option in the money!
</Label.Content>
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding OptionInMoney}" Value="True">
<Setter Property="Visibility"
Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
I'm not sure this is the best way, but in any case, I'd also like to have the label flashing. Clearly, I only want it flashing when it is visible. Can someone point me to some example code, or write a quick example to do this? I assume I need some sort of trigger, and an animation. Presumably I also need a trigger when the label is no longer visible so that I stop the animation?
Thanks,
Dave
P.S. Is there a good book or site for all these WPF tricks? Something like the "MFC Answer Book" for those that remember that book.
You could add a Storyboard animation to the Style.Resources and start it in the EnterActions section of the DataTrigger.
A simple DoubleAnimation on the Opacity should work fine
Something like this:
<Label.Style>
<Style TargetType="{x:Type Label}">
<Style.Resources>
<Storyboard x:Key="flashAnimation" >
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" AutoReverse="True" Duration="0:0:0.5" RepeatBehavior="Forever" />
</Storyboard>
</Style.Resources>
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding OptionInMoney}" Value="True">
<Setter Property="Visibility" Value="Visible" />
<DataTrigger.EnterActions>
<BeginStoryboard Name="flash" Storyboard="{StaticResource flashAnimation}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="flash"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
StoryBoard is certainly the WPF way, but it can be achieved by a simple code also. Here it goes, to make a label background blink:
lblTimer is a Lebel on your form with some text, say, "I AM BLINKING"
This can be applied to any property, as VISIBILITY.
// Create a timer.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Tick += timer_Tick;
timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
timer.Start();
}
// The timer's Tick event.
private bool BlinkOn = false;
private void timer_Tick(object sender, EventArgs e)
{
if (BlinkOn)
{
lblTimer.Foreground = Brushes.Black;
lblTimer.Background = Brushes.White;
}
else
{
lblTimer.Foreground = Brushes.White;
lblTimer.Background = Brushes.Black;
}
BlinkOn = !BlinkOn;
}
Try this post. It's called 'Blinking TextBlock' but you can easily swap a TextBox for a Label`.

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.

Trying to animate height but getting error that height is NaN

Been trying to create an animation to dynamically adjust height. I found this info that helped but when I try to use it I get an error: 'System.Windows.Media.Animation.DoubleAnimation' cannot use default destination value of 'NaN'.
If I specify the height I get that error.
Style:
<Style x:Key="bdrSlideIn"
TargetType="{x:Type Border}">
<Style.Resources>
<Storyboard x:Key="storyBoardIn">
<DoubleAnimation BeginTime="00:00:00"
From="0"
Duration="00:00:00.65"
Storyboard.TargetName="{x:Null}"
Storyboard.TargetProperty="(FrameworkElement.Height)"
DecelerationRatio="1" />
</Storyboard>
<Storyboard x:Key="storyBoardOut">
<DoubleAnimation BeginTime="00:00:00"
To="0"
Duration="00:00:00.65"
Storyboard.TargetName="{x:Null}"
Storyboard.TargetProperty="(FrameworkElement.Height)"
AccelerationRatio="1" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding SearchExecuted}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource storyBoardIn}"
Name="SlideStoryboard" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource storyBoardOut}" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
Border:
<Border VerticalAlignment="Top"
Style="{StaticResource bdrSlideIn}">
<WPFToolKit:DataGrid Name="dgSearchResults"
ItemsSource="{Binding SearchResults}"
MaxHeight="280"
VerticalAlignment="Top">...
If you want to keep Height dynamic then you can't animate Height directly: As you've seen, unless you explicitly assign it WPF will try to interpolate to NaN.Instead, give your element a LayoutTransform <ScaleTransform/>, and animate the ScaleX and ScaleY parameters of that transformation.
You could always create an attached property for the height that does nothing other than set the height property on the target control, that way you can animate using To on your attached property.
public class AnimatedPanelBehavior
{
public static double GetAnimatedHeight(DependencyObject obj)
{
return (double)obj.GetValue(AnimatedHeightProperty);
}
public static void SetAnimatedHeight(DependencyObject obj, double value)
{
obj.SetValue(AnimatedHeightProperty, value);
}
public static readonly DependencyProperty AnimatedHeightProperty =
DependencyProperty.RegisterAttached("AnimatedHeight", typeof(double), typeof(AnimatedPanelBehavior), new UIPropertyMetadata(0d, new PropertyChangedCallback((s, e) =>
{
FrameworkElement sender = s as FrameworkElement;
sender.Height = (double)e.NewValue;
})));
}
Then to animate it you would use a normal animation, just tried it now and it works fine but I've not investigated any further than "it works".
<DoubleAnimation Storyboard.TargetProperty="(local:AnimatedPanelBehavior.AnimatedHeight)" To="100" Duration="0:0:5"/>
use AnimatedHeight instead of height on anything that you want to be able to animate.
Since your TargetProperty is Height, you can just set a default value of Height and it will work. In my case as soon as I have put a number for Height on the actual control itself,
<TextBlock Height="30" ..
<TextBlock Style ..
...
<StoryBoard ..
and then had the animation (which were to make toggle the height) it worked fine.

Resources