Szukaj na tym blogu

piątek, 13 maja 2011

Silverlight 5 Markup Extension and Commanding

Last time, my college from my job told me that creation of ICommand implementation for every required element is very annoying for him and he would be happy if he could bind command (for ex. Button Click) directly to specific method.

As we know, there is not possible customize Markup Extension in Silverlight 4 because the IMarkupExtension interface has only internal access in this version.

Version 5 of Silverlight, extends ours horizons.

Below, I will show how we can achive our goal.

using System;
using System.Windows;
using System.Windows.Input;
using System.Xaml;
using Microsoft.Practices.Prism.Commands;

namespace CommandingSL5
{
    public class Commanding : IMarkupExtension<ICommand>
    {
        public string MethodName { get; set; }

        public ICommand ProvideValue(IServiceProvider serviceProvider)
        {
            if (!String.IsNullOrEmpty(MethodName))
            {
                if (serviceProvider == null)
                    return null;

                var rootService = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
                var root = rootService.RootObject as FrameworkElement;
                if (root != null)
                {
                    if (root.DataContext != null)
                    {
                        var method = root.DataContext.GetType().GetMethod(MethodName);
                        if (method != null)
                        {
                            return new DelegateCommand(() => method.Invoke(root.DataContext, null));
                        }
                    }
                }
            }
            return null;
        }
    }
}

Interface IServiceProvider gives us possibility to get specific service which allows us access to target element (where was used our custom Markup Extension) or root element which is our View with associated ViewModel by property DataContext.

IRootObjectProvider: provide a reference to the Root object of the VisualTree which the element is part of
IXamlTypeResolver : is able to resolve the name of the tags in the markup to the corresponding type.
IProvideValueTarget : gets a reference to the property and the elements which the markup extension is assigned

(Source: Andrea Boschin Blog)

As we see, there is no problem to using more complex methods because I use .NET Reflection which gives me many possibilities.

Usage:
<UserControl x:Class="CommandingSL5.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:c="clr-namespace:CommandingSL5" 
             mc:Ignorable="d">
    
    <UserControl.DataContext>
        <c:MainPageViewModel />
    </UserControl.DataContext>
        
    
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="150" />
        </Grid.ColumnDefinitions>
        
        <TextBox Text="{Binding MethodAText}" 
                 Margin="5,5" />
        <Button Content="Execute TestMethod A" 
                Command="{c:Commanding MethodName=TestMethodA}" 
                Margin="5,5"
                Grid.Column="1"/>
    </Grid>
</UserControl>


and also our ViewModel:

using System.ComponentModel;

namespace CommandingSL5
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region Fields
        private string _methodAText;
        #endregion

        #region Properties
        public string MethodAText
        {
            get { return _methodAText; }
            set
            {
                _methodAText = value;
                RaisePropertyChanged("MethodAText");
            }
        }
        #endregion

        #region Methods
        public void TestMethodA()
        {
            MethodAText = "TestMethodA executed !";
        }
        #endregion

        #region INotifyPropertyChanged Implementation
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) 
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}

Brak komentarzy:

Prześlij komentarz