您好,登錄后才能下訂單哦!
這篇文章給大家介紹怎么在c#項目中自定義MarkupExtension,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
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呢?這個當然是可以的,我們可以通過下面的幾個例子來進行相應的說明。
我們知道,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去認真思考然后加以總結。
這個稍微復雜一些,我們在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就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。