WPF中使用DynamicResource实现换肤
这篇将介绍使用DynamicResource实现动态的界面切换功能。熟悉WPF的园友应该已经猜到了实现方式,简而言之就是动态替换DataTemplate,ControlTemplate,Style等等UI相关的属性。
那么使用DynamicResource能让UI动态到什么程度呢?可以说,心有多大,就可以做多大,只要你想得到,就可以做出来。
下面以展示层次数据结构为例,实现了运行时切换数据显示界面结构的功能。先来看一下要显示的数据,是一个XML文件。
<earth>
<country name="US">
<family name="Bill's">
<member name="Bill"/>
<member name="Mark"/>
</family>
<family name="Hugo's">
<member name="Hugo"/>
<member name="Sherry"/>
</family>
<family name="Li's">
<member name="Li"/>
<member name="Jay"/>
</family>
</country>
<country name="China">
<family name="陆家">
<member name="嘴"/>
<member name="脸"/>
</family>
<family name="徐家">
<member name="汇"/>
<member name="仁"/>
</family>
<family name="黄浦">
<member name="江"/>
<member name="边"/>
</family>
</country>
</earth>
我们要用三种方式来展示这个数据,一种是最常见的TreeView,还可以用一组并列的ListBox,还有不太常见的嵌套式ItemsControl。如下图所示。
图1. TreeView
图2. ListView
图3. GroupView
要实现这些效果,可以使用DataTemplate。把界面中会变的部分独立出来,有人说这个界面除了上面的菜单不变,整个都在变啊。没错,那就把整个主体部分独立出来,放到DataTemplate里。而Window里就只有一个菜单和一个占位符了。如下所示。
<Window x:Class="Skinning.DemoWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="View Demo" Height="300" Width="300">
<DockPanel DataContext="{Binding Source={StaticResource XMLDataDataSource}}">
<Menu DockPanel.Dock="Top">
<MenuItem Header="List View" Click="OnListViewClick"/>
<MenuItem Header="Group View" Click="OnGroupViewClick"/>
<MenuItem Header="Tree View" Click="OnTreeViewClick"/>
</Menu>
<ContentPresenter Content="{Binding}" ContentTemplate="{DynamicResource EarthDataTemplate}"/>
</DockPanel>
</Window>
然后就是定义上面引用到的EarthDataTemplate。以ListView的DataTemplate为例,如下所示。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="NameTemplate">
<TextBlock Text="{Binding XPath=@name}"/>
</DataTemplate>
<DataTemplate x:Key="EarthDataTemplate">
<UniformGrid Rows="1" DataContext="{Binding XPath=earth/country}">
<ListBox ItemsSource="{Binding Mode=Default}" x:Name="countryList" ItemTemplate="{StaticResource NameTemplate}"/>
<ListBox DataContext="{Binding SelectedItem, ElementName=countryList}"
ItemsSource="{Binding Mode=OneWay, XPath=family}"
x:Name="familyList" ItemTemplate="{StaticResource NameTemplate}"/>
<ListBox DataContext="{Binding SelectedItem, ElementName=familyList}"
ItemsSource="{Binding Mode=OneWay, XPath=member}"
ItemTemplate="{StaticResource NameTemplate}"/>
</UniformGrid>
</DataTemplate>
</ResourceDictionary>
其它的DataTemplate就不一一例出了,完整的程序可以从这里下载。
虽然这个例子中只是展示了界面结构上的变化,只使用了DataTemplate,其它的小Case的形式的界面也完全不在话下。比如配色、控件样式、控件位置,乃至所谓的换肤,可以分解为这些技巧的组合。当你把DynamicResource、Style、TemplateSelector、Converter、MarkupExtension等各种WPF技术都用上的时候就会发现WPF可以提供很强大的界面生成功能。
再来介绍一下这种方式的缺点。
为什么微软的文档和WPF的相关书籍中都没有介绍这种方法呢?就像上一篇关于语言支持里列举的现有方案一样,都是要Build进DLL中呢? 我想其中一个原因就是安全上的考虑。把XAML文件这样赤祼 祼地放在外面,对于了解WPF的人来说,完全可以利用这个文件“执行任意代码”。这个很眼熟吧,常常出现在微软的各个安全补丁的描述中。而且一般会是严重的漏洞。
这个问题虽然严重,但也是基本可以解决的。像Web中各种Editor一样,可以对XAML里的内容,过滤一下,替换一下,限制一下等等。如果还不放心,可以把自己定义XML格式来定义界面,然后在内部用XSLT转成XAML再加载。对自定义的XML加限制会更容易些。
这篇内容比较简单,不过是为了下篇主要内容打个基础。下篇将要展示一个自定义的Window的Style,把WPF的WinForm式的边框去掉。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)