WPF通過DynamicResource實現給界面動態更換皮膚


    在我們的程序中有時候需要去實現動態更換皮膚的效果,從而完成一些個性化的設置,那么我們究竟怎樣去實現動態換皮膚的效果呢?那么我們經常用到的就是設置不同的Style,並且在主程序的xaml文件中通過DynamicResource引用這些Style,當我們點擊更換皮膚時我們就動態去改變這些Style,從而實現動態去更換皮膚的操作。我們知道當我們使用Style="{DynamicResource styleBanner}"這種類型的代碼時,我們的編譯器首先會到App.xaml下面去尋找當前x:key為styleBanner的Style,如果能夠找到,那么我們就可以將當前的Style設置為查找到的值,如果找不到會去MainWindow中查找相關的資源,並將查找到的資源賦值給該Style,如果還是找不到的話,會沿着MainWindow的子元素一級級去查找相關資源,直到最后找到相關的類型資源,如果找不到那么Style值為null;

   在我們的程序中我們通過右鍵菜單來完成背景的切換,首先看一下我們的ContextMenu的代碼:

 <Grid.ContextMenu>
      <ContextMenu MenuItem.Click="OnMenuItemClick">
        <MenuItem Tag=".\Resources\Skins\BlackSkin.xaml" IsChecked="True">
          <MenuItem.Header>
            <Rectangle Width="120" Height="40" Fill="Black" />
          </MenuItem.Header>
        </MenuItem>
        <MenuItem Tag=".\Resources\Skins\GreenSkin.xaml">
          <MenuItem.Header>
            <Rectangle Width="120" Height="40" Fill="Green" />
          </MenuItem.Header>
        </MenuItem>
        <MenuItem Tag=".\Resources\Skins\BlueSkin.xaml">
          <MenuItem.Header>
            <Rectangle Width="120" Height="40" Fill="Blue" />
          </MenuItem.Header>
        </MenuItem>
      </ContextMenu>
    </Grid.ContextMenu>

  我們把每一個資源文件的相對路徑作為一個Tag附加到每一個MenuItem上,這樣當我們點擊其中的某一個MenuItem的時候,我們就能夠查找到相關的資源,從而加載該資源,另外這里有一個小技巧,當我們需要為每一個MenuItem執行相同的Click事件的時候,我們不必進行重復去寫,我們可以在MenuItem的上一級ContextMenu 中添加附加屬性MenuItem.Click="OnMenuItemClick",這樣就可以不用重復去定義Click事件了。下面就是關鍵的OnMenuItemClick事件的代碼了。

void OnMenuItemClick(object sender, RoutedEventArgs e)
		{
			MenuItem item = e.OriginalSource as MenuItem;

			// Update the checked state of the menu items.
			Grid mainGrid = this.Content as Grid;
			foreach (MenuItem mi in mainGrid.ContextMenu.Items)
				mi.IsChecked = mi == item;

			// Load the selected skin.
			this.ApplySkinFromMenuItem(item);
		}

  這里通過e.OriginalSource來查找觸發的事件源,這里每選擇其中的一項時會將其他的項的IsChecked屬性設置為false,這段代碼里調用了一個核心的函數叫做ApplySkinFromMenuItem,那么我們來看看具體是怎么實現的。

void ApplySkinFromMenuItem(MenuItem item)
		{
			// Get a relative path to the ResourceDictionary which
			// contains the selected skin.
			string skinDictPath = item.Tag as string;
			Uri skinDictUri = new Uri(skinDictPath, UriKind.Relative);

			// Tell the Application to load the skin resources.
			DemoApp app = Application.Current as DemoApp;
			app.ApplySkin(skinDictUri);
		}

  我們把每個Tag中定義的不同皮膚的Xaml文件的路徑傳入DemoApp下面的ApplySkin函數中,記住使用Application.Current能夠獲取當前應用程序中唯一的APP,我們來看看我們的DemoAPP中定義的這個方法。

