I'm really new to WPF, struggling to get my head around some stuff to do with styles. Firstly, the style doesn't throw any errors anywhere, although i'm still not sure if it's completely correct...so here's my XAML for the style;
edit: The style should allow my custom usercontrol to fade in using a Storyboard and DoubleAnimation
<Application.Resources>
<Style TargetType="{x:Type UserControl}" x:Key="UCStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding Visibility}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="UserControl.Opacity" From="0.0" To="1.0" Duration="0:0:3">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Application.Resources>
From here, i'm using a checkbox to add a usercontrol to a canvas' children, as so;
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
UserControlLibrary.UserControl1 userControl = new UserControlLibrary.UserControl1();
userControl.Style = Resources["UCStyle"] as Style;
canvas1.Children.Add(userControl);
}
I read a around on SO, and found a few answers suggesting this is how you apply a style programatically, referencing the key as a style. However, when i run this, the usercontrol doesn't appear to fade in (as the styling would suggest).
Am i missing something vital here? or just being a little silly?
edit 2:
i've modified my checkbox checked event to set visibility to hidden after adding it to canvas, and a new button with a click event that sets visibility to visible, but sadly it didn't fix my problem.
You're using a DataTrigger that is binding to UserControl.DataContext.Visibility, however I'm fairly sure you want to bind to UserControl.Visibility instead, so you need to use a different binding type.
All bindings by default reference the DataContext, so to reference something other than the DataContext, you need to specify a different source for your binding, such as a RelativeSource binding to Self
<DataTrigger Binding="{Binding Visibility, RelativeSource={RelativeSource Self}}" Value="Visible">
As an alternative, you can use a regular Trigger instead of a DataTrigger, which should base the trigger off a Property of the UserControl instead of trying to find the property through a binding.
<Trigger Property="Visibility" Value="Visible">
Related
I am trying to understand how animation works in WPF with StoryBoards.
So far I have managed the following :
creating a ListView with custom items and resources binding ;
using ItemContainerStyle in order to make sure each item takes all the width of the ListView ;
using EventTrigger to trigger specific animation
My problem is simple.
First I tried the Loaded trigger to animate an item whenever it is added to the ListView's binded ObservableCollection. It works fine with opacityproperty but the problem is that this event is triggered everytime I scroll, meaning Windows loads and unloads items on-the-go to save up memory (which is understable but uses a lot more CPU with complex item).
So it's a no go.
Furthermore, the Unloaded trigger "does not really work" and was solved here : https://stackoverflow.com/a/14619637/3535408
So, using the aforementioned solution, I would like to fire the animation manually.
To be more precise, whenever a specific property of my items (much like Removing) is changed, I'd like a StoryBoard to be started.
How to accomplish this for sliding animation ? Because I would really like to have my items to slide, on the X axis, in and out (entering via the left, and leaving via the left) of my ListView.
These sliding animations must be triggered only when an item is added to the binded ObservableCollection and when it is removed.
The following XAML code does not work :
Within a ListView > ListView.ItemTemplate > DataTemplate > DataTemplate.Triggers
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Removing}" Value="true" >
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard >
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="LayoutTransform.ScaleY" From="0" Duration="0:0:.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</DataTemplate.Triggers>
The opacity animation really works just fine. However the LayoutTransform produces the following System.InvalidOperationException : Cannot resolve all property references in the property path 'LayoutTransform.ScaleY'. Verify that applicable objects support the properties.
Moreover, I just do not know how to do a sliding animation on the X axis :/
Here is the content of my ListView'sItemContainerStyle :
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
<Setter Property="Margin" Value="0, -2, 0, -2" ></Setter>
<Setter Property="LayoutTransform">
<Setter.Value>
<ScaleTransform x:Name="transform" />
</Setter.Value>
</Setter>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
Thanks for the help
I have a label control and style defined in XAML:
<Style x:Key="EditModeEditedHiddenTemplate" TargetType="Control">
<Setter Property="Background" Value="DarkOrange" />
<Setter Property="Visibility" Value="Visible" />
</Style>
... other stuff ...
<Label Grid.Column="0" Grid.Row="1" Name="SomeName" Visibility="Hidden">Some content</Label>
Then, in code behind based on user input, i apply the style to the control like this:
var editModelControlStyle = new Style(control.GetType());
foreach (var setter in editModeStyleTemplate.Setters)
{
editModelControlStyle.Setters.Add(setter);
}
editModelControlStyle.BasedOn = control.Style;
control.Style = editModelControlStyle;
With most dependency properties this work fine, such as Background. However, this does not work when it comes to visibility, the control is still invisible.
Can you please help me understand why this is and how to solve it?
If you have set the Visibility property inline like this:
<Label Name="SomeName" Visibility="Hidden" Content="Something" />
... then this will mean that this specified value will override any value that is set in an applied Style.
<Style x:Key="EditModeEditedHiddenTemplate" TargetType="Control">
<Setter Property="Background" Value="DarkOrange" />
<Setter Property="Visibility" Value="Visible" /><!-- No effect-->
</Style>
DependencyPropertys can be set from many different sources, such as Styles, Animations and inline to name but a few, and as such, they have a particular order of precedence, which specifies which source should override other sources. You can find out more about this in the Dependency Property Setting Precedence List section of the Dependency Property Value Precedence page on MSDN.
From that list, you can see that only Animations and Property system coercion can override an inline property value. Therefore if you can't remove the Visibility="Hidden" from the XAML, you can only set the Visibility property to Visible in an animation, or through property coercion. However, these would both require a change to the XAML in order to work. This is how you could change the Visibility value in an Animation:
<Label Name="SomeName" Visibility="Hidden" Content="Something">
<Label.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard BeginTime="0:0:1">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Label.Triggers>
</Label>
I set the Storyboard.BeginTime to 1 second so that you could see the text appear, but equally, this could be set to 0 seconds to happen as soon as it is loaded. Property coercion would require that you data bind a property to the Visibility property:
<Label Name="SomeName" Visibility="{Binding SomeProperty}" Content="Something" />
However, if you cannot change the XAML on the Label, then you have no way to make the Visibility value Visible in XAML alone. It would be possible to do this in code though... you just need some condition in which to trigger the change. In this example, I have just added a click handler to the Grid to gain access to the code behind:
<Grid PreviewMouseDown="OnPreviewMouseDown" Background="Transparent">
<Label Name="SomeName" Visibility="Hidden" Content="Something" />
</Grid>
...
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
SomeName.Visibility = Visibility.Visible;
}
However, you would still need to change the XAML to do even this. Perhaps you could attach a handler in code instead?
At the moment, I'm doing some experimentation. My current scenario: I have a StoryBoard to transition between 2 UserControls (for example, it shrinks the current UserControl then grows the other).
What I want to do is to have 2 UserControls defined as part of the XAML, with keys of "Current" and "Next". The Current should be bound to the currently viewed UserControl. The Next should be bound before transition, so the StoryBoard knows which element to transition to. Here's where I'm stuck: ENTIRELY using XAML, how would one go about this?
I have a simple StoryBoard that is a Resource of the ItemsControl, as well as two UserControl items:
<ItemsControl.Resources>
<Storyboard x:Key="TransitionStoryboard">
<!-- Shrink this one. -->
<DoubleAnimation Storyboard.Target="{Binding Current}" Storyboard.TargetProperty="Width" To="0" Duration="0:0:1"/>
<!--Grow the next.-->
<DoubleAnimation Storyboard.Target="{Binding Next}" Storyboard.TargetProperty="Width" To="100" BeginTime="0:0:1" Duration="0:0:1"/>
</Storyboard>
<UserControl x:Key="Current"/>
<UserControl x:Key="Next" Width="0"/>
</ItemsControl.Resources>
So, when I define a new UserControl that belongs to the ItemsControl (like this):
<my:Control1 x:Name="ControlOne"/>
How do I set the "Current" UserControl top be ControlOne? Then, when I want to transition, how do I set one as "Next"? And how can I change these after a trigger?
Thanks
This is a total mess. You seem to completely misunderstand on how the static resources are used.
To achieve what you are trying to do, you should first decide on what will trigger the animation. Ideally it should be some DependencyProperty on your controls, or a property on your view model (which implements INotifyPropertyChanged). For example, you can declare an IsSelected property. Then you should create a style which triggers "grow" animation when control is selected and triggers "shrink" animation, when control loses selection.
For example:
<Style TargetType="Control" x:Key="FancyStyle">
<Style.Triggers>
<DataTrigger Binding={Binding IsSelected} Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource YourGrowAnim}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource YourShrinkAnim}"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
Then you should assign that style to every control, you want animated this way and set up transitions between IsSelected properties. You can also use EventTrigger instead and bind animations to events (for example you can trigger those animations when control gets/loses focus).
You should also fix your "grow" animation, it wont work, most likely.
I want to make my gridsplitters in a custom user control have visibility collapsed
based on when the parent control has loaded some data using a load button? I thought that the way to do this would be to create a property dataloaded on the parent control and then set a trigger in the triggers of the usercontrol like below:
but I can't seem to get it to reference the property of the usercontrol (graphviewer).
Also, can property triggers reference other controls within the control like I did below? I am assuming either my syntax is wrong or what I am trying to do is not possible. So Far I have only messed with a few basic properties within trigger templates when making modifications to control templates, so I don't really know whether what I am trying to do is possible.
<UserControl.Triggers>
<Trigger Property="GraphViewer.DataLoaded" Value="true">
<Setter Property="SignalNameGridSplitter.Visibility" Value="Visible" />
</Trigger>
</UserControl.Triggers>
Try this... first add the XML Namespace of your GraphViewer control - something like this:
xmlns:YourXmlNamespace="clr-namespace:YourApplicationName.FolderNameIfApplicable"
Then add this into a Style... it has to be in a Style.Triggers collection because you can't use a DataTrigger in a UserControl.Triggers collection:
<UserControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding DataLoaded, RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type YourXmlNamespace:GraphViewer}}}" Value="True">
<Setter Property="SignalNameGridSplitter.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
I got my animation to work triggered by a property in my ViewModel. If I set my TargetProperty to Width, the below code actually works in growing the image.
Next, I wanted to actually move the image up and down. To do this, I added a Canvas component around my image, to be able to animate based on Canvas.Top property. Setting Canvas.Top on the image moves it where I want.
However, if I set my StoryBoard.TargetProperty to Canvas.Top, I get an error:
Cannot resolve all property references in the property path
Canvas.Top.
<Style x:Key="LoadingImageAnimation" TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="10" To="250" AutoReverse="True" Duration="0:0:30"
Storyboard.TargetProperty="Canvas.Top"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Is my approach totally off, or just a matter of finding the Attached Property?
Did some digging around on Property Path Syntax, and the solution was actually simple. Needed to add parentheses:
Storyboard.TargetProperty="(Canvas.Top)"
The animation is not as smooth as i would like.. but at least it works now.