StoryBoard object becomes read-only when set by a Style - c#

I have an attached behavior that has a single attached property of type StoryBoard. I want to set this property on every item in a ListView. The XAML looks something like this:
<Grid>
<Grid.Resources>
<Storyboard x:Key="TheAnimation" x:Shared="False">
<DoubleAnimation From="0.0" To="1.0" Duration="0:0:0.20"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</Grid.Resources>
<ListView>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="local:MyBehavior.Animation"
Value="{StaticResource TheAnimation}" />
</Style>
</ListView.Resources>
</ListView>
</Grid>
So far so good. Then the code in 'MyBehavior' tries to do this:
private static void AnimationChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var listViewItem = d as ListViewItem;
if (d == null)
return;
var sb = e.NewValue as Storyboard;
if (sb == null)
return;
Storyboard.SetTarget(sb, listViewItem);
sb.Begin();
}
But an InvalidOperationException is thrown on the call to StoryBoard.SetTarget(): "Cannot set a property on object 'System.Windows.Media.Animation.Storyboard' because it is in a read-only state." If I inspect the Storyboard in the debugger, I can see that both its IsSealed and IsFrozen properties are set to true.
By contrast, if I set MyBehavior.Animation directly on the ListView so that I don't need to use a Style, the StoryBoard arrives unsealed and I am able to set the target and run it successfully. But that's not where I want it.
Why is my StoryBoard being sealed, and is there anything I can do to prevent this?
Update: I can solve my problem by adding this right after the null check:
if(sb.IsSealed)
sb = sb.Clone();
But I'm still curious what's going on. Apparently something somewhere (Style? Setter?) is freezing/sealing the object in Setter.Value.

I'm far from an expert in WPF, so I cannot explain the finer details of why this was the choice Microsoft made. But as I understand it, the main issue is that the object that is declared as a resource is likely to be shared with multiple other objects. As such, you are prevented from modifying it.
If you still want to go the resources route, it is possible that you can treat the resource as {DynamicResource...} instead of {StaticResource...} and that might allow you to modify the object that's been used for some other object. As I said, I'm not an expert in WPF and I admit to still being a bit cloudy on the different between DynamicResource and StaticResource, but I have a vague recollection that it addresses this scenario. :)

I've done some research on this, and think I've worked out most of what's going on. The short answer is that this behavior is by design. The MSDN Styling and Templating page for .net 4.0 says flat-out that "once a style has been applied, it is sealed and cannot be changed." Comments with Style.IsSealed back this up. But that's the Style itself; I'm dealing with an object contained in a Style's Setter's Value. Well, Style.Seal seals all its Setters, and Setter.Seal seals its Value. With that info in hand (head), none of what happened here is particularly shocking. But there's still no explanation for why all this sealing is being done in the first place. There are claims here and here that it is related to thread safety. That seems reasonable, but I would speculate further that, if all objects that consume a particular Style share a single Style object (and I don't know if that's the case or not), the sealing might be done for the simple reason that you don't want one consumer modifying the Style and accidentally changing everyone else.
All this seems to mean that there is no general solution to the problem, and it will need to be solved on a case-by-case basis. In my case, the solution was simply to clone the Storyboard and then operate on that clone.

Related

WinUI 3 Desktop MVVM; Animate TextBlock on property change?

