Archive

Posts Tagged ‘Triggers’

Using Behaviors and Triggers in Xaml

April 23, 2011 Leave a comment

I had a great time presenting to the WNC .NET Developers Guild last night. Here is a link to the slides and source code.

Hope you enjoy and thank you to those who came out!

Using Behaviors and Triggers in Xaml

April 13, 2011 Leave a comment

I will be doing a presentation on Thursday, April 21 at the WNC .NET Developers Guild. Here is a summary of the presentation:

We will review what Behaviors and Triggers are all about. We will then discover how we can use them and see how they can enable us as developers. We will also discuss how Behaviors and Triggers allow us to develop following the Model-View-ViewModel (MVVM) pattern. Finally, we will dive into code and examine some common behaviors and triggers that we can use in our line of business applications.

Looking forward to it!

Behaviors and Triggers in Silverlight

August 6, 2010 2 comments

I will be doing a presentation this coming Tuesday, August 10 at the WNC .NET Developers Guild. Here is a summary of the presentation:

Since Silverlight 3 we have had the opportunity to work with Behaviors and Triggers. Most of our exposure has been limited to Blend since the bits come from the Blend application. In this presentation, we will analyze both Behaviors and Triggers and then look at what it takes to build a behavior and trigger from scratch. We will also discuss how we can use Behaviors and Triggers to support Model-View-ViewModel (MVVM) as well. Finally, we will look at where we can use Behaviors and Triggers in our applications in a practical approach. Instead of just showing simple animations, we will look at how we can use these powerful features to get component and logic re-use as much as possible.

Looking forward to it!

Databinding and Triggers – Part III

May 14, 2010 2 comments

This is our final post in a series to provide a simple yet effective user enhancement. Without happy customers, we would be out of business :). We left our las post looking at a set of Xaml:

<i:Interaction.Triggers>
    <int:KeyTrigger Key="F5">
        <int:ExecuteDataMethod Method="RunQuery" />
    </int:KeyTrigger>
    <int:KeyTrigger Key="F5"  Modifiers="Control">
        <int:ExecuteDataMethod Method="ParseQuery" />
    </int:KeyTrigger>
</i:Interaction.Triggers>

The culprit in question is the ExecuteDataMethod object. Let’s take a look and see how that is implemented:

/// <summary>
/// This trigger looks up the VisualTree from AssociatedObject and finds the
/// first UserControl and takes its Datacontext.  It then takes
/// the Method property and invokes it when ever the trigger is fired.
/// Also, you cannot overload methods or it will throw an execption.
/// </summary>
public class ExecuteDataMethod : TriggerAction<FrameworkElement>
{
    private FrameworkElement _element = null;

    #region Method
    /// <summary>
    /// This property the method name to invoke when the trigger is fired.
    /// </summary>
    public string Method
    {
        get { return (string)GetValue(MethodProperty); }
        set { SetValue(MethodProperty, value); }
    }
    public static readonly DependencyProperty MethodProperty =
        DependencyProperty.Register("Method",
        typeof(string), typeof(ExecuteDataMethod), null);

    #endregion

    #region UserState
    /// <summary>
    /// The UserState provides a means to supply state information.
    /// </summary>
    public string UserState
    {
        get { return (string)GetValue(UserStateProperty); }
        set { SetValue(UserStateProperty, value); }
    }
    public static readonly DependencyProperty UserStateProperty =
        DependencyProperty.Register("UserState", typeof(string),
        typeof(ExecuteDataMethod), null);

    #endregion

    #region Overrides
    protected override void Invoke(object parameter)
    {
        // Get the root of the UI.
        if (_element == null)
        {
            _element = Utilities.VisualTreeUtility
                .FindAncestor<UserControl>(this.AssociatedObject);
            if (_element == null && this.AssociatedObject.DataContext != null)
                _element = this.AssociatedObject;
        }

        if (_element != null && _element.DataContext != null)
        {
            object bindingTarget = _element.DataContext;
            MethodInfo method = null;
            try
            {
                method = bindingTarget.GetType().GetMethod(this.Method);
            }
            catch (AmbiguousMatchException ex)
            {
                throw new InvalidOperationException(
                  "Method name cannot be overloaded!", ex);
            }

            if (method != null)
            {
                ParameterInfo[] parameters = method.GetParameters();
                if (parameters.Length == 0)
                {
                    method.Invoke(bindingTarget, null);
                }
                else if (parameters.Length == 1)
                {
                    if (UserState != null &&
                        parameters[0].ParameterType.IsAssignableFrom(
                        this.UserState.GetType()))
                    {
                        method.Invoke(bindingTarget, new object[] { UserState });
                    }
                    else if (this.AssociatedObject != null &&
                             parameters[0].ParameterType.IsAssignableFrom(
                                 this.AssociatedObject.GetType()))
                    {
                        method.Invoke(bindingTarget, new object[] { AssociatedObject });
                    }
                }
                else if (parameters.Length == 2 && this.AssociatedObject != null && 
                    parameter != null)
                {
                    if (parameters[0].ParameterType.IsAssignableFrom(
                        this.AssociatedObject.GetType()) && 
                        parameters[1].ParameterType.IsAssignableFrom(
                        parameter.GetType()))
                    {
                        method.Invoke(bindingTarget, new object[] {
                            this.AssociatedObject, parameter 
                        });
                    }
                }
                else if (parameters.Length == 3 && 
                    this.AssociatedObject != null && parameter != null 
                    && this.UserState != null)
                {
                    if (parameters[0].ParameterType.IsAssignableFrom(
                        this.AssociatedObject.GetType()) &&
                        parameters[1].ParameterType.IsAssignableFrom(
                        parameter.GetType()) &&
                        parameters[2].ParameterType.IsAssignableFrom(
                        this.UserState.GetType()))
                    {
                        method.Invoke(bindingTarget, new object[] {
                            this.AssociatedObject, parameter, UserState 
                        });
                    }
                }
            }
        }
    }
    #endregion
};

