c# WPF中如何自定义MarkupExtension

  在介绍这一篇文章之前,我们首先来回顾一下WPF中的一些基础的概念,首先当然是XAML了,XAML全称是Extensible Application Markup Language (可扩展应用程序标记语言),是专门用于WPF技术中的UI设计语言,通过使用XAML语言,我们能够快速设计软件界面,同时能够通过绑定这种机制能够很好地实现界面和实现逻辑之间的解耦,这个就是MVVM模式的核心了,那么今天我们介绍的MarkupExtension和XAML之间又有哪些的关系呢?  

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

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

#region 程序集 WindowsBase.dll, v3.0.0.0
// C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll
#endregion

using 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;
        }
 
    }

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

以上就是c# WPF中如何自定义MarkupExtension的详细内容,更多关于WPF中自定义MarkupExtension的资料请关注我们其它相关文章!

(0)

相关推荐

  • C# WPF 通过委托实现多窗口间的传值的方法

    在使用WPF开发的时候就不免会遇到需要两个窗口间进行传值操作,当然多窗口间传值的方法有很多种,本文介绍的是使用委托实现多窗口间的传值. 在上代码之前呢,先简单介绍一下什么是C#中的委托(如果只想了解如何传值可以略过这部分)在网络上有很多对于委托的介绍和讲解,经过我的学习和总结加上了一点我自己的理解,我认为委托是一种类似于C语言的指针,但是它指向的是方法而不是变量.如果把委托看作一个变量,那么这个变量里存着的就是你目标方法的地址,调用委托约等于调用你的目标方法.(个人理解欢迎指正交流) 以下正文:

  • C# WPF使用AForge类库操作USB摄像头拍照并保存

    项目中用到 USB 摄像头,需要根据情况进行图像抓拍,查了半天资料,比较多的是使用 WPFMediaKit 和 AForge . 但是由于项目要求不显示 USB 摄像头拍摄的画面,最终确定使用 AForge 解决. 下面用一个测试程序记录一下. 一.无预览拍照 首先建立一个 WPF 项目,我的就叫 AForgeTest,你们随意就好: 然后在 NuGet 包管理器中安装 AForge 库: 我只安装了图中打勾的几个库,这个根据自己项目需要安装就好. 不过用 USB 摄像头拍照必须安装: AFor

  • c# WPF中System.Windows.Interactivity的使用

    背景 在我们进行WPF开发应用程序的时候不可避免的要使用到事件,很多时候没有严格按照MVVM模式进行开发的时候习惯直接在xaml中定义事件,然后再在对应的.cs文件中直接写事件的处理过程,这种处理方式写起来非常简单而且不用过多地处理考虑代码之间是否符合规范,但是我们在写代码的时候如果完全按照WPF规范的MVVM模式进行开发的时候就应该将相应的事件处理写在ViewModel层,这样整个代码才更加符合规范而且层次也更加清楚,更加符合MVVM规范. 常规用法 1 引入命名空间 通过在代码中引入Syst

  • C# WPF 父控件通过使用可视化树找到子控件的示例代码

    在我们使用WPF设计前台界面时,经常会重写数据模板,或者把控件放到数据模板里.但是一旦将控件放到数据模板中,在后台就没有办法通过控件的名字来获取它了,更没办法对它进行操作(例如,隐藏,改变控件的某个值). 如果你是比我还白的小白,对我刚刚陈述的东西不清楚,接下来我简单说一下什么是把控件放在数据模板中,怎么样的情况没法后台通过名字来获取控件,如果读者对于数据模板这些事儿已经清楚了,或者只关心如何使用可视化树可以将这部分跳过哈. 先上代码介绍一下什么是数据模板以WPF中ListBox控件为例: <L

  • C# wpf Brush转Hex字符串的实例代码

    我就废话不多说了,大家还是直接看代码吧~ //from MaterialDesignDemo.Converters public class BrushToHexConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return null; string lowerH

  • C# WPF 建立无边框(标题栏)的登录窗口的示例

    前言:笔者最近用c#写WPF做了一个项目,此前未曾做过完整的WPF项目,算是一边学一边用,网上搜了不少资料,效率当然是不敢恭维的,有时会在一些很简单的问题上纠结很长时间,血与泪的教训可不少. 不过,正如电视剧某榜里的一句话:既然我活了下来,就不会白白活着!笔者怎么也算挣扎过了,有些经验与教训可以分享,趁着记忆深刻总结写下来.希望后来者少走弯路,提高工作效率.如果有写得不好的地方,希望读者能够指正,一起进步! --------------------------------- 今天先从登录窗口说起

  • 在C# WPF下自定义滚动条ScrollViewer样式的操作

    一.实现对ScrollViewer样式的自定义主要包括: 1.滚动条宽度设置 2.滚动条颜色 3.滚动条圆角 4.滚动条拉动时的效果mouseover 二.实现效果: 三.实现方法 1.创建资源字典( ResourceDictionary)文件 由于style代码比较多,之间在控件文件中加载style比较混乱,也不利于其它窗口复用,这里单独创建了ScrollViewDictionary.xaml文件代码如下: <ResourceDictionary xmlns="http://schema

  • C#中WPF依赖属性的正确学习方法

    前言 我在学习WPF的早期,对依赖属性理解一直都非常的不到位,其恶果就是,我每次在写依赖属性的时候,需要翻过去的代码来复制黏贴. 相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学依赖属性的朋友. 那些[讨厌]的依赖属性的讲解文章 初学者肯定会面临一件事,就是百度,谷歌,或者MSDN来查看依赖属性的定义和使用,而这些文章虽然都写的很好,但,那是相对于已经学会使用依赖属性的朋友而言. 而对于初学者而言,说是误导都不过分. 比如,官网的这篇文章https://docs.micro

  • C# WPF上位机实现和下位机TCP通讯的方法

    下位机使用北京大华程控电源DH1766-1,上位机使用WPF.实现了电压电流实时采集,曲线显示.上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟.昨天写的TCP服务端正好排上用场. 界面如下: 服务端 服务端实在上篇基础上实现的.需要做如下更改: while (true) { try { byte[] bufferDate = new byte[1024]; int realLen = pSocket.Receive(bufferDate); if (realLen <= 0) { t

  • c# WPF中如何自定义MarkupExtension

    在介绍这一篇文章之前,我们首先来回顾一下WPF中的一些基础的概念,首先当然是XAML了,XAML全称是Extensible Application Markup Language (可扩展应用程序标记语言),是专门用于WPF技术中的UI设计语言,通过使用XAML语言,我们能够快速设计软件界面,同时能够通过绑定这种机制能够很好地实现界面和实现逻辑之间的解耦,这个就是MVVM模式的核心了,那么今天我们介绍的MarkupExtension和XAML之间又有哪些的关系呢? Markup Extensio

  • c# WPF中自定义加载时实现带动画效果的Form和FormItem

    背景 今天我们来谈一下我们自定义的一组WPF控件Form和FormItem,然后看一下如何自定义一组完整地组合WPF控件,在我们很多界面显示的时候我们需要同时显示文本.图片并且我们需要将这些按照特定的顺序整齐的排列在一起,这样的操作当然通过定义Grid和StackPanel然后组合在一起当然也是可以的,我们的这一组控件就是将这个过程组合到一个Form和FormItem中间去,从而达到这样的效果,我们首先来看看这组控件实现的效果. 一 动画效果 看了这个效果之后我们来看看怎么来使用Form和For

  • 在WinForm和WPF中使用GMap.Net地图插件简单教程

    如何在WinForm中使用GMap.Net 项目主页:https://greatmaps.codeplex.com/ 下载GMap.Net,我下载的版本:greatmaps_81b71bf30091,编译三个核心项目: GMap.Net.Core:核心DLL GMap.Net.WindowsForms:WinForm中使用的DLL GMap.NET.WindowsPresentation:WPF中使用的DLL 在WinForm项目中使用GMap: 1.新建一个Visual C# 的Windows

  • 在Winform和WPF中注册全局快捷键实现思路及代码

    快捷键辅助类 复制代码 代码如下: class HotKey { /// <summary> /// 如果函数执行成功,返回值不为0. /// 如果函数执行失败,返回值为0.要得到扩展错误信息,调用GetLastError..NET方法:Marshal.GetLastWin32Error() /// </summary> /// <param name="hWnd">要定义热键的窗口的句柄</param> /// <param na

  • c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)

    背景 在很多的时候我们需要编辑DataGrid中每一个Cell,编辑后保存数据,原生的WPF中的DataGrid并没有提供这样的功能,今天通过一个具体的例子来实现这一个功能,在这个例子中DataGrid中的数据类型可能是多种多样的,有枚举.浮点类型.布尔类型.DateTime类型,每一种不同的类型需要双击以后呈现不同的效果,本文通过使用Xceed.Wpf.DataGrid这个动态控件库来实现这个功能,当前使用的Dll版本是2.5.0.0,不同的版本可能实现上面有差别,这个在使用的时候需要特别注意

  • WPF中的数据模板用法介绍

    数据模板常用在3种类型的控件, 下图形式: 1.Grid这种列表表格中修改Cell的数据格式, CellTemplate可以修改单元格的展示数据的方式. 2.针对列表类型的控件, 例如树形控件,下拉列表,列表控件, 可以修改其中的ItemTemplate. 3.修改ContentTemplate, 例UserControl控件的数据展现形式. CellTemplate 模板 下面用一个例子, 来演示CellTemplate使用.例子实现一个DataGrid 展示一个普通的数据标, 同时新增一列C

  • WPF中button按钮同时点击多次触发click解决方法

    解决WPF中button按钮同时点击多次触发click的方法,供大家参考,具体内容如下 DateTime lastClick = DateTime.Now; object obj = new object(); int i = 0; private void Button_Click(object sender, RoutedEventArgs e) { this.IsEnabled = false; var t = (DateTime.Now - lastClick).TotalMillise

  • 在 Angular2 中实现自定义校验指令(确认密码)的方法

    我们会在本文中探索 Angular 2 内建的自定义验证. # 介绍 Angular 2 原生就支持一些有用的验证器: required: 验证字段必须存在 minlength: 验证字段值的最小长度有效 maxlength: 验证字段值的最大长度有效 pattern: 验证输入的值是否匹配给定的模板,比如 email 我们会基于下面的接口创建一个表单来获取用户信息. // user.interface.ts export interface User { username: string; /

  • 深入讲解AngularJS中的自定义指令的使用

    AngularJS的自定义指令,就是你自己的指令,加上编译器编译DOM时运行的原生核心函数.这可能很难理解.现在,假设我们想在应用中不同页面复用一些特定的代码,而又不复制代码.那么,我们就可以简单地把这段代码放到单独的文件,并调用使用自定义指令的代码,而不是一遍又一遍地敲下来.这样的代码更容易理解.AngularJS中有四种类型的自定义指令: 元素指令 属性指令 CSS class 指令 注释指令 在我们现有的app中实现他们之前,我们来看看自定义指令是个什么样子:   元素指令 在html中写

随机推荐