文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么在c#项目中自定义MarkupExtension

2023-06-06 15:33

关注

这篇文章给大家介绍怎么在c#项目中自定义MarkupExtension,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

Markup Extension,顾名思义,就是对xaml的扩展,在XAML中,规定如果属性以{}开始及结束,就是Markup Extension,Markup Extension指的是继承于MarkupExtension的类,首先我们通过一张图来看看WPF中有哪些已知的Markup Extension。

怎么在c#项目中自定义MarkupExtension

  看了这张图片之后是不是对这个MarkupExtension有一个常规的认识,你会发现这个在WPF中实在是太重要了,通过这个MarkupExtension我们能够实现绑定、资源等等一系列的操作,在介绍完这个之后,我们来看看,这个抽象的MarkupExtension基类到底是什么?里面包含些什么?怎么去使用它?

#region 程序集 WindowsBase.dll, v3.0.0.0// C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll#endregionusing System;namespace System.Windows.Markup{ // 摘要: //  为所有 XAML 标记扩展提供基类。 public abstract class MarkupExtension {  // 摘要:  //  初始化从 System.Windows.Markup.MarkupExtension 派生的类的新实例。  protected MarkupExtension();  // 摘要:  //  在派生类中实现时,返回一个对象,此对象被设置为此标记扩展的目标属性的值。  //  // 参数:  // serviceProvider:  //  可以为标记扩展提供服务的对象。  //  // 返回结果:  //  将在扩展应用到的属性上设置的对象值。  public abstract object ProvideValue(IServiceProvider serviceProvider); }}

   其实看看里面的内容,仅仅提供了一个抽象的方法ProvideValue,我们在继承这个抽象类后需要去重载这个抽象方法,然后来实现自己的逻辑。

  在对整个MarkupExtension介绍之后,我们可以对它进行一个总结,那就是:

  XAML标记扩展语法格式:

  <元素对象 对象属性=”{扩展标记 扩展标记属性 = 扩展属性值}” />
      这个是不是很熟悉,如果还是不够直观的话,我们可以通过代码来进行说明:      

<TextBox Text=”{Binding Path=ProductName}”/>

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

<Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false" AllowsTransparency="true" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}"/>

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

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

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

<MenuItem Header="New">  <MenuItem.Icon>    <Image Source="data/cat.png"/>  </MenuItem.Icon></MenuItem>

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

<Menu Grid.Column="0">           <MenuItem Header="文本">               <MenuItem Header="重做">                   <MenuItem.Icon>                       <Image Stretch="Uniform" Source="{extension:ImageBinding Redo}"></Image>                   </MenuItem.Icon>               </MenuItem>               <MenuItem Header="撤销">                   <MenuItem.Icon>                       <Image Stretch="Uniform" Source="{extension:ImageBinding Undo}"></Image>                   </MenuItem.Icon>               </MenuItem>               <MenuItem Header="保存所有">                   <MenuItem.Icon>                       <Image Stretch="Uniform" Source="{Binding SaveAll,Converter={StaticResource SourceConverter}}"></Image>                   </MenuItem.Icon>               </MenuItem>               <MenuItem Header="测试">                   <MenuItem.Icon>                       <Image Stretch="Uniform" Source="Resources/Images/Redo.png"></Image>                   </MenuItem.Icon>               </MenuItem>           </MenuItem>           <MenuItem Header="编辑"></MenuItem>           <MenuItem Header="视图"></MenuItem>           <MenuItem Header="插件"></MenuItem>       </Menu>

  第一种方式就是我们今天重点介绍的通过继承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<string> collection = new ObservableCollection<string>();            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;        }     }

关于怎么在c#项目中自定义MarkupExtension就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