UserControl as ListBoxItem and IsSelected - wpf

I have a usercontrol that I want to use as a ListBoxItem.
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyUserControl/>
</DataTemplate>
</ListBox.ItemTemplate>
I'd like to play a storyboard when the usercontrol is unselected.
<UserControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource OnMouseLeaveSB}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
But the storyboard is never firing. Is there a better way to do this?
Edited to Add:
What I am really trying to accomplish this this:
When the mouse is over the UserControl, I want to play a storyboard (OnMouseEnterSB). When the mouse leaves the UserControl, I want to play another storyboard (OnMouseLeaveSB). I have all this working fine.
When the UserControl is selected, however, and the mouse leaves, I do NOT want to play the storyboard.
Finally, when the UserControl is unselected, I want to play the OnMouseLeaveSB storyboard.

I don't have WPF experience rather I am a Silverlgiht girl and in Silverlight the thing you are describing is called "VisualStateManager" (just binged it, it is also available in WPF).
With VSM you would define different visual appearances for each "state" of your (user)control (mouseover, mouseleft, normal) and also depending on the previous and/or next state you can define different transitions between those states (or you may use a default transition for moving between all different states).
Read this blog-post by Tim Heuer. Describes it well with many screenshots :). You may also want to check out this link.
Using VSM the states and animations are a part of the control not the application with bunch of event-handlers and animation.Begin() calls. I really like and recommend it :)

If i understand your question correctly, you want to play this animation 'OnMouseLeaveSB' when any ListViewItem loses selection. But in your trigger you're playing the animation for all the unselected items. Hence even if this works, it will not be the one you wanted.
Reason why the storyboard does not fire is that the default BlueHighlight hides your animation. A hack to get rid of this would be to set the border color which is explained here www.HereIsYourLink.com
To achieve what you want, you'll have to insert your storyboard in Trigger.ExitActions with the IsSelected value 'True'.
If you're not in a hurry, take a look at VSM too.

Related

How can I stop a WPF control inheriting an effect from its parent?

I have a WPF UserControl with many child controls. The root control is a Grid. I have a need to apply a BlurEffect to all the controls if a button within the control is clicked. I have solved this using a style on the main grid which is activated through databinding.
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBlurred, Mode=OneWay}" Value="True">
<Setter Property="Effect">
<Setter.Value>
<BlurEffect Radius="75" KernelType="Gaussian"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
And the button that triggers it:
<Button x:Name="btnBlur" Style="{StaticResource ScanButton}" Margin="30,0,0,0" Width="150" Command="{Binding BlurCommand}">
<TextBlock Text="Blur" VerticalAlignment="Center" Margin="10,0,0,0"/>
</Button>
My issue is that the blur button itself also has the blur effect applied, because it is a child element within the entire grid. The button also already has a style applied to it, which affects other buttons on the same control.
Is there a way to prevent the blur effect from being applied on this single button while still having it applied on all other child components of the grid?
Update: I am using an MVVM pattern for the application, and my ViewModel knows absolutely nothing about the view itself, so I cannot reference the particular button through code. Ideally, I would like a way to not apply or "unapply" the blur effect just to that particular button.
I have attempted to add the same style trigger to my button, and set the Effect property to null when databinding is triggered. This does not work, and it seems the root grid's effect overrides anything done at the child control level.
Based on the comments posted, it seems there is no way to override or avoid the blur effect on a child control.
As a solution, I just copied-pasta'd the data trigger code to all the different parent containers and controls so that it wouldn't affect the particular button. It's certainly not elegant, as the same trigger code appears multiple times throughout the Xaml.
I am unsure if I can put the code into a separate style and still reference/use the databinding that triggers the effect, but it's perhaps something I will attempt when I have more time. For now, as ugly as the solution is, it works.

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.

Is it possible to Animate a color change on multiple textblocks at once?