Hi I'm trying to figure out how to animate a TextBlock when it's property updates. New to WinUI3/XAML but I've found https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/how-to-apply-animations-to-text which gives a lot of examples that work, but they all use TextBlock.Loaded. I've tried changing it to TargetUpdated, SourceUpdated, FrameWork.x, Binding.x etc, all crash so I'm not sure what to put in there. Lot's of example on the net, but ones I can find seem to be either WPF or UWP which still crash.
My code which is pretty much same as MS's examples sans binding:
<TextBlock Text="{x:Bind Path=Viewmodel.UpdatedText, Mode=OneWay}">
<TextBlock.Foreground>
<SolidColorBrush x:Name="MySolidColorBrush" Color="Maroon" />
</TextBlock.Foreground>
<TextBlock.Triggers>
<EventTrigger RoutedEvent="TextBlock.Loaded">
<BeginStoryboard>
<Storyboard>
<ColorAnimation AutoReverse="True" Storyboard.TargetName="MySolidColorBrush" Storyboard.TargetProperty="Color" From="DarkOrange" To="SteelBlue" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
Which does work, when it loads, but I would like to animate it when the text is updated.
One of the examples I found was Animate textblock when the value changes, but it uses .Loaded as well. In the comments it mentions "You have to set the NotifyOnTargetUpdated property to true in the Binding", but NotifyOnTargetUpdated doesn't seem to exist (in WinUI?).
Answer is probably simple but I'm a bit lost on this. I'm also using MVVM pattern so avoiding code behind, but it seems like something this simple shouldn't need it looking at Microsoft's examples? Though If I'm please feel free to correct me. Thanks!
Trying to "avoid code-behind" in cases like this is usually a mistake. View-related logic should be implemented in the view (or control) using a programming language. This does not break the MVVM pattern in any way as the pattern is not about trying to eliminate code from the views in the first place.
Using a markup language such as XAML to implement farily advanced UI logic is generally an antipattern and in the case of UWP and WinUI 3, it's not even possible to use triggers to accomplish what you want.
To quote the ReactiveUI docs, "C# is a much more expressive, more concise language than XAML, and while it may be possible for you to create an entire complex view without writing any code, the result will be an unmaintainable, difficult to read mess."
My suggestion would be to implement the animation programmtically, either directly in the code-behind of the view or as part of an attached behaviour or custom control.

How can I access the implicit (not default) style for a UIElement from code behind?

I am trying to tweak a style for an Infragistics DataRecordCellArea for a XamDataGrid to be largely based on the currently assigned style (via ThemeManager, but potentially on a parent element). Normally, if the theme were being applied in the same way as the system themes, I would do this using something like:
<!-- with xmlns:idp="http://infragistics.com/DataPresenter" -->
<Style
TargetType="{x:Type idp:DataRecordCellArea}"
BasedOn={StaticResource {x:Type idp:DataRecordCellArea}}
>
<!-- Style overrides -->
</Style>
with the style being injected as a resource on the XamDataGrid, in the hope that the implicit style would be picked up based on the BasedOn attribute. However, this isn't what's happening: using this approach, I get a XamlParseException related to StaticResource being unable to find the resource - i.e. there is no style to base things on.
"Great", you might think, "Just get rid of the based on and it'll work." This would be true except that doing so with an empty style clearly DOES affect the control's appearance as a deviation from what the theme provides.
My intended solution is a custom MarkupExtension that takes a named FrameworkElement and a target type and attempts to find the implicit style that WOULD be in use were I to create an instance of the target type as a child of the context-providing object. If that turns out to be the default style (or even a null value), then so be it. This should be safe for sealing when the style starts to be used, too, because I do not need to respond to changes in context, just get the value at the point when the style is constructed. I think.
However, what I can't work out is HOW to retrieve the IMPLICIT style for an element when no explicit value is found for the actual style property. Finding the default style seems easy enough, but since there is no context providable to the Application.Current.FindResource() method, I am convinced this will be the equivalent of leaving the BasedOn attribute blank. Conversely, I expect to get false nulls if I simply get var implicit = Context.Resources.Contains(TargetType) ? Context.Resources[TargetType] : null;, then I expect to only capture styles that are explicit members of that ResourceDictionary and its collection of MergedDictionaries.
There is a possibility of a further complication, too: namely the possibility that the style I actually want to replace may NOT be keyed only with the type. For instance, imagine that the theme may use the same object for multiple purposes, and each of those purposes has a specific key name, with the style then assigned during the ControlTemplate being configured for the parent object, i.e.
<Style TargetType={XamDataGridOrSomeUnknownChildOfXamDataGrid}>
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ControlTemplate.Resources>
<Style TargetType="{TheTypeICareAbout}" x:Key="SomeActualKeyedValue" BasedOn="{StaticResource SomeThemeStyleOrKeyToTheDefaultOne}">
<!-- Style definition -->
</Style>
</ControlTemplate.Resources>
...
<TheTypeICareAbout Style="{StaticResource SomeActualKeyedValue}" />
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
For the time being, I am hoping that this is NOT what is happening, but there is at least some evidence that indicates it might be. If anyone knows of a way to hook onto and override THAT style when the ControlTemplate is rendered, this would also answer my problem - possibly better than finding the implicit style (because then I'm not actually overriding an implicit style).
The approach I ended up taking (ignoring the complicating case for now) was to write a markup extension that took a FrameworkElement for context and a target type. From there, it turns out that all FrameworkElements have a FindResource() method (not just the Application), so it was pretty straightforward to just call FindResource as it pertained to the context object.
The result is xaml that looks something like:
<Style
TargetType={x:Type TheTypeIWant}
BasedOn={BasedOnWithContext Context={x:ref NameOfContextProvidingObject},TargetType={x:Type TheTypeIWant}}
/>
... and C# that looks something like:
public class BasedOnWithContextExtension : MarkupExtension
{
public BasedOnWIthContextExtension()
{
}
public DependencyObject Context {get; set;}
public Type TargetType {get; set;}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this.Context?.FindResource(this.TargetType) as Style;
}
}
(NB the above is an approximation of my solution, since I don't have the code-base to hand, and haven't tried compiling it for now, but it should provide a fair idea as to the approach that worked.)
This evaluates nicely at the point when the style would be created, finds the implicit style, and bases it off that. It doesn't find styles with an unknown (or known) key and replace them though (per the complicating case).