namespace SkinnableApp
{
	public partial class DemoApp : Application
	{
		public void ApplySkin(Uri skinDictionaryUri)
		{
			// Load the ResourceDictionary into memory.
			ResourceDictionary skinDict = Application.LoadComponent(skinDictionaryUri) as ResourceDictionary;

			Collection<ResourceDictionary> mergedDicts = base.Resources.MergedDictionaries;

			// Remove the existing skin dictionary, if one exists.
			// NOTE: In a real application, this logic might need
			// to be more complex, because there might be dictionaries
			// which should not be removed.
			if (mergedDicts.Count > 0)
				mergedDicts.Clear();

			// Apply the selected skin so that all elements in the
			// application will honor the new look and feel.
			mergedDicts.Add(skinDict);
		}
	}
}

  ResourceDictionary skinDict = Application.LoadComponent(skinDictionaryUri) as ResourceDictionary這行代碼是用於加載位於指定統一資源標識符 (URI) 處的 XAML 文件,並將其轉換為由該 XAML 文件的根元素指定的對象的實例,也就是將每一個MenuItem的Tag中標識的路徑下的XAML文件的根元素指定的對象的實例獲取到。我們獲取到了該ResourceDictionary 接下來我們就需要將該ResourceDictionary 加載到Appliciton的MergedDictionaries(獲取 System.Windows.ResourceDictionary 字典的集合,這些字典構成了合並的字典中的各種資源字典。)下面,通過Collection<ResourceDictionary> mergedDicts = base.Resources.MergedDictionaries;我們能夠獲取到當前Application級別下所有的資源字典,這里需要注意的就是在我們將新的ResourceDictionary 添加到mergedDicts 下面之前,我們需要先清除掉原來里面的內容,這是因為我們的

每一個xaml文件中使用的key都是相同的,為了避免混淆,所以首先清除掉原來的內容,其實每一次加載的時候即使有相同的key值編譯器也只會默認加載最后面的一個,但是我們的操作會不斷重復操作,所以mergedDicts 里面的內容會越加越多,所以這在程序中是一種不合理的方式。

     在清除完畢之后,將我們的新的xaml文件作為ResourceDictionary 添加到里面去。這樣我們的MainWindow.xaml就會查找到這些相關的內容。下面貼出相關的代碼以及換膚后的圖片內容。

   MainWindow.xaml文件:

<Window 
  x:Class="SkinnableApp.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
  xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
  xmlns:local="clr-namespace:SkinnableApp"
  SizeToContent="Height"
  ResizeMode="NoResize"
  Title="Skinnable App"
  Width="680"
  WindowStartupLocation="CenterScreen"
  WindowStyle="ToolWindow">
  <Grid x:Name="Root" Style="{DynamicResource styleBackground}">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="50" />
        </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="2.5*" />
    </Grid.ColumnDefinitions> 
    <!-- CONTEXT MENU -->
    <Grid.ContextMenu>
      <ContextMenu MenuItem.Click="OnMenuItemClick">
        <MenuItem Tag=".\Resources\Skins\BlackSkin.xaml" IsChecked="True">
          <MenuItem.Header>
            <Rectangle Width="120" Height="40" Fill="Black" />
          </MenuItem.Header>
        </MenuItem>
        <MenuItem Tag=".\Resources\Skins\GreenSkin.xaml">
          <MenuItem.Header>
            <Rectangle Width="120" Height="40" Fill="Green" />
          </MenuItem.Header>
        </MenuItem>
        <MenuItem Tag=".\Resources\Skins\BlueSkin.xaml">
          <MenuItem.Header>
            <Rectangle Width="120" Height="40" Fill="Blue" />
          </MenuItem.Header>
        </MenuItem>
      </ContextMenu>
    </Grid.ContextMenu>

    <!-- AGENTS DATA SOURCE -->
    <Grid.DataContext>
      <CollectionViewSource>
        <CollectionViewSource.Source>
          <XmlDataProvider Source=".\Resources\Data\agents.xml" XPath="Agents/Agent" />
        </CollectionViewSource.Source>
        <CollectionViewSource.SortDescriptions>
          <scm:SortDescription PropertyName="Name/Last" />
          <scm:SortDescription PropertyName="Name/First" />
        </CollectionViewSource.SortDescriptions>
      </CollectionViewSource>
    </Grid.DataContext>

    <!-- BANNER -->
    <Grid Grid.ColumnSpan="2" Height="70" Style="{DynamicResource styleBanner}" >
      <TextBlock FontSize="26" Padding="10,0,10,0" Text="Insurance Agent Management System" VerticalAlignment="Center"/>
    </Grid>

    <!-- AGENT SELECTOR -->
    <local:AgentSelectorControl  Grid.Row="1" Grid.Column="0"/>
    <!-- AGENT DETAILS -->
    <local:AgentDetailsControl   Grid.Row="1" Grid.Column="1"/>
        <StackPanel Height="40"  Orientation="Horizontal" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Content="移動到第一項" Click="MoveToFirstItem"></Button>
            <Button Content="移動到上一項" Click="MoveToForwardItem" Margin="10,0,10,0"></Button>
            <Button Content="移動到下一項" Click="MoveToNextItem" Margin="10,0,10,0"></Button>
            <Button Content="移動到最后一項" Click="MoveToLastItem" Margin="10,0,10,0"></Button>
        </StackPanel>
    </Grid>
