Datatrigger Not Firing Storyboard Or Not At All - c#

I can not seem to get Data triggers to work no matter what I do. I'm trying to play a storyboard from the view model when a value changes yet nothing happens, no error, no cant find binding, just nothing... The code I'm currently attempting to get working is:
XAML:
<Page
DataContext="{Binding ViewModel, Source={StaticResource Locator}}">
<Page.Resources>
<DataTrigger x:Key="alertInDataTrigger" Binding="{Binding alert}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource alertIn}" x:Name="alertIn_start"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="alertIn_start" />
</DataTrigger.ExitActions>
</DataTrigger>
<Storyboard x:Key="alertIn" Changed="visible" >
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="alert">
<EasingThicknessKeyFrame KeyTime="0:0:0.3" Value="729,2,2,658"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
<\Page.Resources>
C#:
public int alert
{
get { return this._alert; }
set
{
if (this._alert != value)
{
int oldalert = this._alert;
this._alert = value;
RaisePropertyChanged("alert");
}
}
}
I can see the int alert properly changing to 1 and I know the pages bindings are working as all the other bindings on the page are properly showing their bindings yet I can not seem to get data triggers to work. I could use a little help on this stumper :/

I see two things you are doing wrong. One is that triggers are should be added to the Page.Triggers not Page.Resources. The trigger will never fire if it is in the resources.
The other is Storyboard.TargetName="alert". This should not be set to alert unless you have an element in the Page that is named alert. I believe that if you don't add Story.TargetName then it defaults to the current element which is the page. Otherwise, set the name of the element you want to animate its margin.

Related

DataGridRow animation WPF gives error for CellsPanelHorizontalOffset

I have an animation for my DataGridRow that is when a new row is added, it slides it in from left to the view. everything works fine but in the binding errors, I see an error.
The animation code is added to LoadingRow event:
var sb = new Storyboard();
var slideAnimation = new ThicknessAnimation()
{
From = new Thickness(-dgData.ActualWidth, 0, dgData.ActualWidth, 0),
To = new Thickness(0),
Duration = new System.Windows.Duration(TimeSpan.FromSeconds(1)),
};
Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("Margin"));
sb.Children.Add(slideAnimation);
sb.Begin(e.Row);
the error is:
DataGridControl, Name='dgData' CellsPanelHorizontalOffset Button.Width Double Value '-58.32884999999999' (type Double) cannot be assigned to property Button.Width (type Double).
there are a bunch of errors like this every time a new row is added. how to solve the issue?
After a little digging, I found out why the binding error is popping up.
The internals of DataGridCellsPanel which is a template for DataGrid.ItemsPanel. Binds and Calculates the Width for Button (CellsPanelHorizontalOffset).
You can find the source code for that in file DataGridCellsPanel.cs at line 1983.
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGridCellsPanel.cs
If the Grid.Row size is incorrect, the DataGridCellsPanel will calculate the Width with negative value. Which leads to Binding Error:
System.Windows.Data Error: 5 :
Value produced by BindingExpression is not valid for target property. Double:'-92.3575'
BindingExpression:Path=CellsPanelHorizontalOffset; DataItem='DataGrid' (Name='dgData');
target element is 'Button' (Name=''); target property is 'Width' (type 'Double')
That's one of thousands internal bugs in WPF, I would suggest to use a different method, then going into negatives for Row. For example you can use Scale.X
<DataGrid.Resources>
<Storyboard x:Key="s_Slide">
<DoubleAnimation From="0" To="1" Duration="0:0:1"
Storyboard.TargetProperty="LayoutTransform.ScaleX"/>
</Storyboard>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource s_Slide}"/>
</EventTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>

WPF Animation not working on "fast" property change

I am having trouble making an animation work in WPF with DataTriggers when the property binded is changes "fast". The animation is a simple flash of an element. My problem is even after a long time searching on the internet, i can't figure out a way to explain why toggling the property ON and OFF in 2 consecutives lines doesn't work, but if the thread sleeps 1ms it does. I tried inserting other instructions to "waste" some time but it doesn't work either.
Here is the property in question in the viewmodel:
private bool m_activateFlash;
public bool ActivateFlash
{
get { return m_activateFlash; }
set
{
SetPropertyBackingField(ref m_activateFlash, value);
}
}
Here is the XAML :
<DataTemplate x:Key="JobTemplate" DataType="viewModel:JobViewModel">
<Border Margin="8,4,8,4" BorderThickness="1" BorderBrush="{StaticResource BorderBrush}" Background="{StaticResource JobBackgroundBrush}">
<Border.Resources>
<Storyboard x:Key="Blink">
<ColorAnimation Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" FillBehavior="Stop"
Duration="0:0:0.4" To="{StaticResource JobBackgroundFlash}" RepeatBehavior="3x" AutoReverse="True"/>
</Storyboard>
</Border.Resources>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ActivateFlash, NotifyOnTargetUpdated=True}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Blink}" Name="BeginBlinkStoryBoard"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
[....]
And finally here is where it is causing me problems in the viewmodel :
private void TriggerFlash()
{
ActivateFlash = true;
System.Threading.Thread.Sleep(1); // HACK DOESN'T WORK WITHOUT THIS LINE
ActivateFlash = false;
}
I looked up EventTriggers as maybe a way to circumvent the issue, but I feel DataTrigger is the logical way to achieve what i want. Any help is really appreciated :)
EDIT
It appears i wasnt invoking Trigger flash on the main thread. Simply calling Dispatcher.Invoke(...) solved the problem
Thanks
Adam
Yeah, if you do:
ActiveFlash = true;
ActiveFlash = false;
You aren't going to see anything on the screen because the UI is on the main thread and it hasn't had time to paint before the value is changed. Toggling the value happens so fast that you aren't going to see anything on the screen.
I believe and somebody can correct me if I'm wrong, but I believe that WPF batches repaints behind the scenes for performance, so you're just stomping over yourself.

