Silverlight DataTrigger not firing on load
Asked Answered
P

1

7

I'm attempting to convert some of my WPF skills to Silverlight, and have run into a slightly odd problem in the test mini-app I've been working on. In WPF, I got used to using DataTriggers within a style to set up control properties based on properties of the bound data. I discovered that some assemblies related to Blend allow you to do something like this in Silverlight, and I came up with something like this, in which I've got the following namespaces declared:

xmlns:ia="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"  
xmlns:iv="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"


<DataTemplate x:Key="testItemTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBox Text="{Binding Name, Mode=TwoWay}" x:Name="thing"/>                
            <iv:Interaction.Triggers>
                <ia:DataTrigger Binding="{Binding Name}" Value="ReddenMe" Comparison="Equal">
                    <ia:ChangePropertyAction TargetName="thing" PropertyName="Foreground" Value="Red">
                    </ia:ChangePropertyAction>
                </ia:DataTrigger>
            </iv:Interaction.Triggers>
        </StackPanel>
    </DataTemplate>

In this example, I've got a data object implementing INotifyPropertyChanged and raising the PropertyChanged event as usual for the Name property. I get the expected behaviour if I change the value of the textbox and lose focus, but if the initial value of the textbox is set to ReddenMe (which for this contrived example I'm using as the trigger for the text to be red), the text doesn't go red. Does anybody know what's going on here? For DataTriggers in WPF, the trigger would be fired immediately for any data.

I realise that I could use a Converter here, but I can think of situations where I'd want to use triggers, and I wonder if there's anything I could do to make this work.

Piane answered 28/6, 2011 at 14:29 Comment(5)
Hi... tested your dilemma and got the same problem. Sadly this is what happens when, as you, we going down from WPF to Silverlight and below :o( Don't know if a solution exists, but I created a converter to do the job and it worked with the initial values.Oncoming
Thanks for that NestorArturo: yes, after I posted this, I tried to accomplish the same with a converter, and it seemed to do what I was after. It'd be interesting to hear what any WPF/Silverlight gurus think about the merits of separate converters vs purely XAML-based approaches. For things like showing/hiding controls, I've always found the DataTrigger approach to be a bit more transparent, though I suppose you could argue that by having a converter, you end up with a less cluttered markup in the end.Piane
You could be seeing this behaviour because DataTrigger is derived from PropertyChangeTrigger and in your case Name property is not changing initially. Or it could be a bug. You could use .net reflector to peek into the DataTrigger implementation to confirm. I've used DataTriggers before, but I've not run into this issue probably due to the fact that I use visual state manager and start with default state. One way you could try to force desired behaviour is by setting Name value after XAML is loaded.Agram
Ah ok, thanks @Denis. Something I've been trying to get clear recently is how to use the VisualStateManager to respond to changes in data. All the examples I've seen are about responding to things like button pressed events etc, and I haven't really seen anything that talks about binding to the data. Any thoughts on this?Piane
I use data triggers and gotostate actions for this kind of things. In my opinion that is the most MVVM compliant way of doing it. For example: in my view model I have a property that can be set to Loading, Loaded and Failed. In my view I create 3 DataTriggers bound to the property and containing corresponding GoToState action. To make my life easier for cases than property controls many states I've created simple behaviour that binds property directly to the visual state group.Agram
M
11

Here's a solution I found on Tom Peplow's blog: inherit from DataTrigger, and make the trigger evaluate the condition when its associated element is loaded.

Here's how you can code it:

public class DataTriggerEvaluateOnLoad : Microsoft.Expression.Interactivity.Core.DataTrigger
{
    protected override void OnAttached()
    {
        base.OnAttached();
        var element = AssociatedObject as FrameworkElement;
        if (element != null)
        {
            element.Loaded += OnElementLoaded;
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        var element = AssociatedObject as FrameworkElement;
        if (element != null)
        {
            element.Loaded -= OnElementLoaded;
        }
    }

    private void OnElementLoaded(object sender, RoutedEventArgs e)
    {
        EvaluateBindingChange(null);
    }
}
Manouch answered 29/3, 2012 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.