How to manage each item's margin of item collection? - wpf

I dynamic add tab items into tabControl from viewModel. Each new tab item include in a tag property an object, which has status property as enumeration. If object's status is active, I need to change default tab item's margin to another with animation.
I try use dataTrigger in tab item style.
<DataTrigger
Binding="{Binding IsVendorActivated, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
To="0,0,13,1"
Duration="0:0:0.335"
Storyboard.TargetName="Border"
Storyboard.TargetProperty="Margin"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
So, binding property is checking all objects in viewModel object's collection, and when one of this objects has active status, it changes all tab item's margin.
What another ways may be useful to manage tab item's margin by it tag's status?

Related

How to create a single style for multiple mouse over events?

I have four buttons and four text boxes where each button is linked to one of the textblocks. When the mouse is over the button I want the corresponding textblock to fade in (and out on mouse leave). There are plenty of examples of this showing a single button and textblock where you can simply bind a datatrigger to the button name in the textblock style.
Here's what I've got so far (all of this is in a textblock style):
<DataTrigger Binding="{Binding ElementName=UpdateButton, Path=IsMouseOver}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty ="Opacity" Duration="00:00:01">
<DoubleAnimation From="0" To="1" Duration="00:00:01"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetProperty ="Opacity" Duration="00:00:01">
<DoubleAnimation From="1" To="0" Duration="00:00:01"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
As of right now, when I mouse over the Update Button, all of the textblocks show instead of just the one associated with the Update Button.
To fix this I could create styles for each textblock by their name and bind to the appropriate button, but this is a huge amount of repetition. I could likely used "BasedOn" to separate the button binding, but then we're still duplicating all of the code for the Storyboards and whatnot. But does anyone know a better way?
It would seem like there should be a way create this all in a single style using a single generic binding but link the specific buttons to their textblocks, so the button only triggers the Storyboard for it's linked textblock. Anyone know how to do this, or a better way?
A good way to handle this is to create a custom inherited TextBlock that can store reference to a button.
Example
using System.Windows;
using System.Windows.Controls;
//Custom TextBlock
public class SpecialTextBlock : TextBlock
{
//This will be the button reference
public Button BoundButton { get; set; }
//Register the BoundButton as a dependency to allow binding
public static readonly DependencyProperty ButtonProperty = DependencyProperty.Register
(
"BoundButton",
typeof(Button),
typeof(SpecialTextBlock),
new FrameworkPropertyMetadata(default(Button))
);
}
Now that your new SpecialTextBlock is set up, you can create a new style for it. Use your original style, but apply it to TargetType="local:SpecialTextBlock" instead of TargetType="TextBlock".
Then update your DataTrigger from your example within the style so that the trigger binds to itself (the SpecialTextBlock), and then looks at the referenced Button path.
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=BoundButton.IsMouseOver}" Value="True">
...
Now you are set up and can create your TextBlocks like so without having to restyle.
//Set your BoundButton binding to specify which button triggers the animation.
<local:SpecialTextBlock BoundButton="{Binding ElementName=UpdateButton}" />
<StackPanel>
<Button x:Name="MouseTarget"
Content="Mouse Over This"
/>
<Button Content="This one changes...">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MouseTarget, Path=IsMouseOver}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>

WPF: How do I initialize a property to be animated from the trigger that's animating it?

I have a Grid in my view whose "visibility" is determined by whether a collection has more than one element. That Grid needs to be animated expanding and collapsing as elements are added and removed. I've written a DataTrigger to do that which works fine.
<DataTrigger Binding="{Binding Path=MyCollection.Count, Converter={StaticResource IsLessThanConverter}, ConverterParameter=2}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="MyCollapsingGrid" TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)">
<DoubleAnimation Duration="00:00:0.2"
EasingFunction="{StaticResource ExpoEaseInOut}"
To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="MyCollapsingGrid" TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)">
<DoubleAnimation Duration="00:00:0.2"
EasingFunction="{StaticResource ExpoEaseInOut}"
To="1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
I'm initializing the LayoutTransform for my Grid to a default ScaleTransform
<Grid x:Name="MyCollapsingGrid">
<Grid.LayoutTransform>
<ScaleTransform />
</Grid.LayoutTransform>
</Grid>
When my view is initialized with a collection that has two elements and I remove one, the Grid animates collapsing correctly. However, when I initialize my view with a collection with one element, it shows the Grid briefly as it plays the collapse animation. Since I'm already using a trigger to do this animation, how do I initialize the Grid's LayoutTransform to have a ScaleX of 0 when my collection only has one element so that I don't see the collapse animation?
The simplest solution is to bind ScaleTransform.ScaleY property with mode set to OneTime:
<ScaleTransform ScaleY="{Binding Path=MyCollection.Count,
Converter={StaticResource TheMissingConverter},
ConverterParameter=2,
Mode=OneTime}" />
The only piece missing from this picture is the converter, which should work similarly to your IsLessThanConverter except it should return 1 and 0 instead false and true respectively (scale should be 1 if count is not less than 2, and 0 otherwise). Note that it is important to set the mode to OneTime (with mode set to OneWay I experienced unexpected behavior in certain circumstances while testing this solution).

