WPF中自定义MarkupExtension

2021-04-08 12:25

阅读:509

  再来一个复杂一些的例子吧,

  类似的这种我们在WPF中见到的是在是太多了,那么既然基类是一个抽象方法那么我们是不是可以通过重载这种方式来写自己的MarkupExtension呢?这个当然是可以的,我们可以通过下面的几个例子来进行相应的说明。

  示例1:通过MarkupExtension绑定MenuItem的Icon属性。

  我们知道,MenuItem的Icon属性可以通过下面的方式进行设置:

  这个是MSDN介绍的常规方式,在这里我们可以通过三种不同的方式来达到这个目的,具体来看看是怎么实现的吧?

 

  第一种方式就是我们今天重点介绍的通过继承MarkupExtension来实现同样的效果,我们来具体分析一下这个ImageBinding

 public class ImageBindingExtension : System.Windows.Markup.MarkupExtension
    {
        public ImageBindingExtension(string path)
            : this()
        {
            Path = path;
        }

        public ImageBindingExtension()
        {
        }

        [ConstructorArgument("path")]
        public string Path
        {
            get;
            set;
        }


        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;

            if (target.TargetObject is Setter)
            {
                return new Binding(Path) { Converter = ImgaeSourceConverter.Default };
            }
            else
            {
                Binding binding = new Binding(Path) { Converter = ImgaeSourceConverter.Default };
                return binding.ProvideValue(serviceProvider);
            }
           
        }
    }

  这里面我们定义的Path属性就是绑定到ViewModel中的一个特定的属性,这里我们通过重写ProvideValue方法,最终调用BindingBase的ProvideValue返回ImageSource对象,这里是通过一个转换器来实现源属性(字符串)到目标属性ImageSource的转换的,我们会发现,其实这种方法和直接绑定并设置转换器其实效果是一样的,只不过第一种方式更为直观,将所有的转换过程都放在了重写ProvideValue函数的过程中了,这个读者在后面可以对照demo去认真思考然后加以总结。

  示例2:通过MarkupExtension绑定到ListBox的ItemsSource属性

  这个稍微复杂一些,我们在Reflection这个MarkupExtension中加入了一些自定义的属性,这些属性能够控制后面返回的数据源的最终内容,其实这个也是非常好理解的,我们在定义RelativeSource这个MarkupExtension的时候,也是通过定义Mode、AncestorType、AncestorLevel等属性组合起来最终实现在视觉树上找到最终的元素。在代码里面也不复杂主要是通过反射来获取Button的属性、方法、事件、字段等等,这个具体的实现过程可以参考后面的代码。

public class ReflectionExtension : System.Windows.Markup.MarkupExtension
    {
        public Type CurrentType { get; set; }
        public bool IncludeMethods { get; set; }
        public bool IncludeFields { get; set; }
        public bool IncludeEvents { get; set; }

        public ReflectionExtension(Type currentType)
        {
            this.CurrentType = currentType;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (this.CurrentType == null)
            {
                throw new ArgumentException("Type argument is not specified");
            }

            ObservableCollection collection = new ObservableCollection();
            foreach (PropertyInfo p in this.CurrentType.GetProperties())
            {
                collection.Add(string.Format("属性 : {0}", p.Name));
            }

            if (this.IncludeMethods)
            {
                foreach (MethodInfo m in this.CurrentType.GetMethods())
                {
                    collection.Add(string.Format("方法 : {0} with {1} argument(s)", m.Name, m.GetParameters().Count()));
                }
            }
            if (this.IncludeFields)
            {
                foreach (FieldInfo f in this.CurrentType.GetFields())
                {
                    collection.Add(string.Format("字段 : {0}", f.Name));
                }
            }
            if (this.IncludeEvents)
            {
                foreach (EventInfo e in this.CurrentType.GetEvents())
                {
                    collection.Add(string.Format("事件 : {0}", e.Name));
                }
            }
            return collection;
        }

    }

  今天就如何自定义MarkupExtension做了一个简单的介绍,最重要的是能够通过这种方式来实现自己的合理绑定的目的,同时通过这种合理的扩展方式也能够让我们的代码更加灵活多变,最后附上整个测试用的DEMO,希望有需要的点击进行下载,这篇文章只是一个抛砖引玉的作用,希望读完之后能引发自己更多的共鸣,最终代码越写越好。

 


评论


亲,登录后才可以留言!