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式的邊框去掉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM