c# WPF中如何自定義MarkupExtension
在介紹這一篇文章之前,我們首先來(lái)回顧一下WPF中的一些基礎(chǔ)的概念,首先當(dāng)然是XAML了,XAML全稱是Extensible Application Markup Language (可擴(kuò)展應(yīng)用程序標(biāo)記語(yǔ)言),是專門用于WPF技術(shù)中的UI設(shè)計(jì)語(yǔ)言,通過(guò)使用XAML語(yǔ)言,我們能夠快速設(shè)計(jì)軟件界面,同時(shí)能夠通過(guò)綁定這種機(jī)制能夠很好地實(shí)現(xiàn)界面和實(shí)現(xiàn)邏輯之間的解耦,這個(gè)就是MVVM模式的核心了,那么今天我們介紹的MarkupExtension和XAML之間又有哪些的關(guān)系呢?
Markup Extension,顧名思義,就是對(duì)xaml的擴(kuò)展,在XAML中,規(guī)定如果屬性以{}開(kāi)始及結(jié)束,就是Markup Extension,Markup Extension指的是繼承于MarkupExtension的類,首先我們通過(guò)一張圖來(lái)看看WPF中有哪些已知的Markup Extension。
看了這張圖片之后是不是對(duì)這個(gè)MarkupExtension有一個(gè)常規(guī)的認(rèn)識(shí),你會(huì)發(fā)現(xiàn)這個(gè)在WPF中實(shí)在是太重要了,通過(guò)這個(gè)MarkupExtension我們能夠?qū)崿F(xiàn)綁定、資源等等一系列的操作,在介紹完這個(gè)之后,我們來(lái)看看,這個(gè)抽象的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 標(biāo)記擴(kuò)展提供基類。 public abstract class MarkupExtension { // 摘要: // 初始化從 System.Windows.Markup.MarkupExtension 派生的類的新實(shí)例。 protected MarkupExtension(); // 摘要: // 在派生類中實(shí)現(xiàn)時(shí),返回一個(gè)對(duì)象,此對(duì)象被設(shè)置為此標(biāo)記擴(kuò)展的目標(biāo)屬性的值。 // // 參數(shù): // serviceProvider: // 可以為標(biāo)記擴(kuò)展提供服務(wù)的對(duì)象。 // // 返回結(jié)果: // 將在擴(kuò)展應(yīng)用到的屬性上設(shè)置的對(duì)象值。 public abstract object ProvideValue(IServiceProvider serviceProvider); } }
其實(shí)看看里面的內(nèi)容,僅僅提供了一個(gè)抽象的方法ProvideValue,我們?cè)诶^承這個(gè)抽象類后需要去重載這個(gè)抽象方法,然后來(lái)實(shí)現(xiàn)自己的邏輯。
在對(duì)整個(gè)MarkupExtension介紹之后,我們可以對(duì)它進(jìn)行一個(gè)總結(jié),那就是:
XAML標(biāo)記擴(kuò)展語(yǔ)法格式:
<元素對(duì)象 對(duì)象屬性=”{擴(kuò)展標(biāo)記 擴(kuò)展標(biāo)記屬性 = 擴(kuò)展屬性值}” />
這個(gè)是不是很熟悉,如果還是不夠直觀的話,我們可以通過(guò)代碼來(lái)進(jìn)行說(shuō)明:
<TextBox Text=”{Binding Path=ProductName}”/>
再來(lái)一個(gè)復(fù)雜一些的例子吧
<Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false" AllowsTransparency="true" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}"/>
類似的這種我們?cè)赪PF中見(jiàn)到的是在是太多了,那么既然基類是一個(gè)抽象方法那么我們是不是可以通過(guò)重載這種方式來(lái)寫(xiě)自己的MarkupExtension呢?這個(gè)當(dāng)然是可以的,我們可以通過(guò)下面的幾個(gè)例子來(lái)進(jìn)行相應(yīng)的說(shuō)明。
示例1:通過(guò)MarkupExtension綁定MenuItem的Icon屬性。
我們知道,MenuItem的Icon屬性可以通過(guò)下面的方式進(jìn)行設(shè)置:
<MenuItem Header="New"> <MenuItem.Icon> <Image Source="data/cat.png"/> </MenuItem.Icon> </MenuItem>
這個(gè)是MSDN介紹的常規(guī)方式,在這里我們可以通過(guò)三種不同的方式來(lái)達(dá)到這個(gè)目的,具體來(lái)看看是怎么實(shí)現(xiàn)的吧?
<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="測(cè)試"> <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>
第一種方式就是我們今天重點(diǎn)介紹的通過(guò)繼承MarkupExtension來(lái)實(shí)現(xiàn)同樣的效果,我們來(lái)具體分析一下這個(gè)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中的一個(gè)特定的屬性,這里我們通過(guò)重寫(xiě)ProvideValue方法,最終調(diào)用BindingBase的ProvideValue返回ImageSource對(duì)象,這里是通過(guò)一個(gè)轉(zhuǎn)換器來(lái)實(shí)現(xiàn)源屬性(字符串)到目標(biāo)屬性ImageSource的轉(zhuǎn)換的,我們會(huì)發(fā)現(xiàn),其實(shí)這種方法和直接綁定并設(shè)置轉(zhuǎn)換器其實(shí)效果是一樣的,只不過(guò)第一種方式更為直觀,將所有的轉(zhuǎn)換過(guò)程都放在了重寫(xiě)ProvideValue函數(shù)的過(guò)程中了,這個(gè)讀者在后面可以對(duì)照demo去認(rèn)真思考然后加以總結(jié)。
示例2:通過(guò)MarkupExtension綁定到ListBox的ItemsSource屬性
這個(gè)稍微復(fù)雜一些,我們?cè)赗eflection這個(gè)MarkupExtension中加入了一些自定義的屬性,這些屬性能夠控制后面返回的數(shù)據(jù)源的最終內(nèi)容,其實(shí)這個(gè)也是非常好理解的,我們?cè)诙xRelativeSource這個(gè)MarkupExtension的時(shí)候,也是通過(guò)定義Mode、AncestorType、AncestorLevel等屬性組合起來(lái)最終實(shí)現(xiàn)在視覺(jué)樹(shù)上找到最終的元素。在代碼里面也不復(fù)雜主要是通過(guò)反射來(lái)獲取Button的屬性、方法、事件、字段等等,這個(gè)具體的實(shí)現(xiàn)過(guò)程可以參考后面的代碼。
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做了一個(gè)簡(jiǎn)單的介紹,最重要的是能夠通過(guò)這種方式來(lái)實(shí)現(xiàn)自己的合理綁定的目的,同時(shí)通過(guò)這種合理的擴(kuò)展方式也能夠讓我們的代碼更加靈活多變,最后附上整個(gè)測(cè)試用的DEMO,希望有需要的點(diǎn)擊進(jìn)行下載,這篇文章只是一個(gè)拋磚引玉的作用,希望讀完之后能引發(fā)自己更多的共鳴,最終代碼越寫(xiě)越好。
以上就是c# WPF中如何自定義MarkupExtension的詳細(xì)內(nèi)容,更多關(guān)于WPF中自定義MarkupExtension的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- c# wpf使用GMap.NET類庫(kù),實(shí)現(xiàn)地圖軌跡回放
- c# 基于wpf,開(kāi)發(fā)OFD電子文檔閱讀器
- c# WPF中自定義加載時(shí)實(shí)現(xiàn)帶動(dòng)畫(huà)效果的Form和FormItem
- c# WPF中CheckBox樣式的使用總結(jié)
- c# WPF中通過(guò)雙擊編輯DataGrid中Cell的示例(附源碼)
- c# WPF設(shè)置軟件界面背景為MediaElement并播放視頻
- c# WPF如何實(shí)現(xiàn)滾動(dòng)顯示的TextBlock
- c# WPF中System.Windows.Interactivity的使用
- 在C# WPF下自定義滾動(dòng)條ScrollViewer樣式的操作
- C#中WPF依賴屬性的正確學(xué)習(xí)方法
- C# WPF上位機(jī)實(shí)現(xiàn)和下位機(jī)TCP通訊的方法
- c# 基于GMap.NET實(shí)現(xiàn)電子圍欄功能(WPF版)
相關(guān)文章
c#刪除數(shù)組中符合條件的元素(正確寫(xiě)法)
這篇文章主要介紹了c#刪除數(shù)組中符合條件的元素,分別給大家展示了錯(cuò)誤寫(xiě)法和正確寫(xiě)法,補(bǔ)充介紹了從C#的數(shù)組中刪除指定元素的幾種方法,需要的朋友可以參考下2023-10-10listview控件實(shí)現(xiàn)點(diǎn)擊列表頭進(jìn)行l(wèi)istview排序示例分享
這篇文章主要介紹了listview控件實(shí)現(xiàn)點(diǎn)擊列表頭進(jìn)行l(wèi)istview排序示例分享,需要的朋友可以參考下2014-03-03C#使用UdpClient類進(jìn)行簡(jiǎn)單通信的實(shí)例
本文主要介紹了C#使用UdpClient類進(jìn)行簡(jiǎn)單通信的實(shí)例,具有很好的參考價(jià)值,需要的朋友可以看下2016-12-12C#通過(guò)oledb訪問(wèn)access數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了C#通過(guò)oledb訪問(wèn)access數(shù)據(jù)庫(kù)的方法,實(shí)例分析了C#操作access數(shù)據(jù)庫(kù)的相關(guān)技巧,需要的朋友可以參考下2015-06-06C#數(shù)據(jù)結(jié)構(gòu)之堆棧(Stack)實(shí)例詳解
這篇文章主要介紹了C#數(shù)據(jù)結(jié)構(gòu)之堆棧(Stack),結(jié)合實(shí)例形式較為詳細(xì)的分析了堆棧的原理與C#實(shí)現(xiàn)堆棧功能的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11WinForm通過(guò)操作注冊(cè)表實(shí)現(xiàn)限制軟件使用次數(shù)的方法
這篇文章主要介紹了WinForm通過(guò)操作注冊(cè)表實(shí)現(xiàn)限制軟件使用次數(shù)的方法,結(jié)合實(shí)例形式分析了WinForm操作注冊(cè)表的原理、步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06