Custom Control Storyboard Template Binding Not Working - XAML C#

I created a custom Control which has some Dependency Properties which values I also Need to assign to the storyboard in the ControlTemplate.
Here is the beginning of the Code of my Control Template:
<ControlTemplate TargetType="local:RingPresenter">
<Grid x:Name="RootGrid">
<Grid.Resources>
<Storyboard x:Key="StatisticUpdateAnnimation">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(RingSlice.EndAngle)" Storyboard.TargetName="ringSlice">
<EasingDoubleKeyFrame KeyTime="0" Value="45"/>
<EasingDoubleKeyFrame KeyTime="0:0:2.2" Value="{TemplateBinding Angle}">
The Important part is the Value={TemplateBinding Angle}" - the value is getting applied succesfully to my
<helper:RingSlice InnerRadius="100" Radius="150" StartAngle="0" EndAngle="{TemplateBinding Angle}" Fill="DarkCyan" x:Name="ringSlice">
</helper:RingSlice>
... which is part of my Control Template, but in the storyboard the value is staying 0. The Debugger is saying that Anglehas the correct value and it is working fine for my Ringslice and even the metadata should be 45.
Why is this value not applied to my storyboard? How can I fix this?
When in doubt, swap EndAngle="{TemplateBinding Angle}" for EndAngle="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Angle}".
TemplateBinding is quick and useful but doesn't always have the strongest capacity to feed through information it's bound to, for reasons that escape me entirely.

How do I make a Block Arrow blink?

I have a block arrow and i want to make it blink by just filling it with green. I would like to be able to stop it also. I have a right click menu to start it and stop it.
This is what i have so far. But i cant figure out how to start it. I tried to access it but i got an error:
All objects added to an IDictionary must have a
Key attribute or some other type of key associated with them. Line 11 Position 10.
Here is my xaml code:
<ed:BlockArrow x:Name="ArrowLeft" Fill="Green" HorizontalAlignment="Left" Height="29" Margin="142,0,0,-3" Orientation="Left" Stroke="#FF13FF00" VerticalAlignment="Bottom" Width="39" />
<Window.Resources>
<Storyboard x:Name="Blink" AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ArrowLeft"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01" Value="Green"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
So, in the right click menu to start it i have:
private void MenuItemLeft_Click(object sender, RoutedEventArgs e)
{
Storyboard blinkAnimation = TryFindResource("Blink") as Storyboard;
if (blinkAnimation != null)
{
blinkAnimation.Begin();
}
Is there a better way to do this? or what am i doing wrong?
WPF Resources are dictrionaries, hence everything within a Resource must have a key. You can add a key by adding an x:Key attribute. You can then locate your item by indexing into the Resource dictionary directly, Resources["MyKeyName"]
Regarding your method of implementation, it looks fine to me.

StackOverflowException on InitializeComponent

I'm trying to follow this little tutorial, but I keep getting this exception.
The relevant XAML looks like this:
<StatusBar Margin="0,288,0,0" Name="statusBar" Height="23" VerticalAlignment="Bottom">
<StatusBar.DataContext>
<m:MainWindow />
</StatusBar.DataContext>
<TextBlock Name="statusText" Text="{Binding Path=StatusBarText, NotifyOnTargetUpdated=True}" DataContext="{Binding}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</StatusBar>
I'm guessing I'm getting the StackOverflowException because I'm trying to use MainWindow as the DataContext. I want to use the MainWindow because it seems like a logical place to put my StatusBarText property,
public partial class MainWindow : Window
{
public string StatusBarText { get; set; }
It makes it easier to access in my code-behind event handlers.
What am I supposed to do then? Where am I supposed to put this property? Or is there a way to set the DataContext to "this" so that it doesn't create a new instance of MainWindow and just refers to itself?
I generally set my DataContext in code-behind, in the constructor (I usually use MVVM, but have used a window in small temp projects):
public MainWindow()
{
statusBar.DataContext = this;
}
Note that in your shown code example, you will only get your initial StatusBarText value, because you are not implementing INotifyPropertyChanged.
Ideally your properties which you will bind to should live within a ViewModel following the MVVM pattern, abstracting themselves away from the View. Since that is not your question however we shall move along...the DataContext is inherited from its parent. Therefore if the StatusBar lives in the Window, which I am pretty certain it does, it will already be inheriting the DataContext from the Window. You are essentially trying to bind a UI component using a UI component (Window) as your source for the DataContext. Far from ideal...here is an overview of the MVVM pattern...

Categories

Resources