</Window>

  這里列舉其中的一種皮膚的樣式:BlueSkin.xaml

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
  <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source=".\BaseSkin.xaml" />
  </ResourceDictionary.MergedDictionaries>

  <!-- Background Style -->
  <Style x:Key="styleBackground">
    <Setter Property="Control.Background">
      <Setter.Value>
        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5" Opacity="0.5">
          <GradientStop Color="LightSkyBlue" Offset="0" />
          <GradientStop Color="WhiteSmoke" Offset="0.5" />
          <GradientStop Color="LightSkyBlue" Offset="1" />
        </LinearGradientBrush>
      </Setter.Value>
    </Setter>
  </Style>

  <!-- Banner Style -->
  <Style x:Key="styleBanner">
    <Setter Property="StackPanel.Background">
      <Setter.Value>
        <LinearGradientBrush StartPoint="0,0.25" EndPoint="1,0.5">
          <GradientStop Color="#CC0088DD" Offset="0.3" />
          <GradientStop Color="#3300FFFF" Offset="0.85" />
        </LinearGradientBrush>
      </Setter.Value>
    </Setter>
    <Setter Property="TextBlock.Foreground" Value="Yellow" />
    <Setter Property="TextBlock.FontFamily" Value="Comic Sans MS" />
  </Style>

  <!-- Content Area Style -->
  <Style x:Key="styleContentArea" BasedOn="{StaticResource styleContentArea_Base}">
    <Setter Property="Border.Background">
      <Setter.Value>
        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
          <GradientStop Color="#FFFFFFFF" Offset="0.5" />
          <GradientStop Color="#CCFFFFDD" Offset="1" />
        </LinearGradientBrush>
      </Setter.Value>
    </Setter>
    <Setter Property="Border.BorderBrush" Value="Gray" />
    <Setter Property="TextBlock.FontFamily" Value="Comic Sans MS" />
  </Style>

  <!-- Content Area Header Brush -->
  <LinearGradientBrush x:Key="contentAreaHeaderBrush" StartPoint="0.5,0" EndPoint="1,0.5">
    <GradientStop Color="#CC0088DD" Offset="0.3" />
    <GradientStop Color="#CC00AADD" Offset="0.75" />
  </LinearGradientBrush>

  <!-- Content Area Header Style -->
  <Style x:Key="styleContentAreaHeader" BasedOn="{StaticResource styleContentAreaHeader_Base}">
    <Setter Property="Border.Background" Value="{StaticResource contentAreaHeaderBrush}" />
    <Setter Property="TextBlock.Foreground" Value="Yellow" />
    <Setter Property="FrameworkElement.BitmapEffect">
      <Setter.Value>
        <DropShadowBitmapEffect ShadowDepth="12" />
      </Setter.Value>
    </Setter>
    <Setter Property="FrameworkElement.LayoutTransform">
      <Setter.Value>
        <RotateTransform Angle="-2" />
      </Setter.Value>
    </Setter>
  </Style>

  <!-- Agent Image Border Style -->
  <Style x:Key="styleAgentImageBorder" TargetType="Border" BasedOn="{StaticResource styleAgentImageBorder_Base}">
    <Setter Property="BitmapEffect">
      <Setter.Value>
        <DropShadowBitmapEffect />
      </Setter.Value>
    </Setter>
    <Setter Property="BorderBrush" Value="Gray" />
    <Setter Property="BorderThickness" Value="1" />
  </Style>
  
</ResourceDictionary>

  下面貼出幾更換皮膚的圖片。

  


免責聲明!

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



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