Property Value Animation triggered by a custom DependencyProperty in UserControl?

Question Prelude :
How can I animate the Angle Property of a RotateTransform of
an UIElement A when the value of a Custom DependencyProperty
of type boolean becomes True when I click on an
UIElement B, all inside an UserControl ?
And in XAML ONLY (or mostly) ? if possible :)
I've written all the following to provide all the required details of my issue. You can stop reading from top to bottom anytime; even directly jump to the actual question, which is within the first quarter of the post.
Context :
The question is about Animation Triggers and Custom Property Binding, all within a single UserControl. No Window involved so far.
To begin with, let's assume I created an UserControl, which has a main Grid that contains two other Grids. Simpliest schemas :
<!-- MyControl.xaml -->
<UserControl ...blahblahblah>
<Grid>
<Grid x:Name="TiltingGrid">
<!-- This Grid contains UIElements that I want to tilt alltogether -->
</Grid>
<Grid>
<Ellipse x:Name="TiltingTrigger" ...blahblahblah>
<!-- This Ellipse is my "click-able" area -->
</Ellipse>
</Grid>
</Grid>
</UserControl>
Then, in Code Behind, I have a DependencyProperty called IsTilting.
// MyControl.xaml.cs
public bool IsTilting
{
// Default value is : false
get { return (bool)this.GetValue(IsTiltingProperty); }
set { this.SetValue(IsTiltingProperty, value); }
}
private static readonly DependencyProperty IsTiltingProperty =
DependencyProperty.Register(
"IsTilting",
typeof(bool),
typeof(MyControl),
new FrameworkPropertyMetadata(
false,
new PropertyChangedCallback(OnIsTiltingPropertyChanged)));
private static void OnIsTiltingPropertyChanged(...) { ... }
// .. is a classic Callback which calls
// private void OnIsTiltingChanged((bool)e.NewValue)
// and/or
// protected virtual void OnIsTiltingChanged(e) ...
Then, I defined some Properties for my Grid named TiltingGrid in the XAML :
<Grid x:Name="TiltingGrid"
RenderTransformOrigin="0.3, 0.5">
<Grid.RenderTransform>
<RotateTransform
x:Name="TiltRotate" Angle="0.0" />
<!-- Angle is the Property I want to animate... -->
</Grid.RenderTransform>
<!-- This Grid contains UIElements -->
<Path ... />
<Path ... />
<Ellipse ... />
</Grid>
And I would like to trigger the tilting upon clicking on a specific area inside this UserControl : An Ellipse, in the secund Grid :
<Grid>
<Ellipse x:Name="TiltingTrigger"
... Fill and Stroke goes here ...
MouseLeftButtonDown="TryTilt_MouseLeftButtonDown"
MouseLeftButtonUp="TryTilt_MouseLeftButtonUp">
</Ellipse>
</Grid>
If I'm not mistaken, Ellipse doesn't have a Click Event, so I had to create two EventHandlers for MouseLeftButtonDown and MouseLeftButtonUp. I had to do it that way to be able to :
Make the Ellipse capture Mouse upon MouseLeftButtonDown, and set a private field to true
Test whether the Mouse Point is inside the Ellipse upon MouseLeftButtonUp, set the value of the private field to false, then Release the Mouse.
Invert the value of the DependencyProperty IsTilting (true/false) if something looking like a "Click" occurs (..which would trigger the tilting animation if I'm able to resolve the appropriate Binding..)
I'll save you the MouseLeftDown/Up code, but I can provide it if required. What they do is to change the value of the DP.
Issue(s) :
I don't know how to trigger the Angle Animation when my DependencyProperty is updated. Well. That's not an actual issue, it's a lack of knowledge I reckon :
I don't know how to capture a custom event to be used with <EventTrigger>
I don't know how and where to trigger a StoryBoard using a True/False DependencyProperty.
And the actual question is :
From now on, how do I declare the code that makes the Angle
Property of the RotateTransform to animate from 0.0 to
45.0 (Rendering Transform of my Grid "TiltingGrid") when my DP IsTilting is set to true, and animate back to 0.0
when it's False ?
mostly in XAML way ..?
I do have a working code in C# code behind (detailed below) What I'm looking for is a workable solution in XAML (because it's usually very easy to rewrite almost anything in CodeBehind when you know how to do it in XAML)
What I tried so far...
From now on, you don't have to read further unless you absolutely want to know all the details...
1) Triggering the animation using natively defined Ellipse EventTriggers works only for Events defined for this specific UIElement (Enter/Leave/MouseLeftDown...) Done that alot with many UIElements.
But those triggers are not the ones I need : My Grid should tilt based on an On/Off or True/False custom state in a DP, not when something like a Mouse activity occurs.
<Ellipse.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="TiltRotate"
Storyboard.TargetProperty="Angle"
From="0.0" To="45.0"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
...
</Ellipse.Triggers>
When the mouse enters the Ellipse, my Grid is tilting accordingly, but hence, How do I have access to custom Events defined in my UserControl ?
2) Then, based on the above scheme, I supposed I just had to create a Routed Event on my MyControl Class, or two, actually :
TiltingActivated
TiltingDisabled
.
public static readonly RoutedEvent TiltingActivatedEvent =
EventManager.RegisterRoutedEvent(
"TiltingActivated",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(EventHandler));
public event RoutedEventHandler TiltingActivated
{
add { AddHandler(MyControl.TiltingActivatedEvent, value); }
remove { RemoveHandler(MyControl.TiltingActivatedEvent, value); }
}
private void RaiseTiltingActivatedEvent()
{
RoutedEventArgs newEventArgs =
new RoutedEventArgs(MyControl.TiltingActivatedEvent, this);
RaiseEvent(newEventArgs);
}
Then, I'm calling RaiseTiltingActivatedEvent() in one method called by my IsTilting DependencyProperty Callback when its new value is true, and RaiseTiltingDisabledEvent() when its new value is false.
Note : IsTilting value is changed to either true of false upon Ellipse "Click", and the two events are fired accordingly. But there's a problem : it's not the Ellipse that fires the Events, but the UserControl itself.
Anyway, I tried to replace the <EventTrigger RoutedEvent="UIElement.MouseEnter"> with the followings :
Attempt one :
<EventTrigger RoutedEvent="
{Binding ic:MyControl.TiltingActivated,
ElementName=ThisUserControl}">
.. and I get :
"System.Windows.Markup.XamlParseException: (...)"
"A 'Binding' can only be set on a DependencyProperty of a DependencyObject."
I'm assuming I cannot bind to an Event ?
Attempt two :
<EventTrigger RoutedEvent="ic:MyControl.TiltingActivated">
.. and I get :
"System.NotSupportedException:"
"cannot convert RoutedEventConverter from system.string"
I'm assuming the RoutedEvent name cannot be resolved ? Anyway, this approach make me drift far from my initial goal : Trigger a DoubleAnimation when a custom Property changes (because in more complex scenarios, wouldn't it be easier to trigger different animations and call specific methods, all in CodeBehind when we can have dozens of different values, than creating lengthy and tricky XAML things ? Best would be learning how to do both of course. I'm eager to know)
3) Then I came across this article : Beginner's WPF Animation Tutorial.
A Code Behind Animation Creation. That's the thing I wanted to learn after knowing how to do it in XAML. Anyway, let's have a try.
a) Create two Animation Properties (private), one for tilting animate and another for tilting animate back.
private DoubleAnimation p_TiltingPlay = null;
private DoubleAnimation TiltingPlay
{
get {
if (p_TiltingPlay == null) {
p_TiltingPlay =
new DoubleAnimation(
0.0, 45.0, new Duration(TimeSpan.FromSeconds(0.2)));
}
return p_TiltingPlay;
}
}
// Similar thing for TiltingReverse Property...
b) Subscribe to the two events then set the Angle Animation of our RotateTransform live at runtime in code behind :
private void MyControl_TiltingActivated(object source, EventArgs e)
{
TiltRotate.BeginAnimation(
RotateTransform.AngleProperty, TiltingPlay);
}
// Same thing for MyControl_TiltingDisabled(...)
// Subscribe to the two events in constructor...
public MyControl()
{
InitializeComponent();
this.TiltingActivated +=
new RoutedEventHandler(MyControl_TiltingActivated);
this.TiltingDisabled +=
new RoutedEventHandler(MyControl_TiltingDisabled);
}
Basically, when I "click" (MouseButtonLeftDown + Up) on the Ellipse :
Mouse hit spot is resolved
if within the Ellipse area, change DP IsTilting to not IsTilting.
IsTilting then fires either TiltingActivated or TiltingDisabled.
Both are captured, then the related tilting animation (private properties) is activated on the named <RotateTransform ..> of the Grid.
And it works !!!
I said it would be very easy in code behind ! (lengthy code .. yes, but it works) Hopefully, with snippets templates, it's not that boring.
But I still don't know how to do it in XAML. :/
4) Since my custom events seems to be out of scope in the XAML side, what about <Style> ? Usually, binding in a Style is like breathing. But honestly, I don't know where to begin.
the animation target is the Angle Property of a <RotateTransform /> applied to a Grid.
the binded Dep. Property IsTilting is a custom DP of MyControl, not UserControl.
and one Ellipse drives the updating of the DP.
let's try something like <RotateTransform.Style>
<RotateTransform ...>
<RotateTransform.st...>
</RotateTransform>
<!-- such thing does not exists -->
or RotateTransform.Triggers ? ... doesn't exist either.
UPDATE :
This approach works by declaring the Style in the Grid to animate, as explained in Clemens's answer. To resolve the custom
UserControl Property binding, I just had to use
RelativeSource={RelativeSource AncestorType=UserControl}}. And to
"target" the Angle Property of the RotateTransform, I just had to use
RenderTransform.Angle.
What else ?
I often see samples that sets the DataContext to something like "self". I don't really understand what's a DataContext, but I'm assuming it makes all Path resolving point to the declared Class by default, for Bindings. I already used that in one UserControl which solved my issue, but I didn't dig deeper to understand the how and why. Perhaps this could help resolve capturing custom Events in code behind directly from the XAML side ?
One XAML mostly way I'm nearly sure will work is :
to create a custom UserControl for that Ellipse, say, EllipseButton, with its own Events and Properties
then, embed that in MyControl UserControl.
Capture the TiltingActivated Event of the EllipseButton to trigger the DoubleAnimation in a Storyboard of the EllipseButton, just like it could be done for the Click event of a Button.
That would work fine, but I find it hacky to create and embed another control just to be able to access the appropriate custom event. MyControl is not a SuperWonderfulMegaTop project that would require such surgery. I'm sure I'm missing something soooooooo obvious; can't believe something that simple outside the WPF world can't be even simplier in WPF.
Anyway, such cross-connections are highly subject to memory leaks (perhaps not the case here, but I try to avoid that whenever possible...)
Perhaps defining <Grid.Style> or alike would do the trick ... but I don't know how. I only know how to use <Setter>. I don't know how to create EventTriggers in a Style declaration. UPDATE : Explained by Clemens's answer.
This SO question (Fire trigger in UserControl based on DependencyProperty) suggests to create a Style in UserControl.Resources. Tried the following... It doesn't work (and there is no animation there anyway - I don't know how to declare animation in Style yet)
.
<Style TargetType="RotateTransform">
<Style.Triggers>
<DataTrigger
Binding="{Binding IsTilting, ElementName=ThisUserControl}" Value="True">
<Setter Property="Angle" Value="45.0" />
</DataTrigger>
</Style.Triggers>
</Style>
This SO question (Binding on RotateTransform Angle in DataTemplate not taking effect) has a lot of unknown knowledge to me to be understandable. However, assuming the suggested workaround works, I don't see anywhere something looking like an animation. Just a binding to a value that is not animated. I don't think the Angle animates itself magically.
In Code Behind like the working code above, I could create another DependencyProperty called GridAngle (double), then bind the Angle Property of RotateTransform to that new DP, then animate that DP directly ??? Worth a try, but at a later time : I'm tired.
Just found that my Registered Events are of Bubble Strategy. This would matter if the Event is to be captured by some parent containers, but I want to handle everything directly inside the UserControl, not like on this SO question. However, Tunneling strategy - that I don't understand yet - may play a role : would Tunneling allows my Ellipse to capture the Events of my UserControl ? Have to read the documentation again and again because it's still very obscure to me... What bugs me now is that I am still unable to use my custom events in this UserControl :/
What about a CommandBinding ? That seems very interresting, but it's a whole different chapter to learn. It seems to involve a lot of code behind, and since I already have a working code behind (which looks more readable to me...)
In this SO question (WPF Data Triggers and Story Boards), the accepted answer seems to only work if I'm animating a property of an UI Element that can have a UIElement.Style definition. RotateTransform doesn't have such ability.
Another answer suggest the use of ContentControl, ControlTemplate... Just like CommandBinding above, I haven't dig deep enough to understand how I could adapt that to my UserControl.
However, those answers seems the ones that mostly fit my needs, expecially that ContentControl way. I'll have some tries at a later time, and see if it solves the XAML mostly way of implementing the desired behaviour. :)
And last, this SO question (EventTrigger bind to event from DataContext) suggest the use of Blend/Interactivity. The approach looks nice, but I don't have Blend SDK and not really willing to unless I absolutely have to... Again : another whole Chapter to eat... :/
Side note :
As you would have guessed, I'm a beginner in WPF/XAML (I know it's not an excuse) which I started to learn a few weeks ago. I'm kind of "the whole stuff would be very easy to do in WinForms right now..." but perhaps you could help me figure out how easy it would be to achieve it in WPF :)
I've searched alot (I know it's not an excuse either) but I have no luck for this time. - Okay, I've just read three dozens of articles, code projects and SO topics, and the MSDN documentation about triggers, animations, routed events.. just seems to polish the surface without digging deep in the core (seems like MS think inheriting from Button is the way to solve almost anything...)
Long question, short answer. Use Visual States:
<UserControl ...>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="TiltedState">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="TiltingGrid"
Storyboard.TargetProperty="RenderTransform.Angle"
To="45" Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="TiltingGrid" RenderTransformOrigin="0.3, 0.5">
<Grid.RenderTransform>
<RotateTransform/>
</Grid.RenderTransform>
...
</Grid>
</Grid>
</UserControl>
Whenever an appropriate condition is met, call
VisualStateManager.GoToState(this, "TiltedState", true);
in the UserControl's code behind. This may of course also be called in the PropertyChangedCallback of a dependency property.
Without using Visual States, you might create a Style for your TiltingGrid which uses a DataTrigger with a Binding to your UserControl's IsTilted property:
<Grid x:Name="TiltingGrid" RenderTransformOrigin="0.3, 0.5">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding IsTilted,
RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.Angle"
To="45" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="RenderTransform.Angle"
To="0" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.RenderTransform>
<RotateTransform/>
</Grid.RenderTransform>
...
</Grid>