I've to a Windows Phone 7 app with a page and a StackPanel on that page.
The StackPanel contains several TextBlock elements.
I need to animate the color of ALL the TextBlock elements the same way.
Unfortunately, when I set up a ColorAnimationUsingKeyFrames, I can only specify a single TargetName. Since there are multiple TextBlock controls that need to be animated the same way, that's pretty inconvenient, and I suspect there has to be a better way to handle this that copy pasting n ColorAnimation definitions, one for each textblock to animate.
Any ideas how to set up a color animation to apply to multiple controls at once?
EDIT: I realize this is a WP7 question, but I've tagged it WPF, since I believe the same technique would apply to both, but feel free to correct me if I'm wrong.
You should be able to target your animation at the property of a single element, then use ElementName binding to synchronize values between TextBlocks. or example:
<TextBlock x:Name="textOne" Text="One"/>
<TextBlock x:Name="textTwo" Text="Two"
Background="{Binding Background, ElementName=textOne}"/>
In the above XAML the background of one TextBlock is bound to the other. If your storyboard targets 'textOne', then the other TextBlock will animate also.
I would try to do this symmetrically, externalize the brush and use it in all respective places, something like this:
<StackPanel.Resources>
<SolidColorBrush x:Key="TBBackground" Color="White"/>
<Style TargetType="TextBlock">
<Setter Property="Background" Value="{StaticResource TBBackground}"/>
</Style>
<StackPanel.Resources>
<!- ... -->
<Storyboard>
<ColorAnimation Storyboard.Target="{StaticResource TBBackground}"
Storyboard.TargetProperty="Color"
To="Red"/>
</Storyboard>
Edit: Tested it and there's some problem with immutability, possibly would need to wrap the colour otherwise, in Silverlight that may be connected with some trouble.
Use a storyboard to change your textblock. You can style your textblocks with a storyboaed and then execute the storyboard when needed.

WPF Animation "Cannot freeze this Storyboard timeline tree for use across threads"

I currently have a listbox that has its selected item bound to a property on my ViewModel. Whenever the selected item isn't null I want to perform an animation on it. However I keep getting the following error "Cannot freeze this Storyboard timeline tree for use across threads" and from research sort of understand why this is happening. However I am unsure of what approach I need to take to get the behavior I want.
<Storyboard x:Key="ShowItemEdit">
<DoubleAnimation
Storyboard.TargetName="lstItemList"
Storyboard.TargetProperty="ListBox.Width"
To="{Binding ActualWidth, ElementName=UserControl}"
Duration="0:0:0.40" />
...
</Storyboard>
<Style x:Key="ListStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, Converter={StaticResource IsNullConverter}}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource ShowItemEdit}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
<ListBox x:Name="lstItemList" Style={StaticResource ListStyle}" SelectedItem="{Binding SelectedItem}">
...
</ListBox>
Can you post your Storyboard? It sounds like you have some kind of Binding in the Storyboard definition.
Ok so, as I suspected, it's because you're using a Binding in your Storyboard. You can't do this because WPF attempts to freeze all the resources leveraged by a template for efficiency and when you use a Binding on a Freezable, in this case the Storyboard, it prevents it from being able to be frozen.
There is a technique that you can use to get around the Freezable issue that allows you to use a binding for the "To" value of your animation (rather than hard-coding a value there). Its pretty straightforward and I've outlined it here.
Old question but might be useful for other people.
Sometimes creating the Storyboard in the code-behind can be simpler: https://stackoverflow.com/a/10848781/779521

WPF databind Image.Source in MVVM

I'm using MVVM and am trying to databind the Source property of Image to my ViewModel in such a way that I can change the icon on the fly. What is the best pattern to follow for this? I still have the flexibility to change my ViewModel to suit, but I don't know where to start in either the xaml or ViewModel.
To be clear, I don't want my ViewModel to know about the specific images (that's for the View to know), just the state that triggers different images. For now I have just two states, lets say Red and Green. Should I create an Enum property or a bool? And then how do I databind to switch the image source?
You can use a DataTrigger, and change the image (entirely in XAML) based on the value of a property in your ViewModel. I, personally, would use an enum, since you may want multiple states.
VisualStateManager will work for this as well, but will require WPF Futures or .NET 4.
In order to use a DataTrigger, you can do something like:
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="1.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding ViewModelEnumProperty}" Value="Image2">
<Setter Property="Source" Value="2.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
This will use "1.png", but when your enum is set to "Image2" in the VM, it'd switch to 2.png. More DataTriggers can be added as needed.

Resources