Force a reload of Listbox items(usercontrols) on ListBox Visibility Changed

I have a ListBox that is bound to a List of UserControls.
The uc has a TextBlock that has an animation that fires OnLoad.
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.8" Storyboard.TargetProperty="FontSize" To="16" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style
Works fine. My problem is the List can be Collapsed by the user. When the user unCollapses the List I need the aanimation to fire again (it doesn't, of course - because they are already loaded). Doesn't seem to be any other event that would work. Soo..I'm trying to dump the ItemSource and reload them. It won't hurt as there are only up to 10 or 12 items. I have an ICommand in the ViewModel that catches the Visibility Collapse...but I'm stuck there. Thanks
What I ended up doing is copying the Collection on Collapse...then Clearing. Now I reload the copy when Visibility becomes Visible. Thanks

How do I animate a WPF control bound to a viewmodel property?

I have a WPF app, which uses MVVM. When the users edits data, if certain conditions are met, they will need to fill in revision notes for auditing purposes. If they don't need to, I hide the revision notes textbox to keep the UI clear.
At the moment this is done by binding the Visibility property of the Grid that surrounds the textbox (and its label) to a bool property on the viewmodel. When the bool changes, the revsion notes textbox is hidden or shown as necessary.
This works fine, but the textbox just appears. I would like to animate it, so it grows from zero height to its default, or something similar.
Any idea how I would do this? I have done animation before, but this was always when I manually triggered the animation. In this case, I want to declare the animation in XAML, so it happens automatically when the binding changes.
Anyone able to point me in the right direction?
Just use a DataTrigger to kick off an animation:
<Grid>
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding MyVMBool}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<!-- DoubleAnimation on height or whatever -->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<!-- you could animate close too if you wanted -->
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
Sounds like you solve it with a DataStateBehavior. Here's the MSDN documentation http://msdn.microsoft.com/en-us/library/vstudio/dn195678(v=vs.110).aspx. You could also take a look at the GoToStateAction.

Synchronize WPF ColorAnimation across several controls

I have several toggle-like buttons that I want to pulsate in unison when in the pressed state.
I have defined a style with a trigger that kicks-off the glow animation and this works just fine, apart from the fact that each button pulsates asynchronously from the others.
How can I have each button synchronize its pulse to the others?
Here's the style:
<Storyboard x:Key="pulseStory">
<ColorAnimation
Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)"
From="Red"
To="Transparent"
Duration="0:0:1" />
</Storyboard>
<Style x:Key="pulseButton" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Tag,RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource pulseStory}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Cheers!
OK ... I'll take a stab at this one ...
The WPF framework doesn't have any facility for synchronizing animations that are running concurrently, so you are going to have to come up with a different method. One idea springs to mind ...
Animate some Color property of a hidden UI element within your storyboard, then use UI binding (i.e. ElementName bindings) to connect to the Color of each of your buttons to this hidden UI element.
Actually you should be doing this via a resource, at least using a hidden control is a bit too much of a hack for me personally.
What needs to be met for it to work:
The property you bind to needs to be a DependencyProperty, hence your enveloping object needs to be a DependencyObject.
You have to reference the object as a static resource (as opposed to a dynamic resource) like this:
<DoubleAnimation
Storyboard.Target="{StaticResource AnimationValue}"
Storyboard.TargetProperty="(local:WrappedValue.Value)"
To="0" Duration="0:0:1"/>
Well, admittedly it's a bit hacky as well to have a Wrapper-class for this but it's cleaner than a full control (if you want to use controls you can utilize some unused Tag-property, e.g. of the container that hosts all your controls)

Resources