This trigger exposes two DependencyProperties: Method and UserState. The Method property allow you to specify the method name that you want to be fired and the UserState property allows you to provide any custom information that you would like.

The Invoke method is where all the exciting stuff lives. The first thing it tries to do is grab the FrameworkElement and store it in a class level variable. It tries to walk the VisualTree and looks for the host control which is a UserControl. If it cannot find a UserControl than it makes sure that the current AssociatedObject is not null and has a DataContext.

Once we have our _elmeent object hydrated, we then use Reflection to try and find a method on the object with the same name as the one provided in our DependencyProperty.

If we have a valid method, we then check and see what parameters are associated with the method. If there are no parameters, we can go ahead and invoke the method immediately. Otherwise, we have currently support one, two , or three parameters. One parameter will either call the method passing in the UserState or passing in the AssociatedObject itself. It also performs a check to be sure that type of each object can be passed in as the parameter.

Two parameters will pass in the AssociatedObject as the first parameter and the parameter object that is passed into the Invoke method. Again a check is performed to be sure that the type of each object can be passed in as the correct corresponding parameter.

Three parameters will do the same as two parameters but it will also pass in the UserState object as the third parameter.

Here is what the ViewModel method looks like for the Xaml sample provided above:

public void ParseQuery()
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("parse");
}

public void RunQuery()
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("run");
}

These methods could also look like this with the UserState passed in:

public void ParseQuery(object userState)
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("parse");
}

public void RunQuery(object userState)
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("run");
}

They could also look like this as you have seen in normal EventHandlers:

public void ParseQuery(object sender, EventArgs e)
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("parse");
}

public void RunQuery(object sender, EventArgs e)
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("run");
}

Finally, they could look like this with the added UserState passed in as well:

public void ParseQuery(object sender, EventArgs e, object userState)
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("parse");
}

public void RunQuery(object sender, EventArgs e, object userState)
{
    _eventAggregator.GetEvent(Of QueryManagerClickEvent)().Publish("run");
}

I typically use the UserState when I have a generic method in my ViewModel and I need to switch on what control or business logic needs to be performed.

Hopefully this gives you some ideas and options as to how you would go about providing some user enhancements. The real power of these Triggers is that you write them once and then you can use them everywhere. It adds to your overall consistency of your application and provides better maintenance.

Categories: English Tags: , ,

Databinding and Triggers – Part II

In our last post, we looked at creating a TextBoxBindingTrigger in order to fire updates on the TextChanged event instead of LostFocus. This time around we are going to be looking at another Trigger that will facilitate handling key press events. Let’s go ahead and take a look at the class that provides this functionality:

public class KeyTrigger : TriggerBase<FrameworkElement>
{
    private FrameworkElement _root;

    #region Key
    public Key Key
    {
        get { return (Key)GetValue(KeyProperty); }
        set { SetValue(KeyProperty, value); }
    }
    public static readonly DependencyProperty KeyProperty =
        DependencyProperty.Register("Key", typeof(Key),
        typeof(KeyTrigger), new PropertyMetadata(Key.None));
    #endregion

    #region Modifiers
    public ModifierKeys Modifiers
    {
        get { return (ModifierKeys)GetValue(ModifiersProperty); }
        set { SetValue(ModifiersProperty, value); }
    }
    public static readonly DependencyProperty ModifiersProperty =
        DependencyProperty.Register("Modifiers", typeof(ModifierKeys),
        typeof(KeyTrigger), new PropertyMetadata(ModifierKeys.None));
    #endregion

    private FrameworkElement FindRoot(FrameworkElement element)
    {
        FrameworkElement parent = element;
        while (parent != null)
        {
            element = parent;
            parent = element.Parent as FrameworkElement;
        }

        return element;
    }