Accessing a given child of a ControlTemplate programmatically

I have this ControlTemplate that contains stuff (gradients in this case) that I want to be able to access programmatically and after hours of trial and error I feel that it's finally time to turn to you for assistance, StackOverflow.
The template generates a flower, and I didn't know what to use, so I just picked the Thumb-element, since I've used that before in a similar manner. If you can think of anything else that would be better suited, please let me know.
Anyways, this is the beginning of my ControlTemplate, from the XAML-file:
<ControlTemplate x:Key="cherryFlowerStyle" TargetType="{x:Type Thumb}">
<Viewbox Width="119.560" Height="114.268" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="119.560" Height="114.268">
<Canvas>
<!-- Layer 1/<Path> -->
<Path Data="... (removed to save space) ...">
<Path.Fill>
<RadialGradientBrush x:Name="cherryFlowerColorGradient" MappingMode="Absolute" GradientOrigin="593.380,333.416" Center="593.380,333.416" RadiusX="36.460" RadiusY="36.460">
<RadialGradientBrush.GradientStops>
<!-- Flower color -->
<GradientStop x:Name="cherryFlowerColorGradientOuterColor" Offset="0.15" Color="#ffd6e062"/>
And here's what I'm doing in C#:
Thumb flower = new Thumb();
flower.Template = TryFindResource("cherryFlowerStyle") as ControlTemplate;
GradientStop grStop = (GradientStop)flower.Template.FindName("cherryFlowerColorGradientOuterColor", flower);
Console.WriteLine("gradient: " +grStop);
Creating a new Thumb and applying the template works (it's drawn as a flower on the canvas).
Trying to access the gradients inside the template, however, does not work. I hope there's a good solution to this, or else I have to do it the ugly way; create a flower off-screen (in XAML) and reference that in the code-behind, because that works :/
Thanks in advance!
That won't work, because the Gradient is not a child of your template, its the value of the property of a child of your template. So you can access the Path by name, and modify its Fill value. But remember, the Gradient might be frozen.
Why not use binding and the DataContext for that? Or yet better, use dependency properties to modify the colors directly and just use TemplateBinding. It would be a much much better way to handle that, with less work. Also the Thumb element is used for moving elements and it encapsulates the mouse handling for that, if you don't need that the Control element would be a better base class. If you want performance, Visual or ContainerVisual would be much better, but also very limited.

XAML - ListViewItem - IsEnabled Binding

Please put me out of my misery:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsEnabled" Value="{Binding MyBoolField}" />
</Style>
</ListView.ItemContainerStyle>
Where MyBoolField is a property available on each item in the ListView's assigned ItemsSource, and is of course of type bool.
The desired behaviour is that the IsEnabled property of each ListViewItem is bound to the MyBoolField property of the object it contains (an element from the ListView's ItemsSource). The example above however pays no attention to this binding and is always true.
If I set the value to false statically, the item becomes disabled as expected.
I can only assume this is an issue of scoping, or a restriction on the use of bindings in this particular scenario, however I am unable to find any documentation to support this.
Perhaps it is useful to know that bindings set up in the DataTemplate assigned to this ListView's ItemTemplate all work okay, so the problem is hopefully not that fundamental/stupid.
Points from the Comments
There is no relevant output in the debug 'Output' window.
Binding the MyBoolField property elsewhere works fine, as such the underlying data source is providing a Boolean value correctly and this seems to be solely an issue of binding it in this context.
Ok, reading this post it seems that its a known limitation of WinRT, maybe you find something useful there.
One thing i would also try, because the OP in that post said something regarding that is. Using a Self binding, and use Path=DataContext.MyBoolField
Further reading, this is a limitation from Silverlight, which WinRT was build upon. So this is a workaround which should also work for WinRT

Categories

Resources