MarkupExtension的使用

记得第一次看到MarkupExtension是在几年前的一次面试中。很不好意思说,当时不知道是什么东东。最近在项目中又用到了WPF的这个功能,决定在博客里记录一下。

在Xaml中为某个对象以Attribute的方式设置对象的属性时,attribute的值默认只能是字符串。例如:

<TextBlock Text="Text"/>

上面为Text属性设置值"Text"。如果属性的类型为String(如上面代码中的Text属性),这是没有问题的。但是如果属性的类型不是String,比如Foreground属性,它的类型是Brush。我们发现Xaml中仍然是可以通过设置一个字符串来完属性赋值的。例如:

<TextBlock Foreground="Aqua" Text="Foreground"/>

这是因为BCL为Brush类型定义了一个TypeConverter: BrushConverter. 它会在运行时将字符串转换成一个Brush结构。如果我们要在Xaml中直接给Foreground属性传递一个Brush对象怎么办呢?这就到了MarkupExtension发挥作用的时候了。它的作用就是扩充了Attribute方式赋值时只能赋给字符串的限制。让在赋值的时候可以执行后台代码从而产生所期望的对象。其实我们经常用的Binding,StaticResource,DynamicResource等都是属于MarkupExtension. 上代码:

先看一下使用MarkupExtension后xaml的效果:

<Window x:Class="WPFSample.Samples.MarkupExtensionSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:me="clr-namespace:WPFSample.Samples.MarkupExtensionSamples"
        Title="MarkupExtensionSample" Height="300" Width="300">
    <Grid>
        <StackPanel>
            <TextBlock Foreground="Aqua" Text="Foreground"/>
            <!--Following xaml code set a foreground using markup extension.-->
            <TextBlock Foreground="{me:BrushGetter TitleBrush}" Text="Foreground from markup extension"/>
            <TextBlock Foreground="{me:BrushGetter ContentBrush}" Text="Foreground from markup extension"/>
        </StackPanel>
    </Grid>
</Window>

再看看BrushGetter的定义:

public class BrushGetter : MarkupExtension
    {
        private string _brushName;

        public BrushGetter(string brushName)
        {
            //在Xaml中使用该MarkupExtension时传递brushName参数。
            _brushName = brushName;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            //当在通过该MarkupExtension在Xaml中给属性赋值时,该方法将被调用。根据条件返回一个合适的对象即可。
            //至于参数serviceProvider,一般情况下用不到。暂时不纠结它了。
            switch (_brushName)
            {
                case "TitleBrush":
                    return Brushes.Black;
                case "ContentBrush":
                    return Brushes.Blue;
                default:
                    break;
            }

            return null;
        }
    }

最后看看最终效果:

image

其实使用MarkupExtension来赋值时,运行时会在每次赋值时构造一个Markup Extension对象,传递xaml中定义的参数,然后调用ProvideValue方法获取一个值。

posted @ 2013-12-07 22:40  self.refactoring  阅读(3973)  评论(1编辑  收藏  举报