    #region Overrides
    protected override void OnAttached()
    {
        base.OnAttached();

        _root = FindRoot(this.AssociatedObject);
        _root.KeyDown += HandleHostKeyDown;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        _root.KeyDown -= HandleHostKeyDown;
    }
    #endregion

    #region Events
    private void HandleHostKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == this.Key && Keyboard.Modifiers == this.Modifiers)
        {
            this.InvokeActions(e);
        }
    }
    #endregion

};

This class exposes two DependencyProperties: Key and Modifiers. The allow you to specify what key and modifier(s) you want to provide a response. The FindRoot method takes in the underlying FrameworkElement and attempts to walk up the VisualTree to find the root. The overrides OnAttahced() and OnDetaching() provide us with the ability to add an event handler to the KeyDown event. This in turn call the HandleHostKeyDown event handler with in turn fires our newly created trigger.

Let take a look and see how this is used in Xaml:

<i:Interaction.Triggers>
    <int:KeyTrigger Key="F5">
        <int:ExecuteDataMethod Method="RunQuery" />
    </int:KeyTrigger>
    <int:KeyTrigger Key="F5"  Modifiers="Control">
        <int:ExecuteDataMethod Method="ParseQuery" />
    </int:KeyTrigger>
</i:Interaction.Triggers>

Finally we can see that when an F5 or Ctl+F5 is pressed, we fire the RunQuery or ParseQuery methods correspondingly. As you have noticed, there is one more Trigger that is being used to allow us to call a given method on the DataContext or your ViewModel of your control. We will look at this Trigger in the following post.

Categories: English Tags: , ,

Databinding and Triggers – Part I

As I develop Silverlight applications, one of my main goals is to maintain a high user experience. I want the application to be simple, intuitive, and very responsive. With that said, I ran into a problem recently when I had published a new feature. I was trying to enhance the user’s experience by providing hot keys for common scenarios while out of the browser. The enhancement was on a screen that allowed users to enter in a query. The two key presses provided an alternative of needing to use the mouse. When a user clicked on the Play (F5) button, the screen would submit the query to the service and the server would return a resultset. When the user clicked on the Parse (Ctl+F5) button, the screen would submit the query to the service and server would return whether or not the query was valid.

Now comes the fun part. The default behavior for updating the binding on TextBox controls is on the LostFocus event. When a user clicks on a button this works perfectly fine but if a user presses the hot key(s), the control never loses focus and therefore the actual query is never pushed onto the ViewModel. Here is where Triggers come to the rescue.

The following code is a sample of a Trigger that will allow us to fire the UpdateSource method on the binding:

public class TextBoxBindingTrigger : TriggerAction<TextBox>
{
    protected override void Invoke(object parameter)
    {
        BindingExpression binding = 
            AssociatedObject.GetBindingExpression(TextBox.TextProperty);
        if (binding != null)
        {
            binding.UpdateSource();
        }
    }
};

As you can see, there really isn’t a lot of logic required to allow us to call the UpdateSource() method on the underlying binding. Obviously, in order for this to work, we do need to have a binding defined on the TextBox control.

Here is the Xaml that uses this newly created trigger:

<TextBox Grid.Row="2" TextWrapping="Wrap" 
    Text="{Binding QueryManagerQuery, Mode=TwoWay}" 
    AcceptsReturn="True">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged">
            <int:TextBoxBindingTrigger />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

This now allows us to markup certain TextBox controls to fire updates on TextChanged instead of on LostFocus.

The final piece to this puzzle is how to respond to a key press and call a method on the ViewModel to respond just like a Button click. We will cover this on next blog post.

Categories: English Tags: , ,

Xaml Code Generation and XamlReader

April 28, 2010 Leave a comment

When you are performing code generation and you would like to dynamically create some UI stuff it into a ContentPresenter, the XamlReader class comes to the rescue.  However, if you have any events that you would like to handle in your newly composed Xaml, the XamlReader class will throw an exception stating that no event handlers can be present.  I use code generation quite extensively in my applications when I have scenarios that I need user driven interfaces.  Thus, it is important to be able to have the flexibility to create Xaml on the fly and still have all the power of interacting with any event that the new UI contains as needed.

Here is a sample of Xaml taht will throw an exception when loaded by the XamlReader class:

One of the nice features that we have had since Silverlight 3 is the ability to define behaviors and triggers using a set pattern.  The good thing about this is that these behaviors and triggers are not evaluated until the given control is loaded in the VisualTree.  This is good because now we have a solution that will allow us to use the XamlReader class that has event handlers and the class does not throw an exception.

Here is a snippet of some Xaml that is created on the fly:

So the next time you are wanting to do some dynamic UI generation but also want to process events, you now have the tools to let you do so.  As an added bonus, the triggers will fire on whatever DataContext the UI is a part of.  This is nice because it supports a clean separation of concerns and follows the common Model-View-ViewModel (MVVM) pattern.