WPF换肤设计原理浅析

作者:sunny906 时间:2022-11-18 00:37:00 

WPF换肤的设计原理,利用资源字典为每种皮肤资源添加不同的样式,在后台切换皮肤资源文件。

截图

WPF换肤设计原理浅析

上图中,第一张图采用规则样式,第二张图采用不规则样式,截图的时候略有瑕疵。

资源字典

规则样式资源Skin.RegularStyle.xaml


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

<!--Window样式-->
<Style x:Key="WindowStyle" TargetType="Window">
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="Window">
    <Border BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}">
     <Border.Background>
      <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
       <GradientStop Color="Green" Offset="0"></GradientStop>
       <GradientStop Color="LightGreen" Offset="0.4"></GradientStop>
       <GradientStop Color="White" Offset="1"></GradientStop>
      </LinearGradientBrush>
     </Border.Background>
     <ContentPresenter></ContentPresenter>
    </Border>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--Button样式-->
<Style TargetType="Button">
 <Setter Property="Width" Value="70"></Setter>
 <Setter Property="Height" Value="23"></Setter>
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="Button">
    <Border Name="bdr" Cursor="Arrow"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}">
     <Border.Background>
      <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
       <GradientStop Color="White" Offset="0"></GradientStop>
       <GradientStop Color="LightGreen" Offset="0.3"></GradientStop>
       <GradientStop Color="Green" Offset="1"></GradientStop>
      </LinearGradientBrush>
     </Border.Background>
     <TextBlock Name="tbk" Background="Transparent" Foreground="DarkGreen" TextAlignment="Center"
        Text="{TemplateBinding Content}"></TextBlock>
    </Border>
    <ControlTemplate.Triggers>
     <Trigger Property="IsMouseOver" Value="True">
      <Setter TargetName="bdr" Property="Background">
       <Setter.Value>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
         <GradientStop Color="LightGreen" Offset="0"></GradientStop>
         <GradientStop Color="Green" Offset="1"></GradientStop>
        </LinearGradientBrush>
       </Setter.Value>
      </Setter>
      <Setter TargetName="tbk" Property="Foreground" Value="White"></Setter>
     </Trigger>
    </ControlTemplate.Triggers>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--TextBox样式-->
<Style TargetType="TextBox">
 <Setter Property="FontFamily" Value="SketchFlow Print"/>
 <Setter Property="FontSize" Value="14"/>
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="TextBox">
    <Border BorderBrush="DarkGreen" BorderThickness="0.5">
     <ScrollViewer x:Name="PART_ContentHost" Focusable="false"
         HorizontalScrollBarVisibility="Hidden"
         VerticalScrollBarVisibility="Hidden"></ScrollViewer>
    </Border>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--ContextMenu样式-->
<Style TargetType="ContextMenu">
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="ContextMenu">
    <Border BorderBrush="Green" BorderThickness="1">
     <ItemsPresenter/>
    </Border>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--MenuItem样式-->
<Style TargetType="MenuItem">
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="MenuItem">
    <Border Name="border" Background="LightGreen" BorderThickness="0">
     <TextBlock Name="tbk" Background="Transparent" Padding="5,5"
        Text="{TemplateBinding Header}"></TextBlock>
    </Border>
    <ControlTemplate.Triggers>
     <Trigger Property="IsMouseOver" Value="True">
      <Setter TargetName="border" Property="Background" Value="Green"></Setter>
      <Setter TargetName="tbk" Property="Foreground" Value="White"></Setter>
     </Trigger>
    </ControlTemplate.Triggers>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--TextBlock样式-->
<Style TargetType="TextBlock">
 <Setter Property="FontFamily" Value="SketchFlow Print"/>
 <Setter Property="FontSize" Value="14"/>
</Style>

</ResourceDictionary>
不规则样式资源Skin.RoundedCornerStyle.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<!--Window样式-->
<Style x:Key="WindowStyle" TargetType="Window">
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="Window">
    <Grid Margin="10">
     <Rectangle Fill="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
        RadiusX="5" RadiusY="5">
      <Rectangle.Effect>
       <DropShadowEffect BlurRadius="10" Color="Black" Direction="0" Opacity="0.8"
            RenderingBias="Performance" ShadowDepth="0"/>
      </Rectangle.Effect>
     </Rectangle>
     <Border BorderBrush="{TemplateBinding BorderBrush}"
       BorderThickness="{TemplateBinding BorderThickness}"
       SnapsToDevicePixels="True" CornerRadius="5">
      <Border.Background>
       <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
        <GradientStop Color="Blue" Offset="0"></GradientStop>
        <GradientStop Color="LightBlue" Offset="0.4"></GradientStop>
        <GradientStop Color="White" Offset="1"></GradientStop>
       </LinearGradientBrush>
      </Border.Background>
      <ContentPresenter></ContentPresenter>
     </Border>
    </Grid>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--Button样式-->
<Style TargetType="Button">
 <Setter Property="Width" Value="70"></Setter>
 <Setter Property="Height" Value="23"></Setter>
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="Button">
    <Border Name="bdr" CornerRadius="5" Cursor="Hand"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}">
     <TextBlock Name="tbk" Background="Transparent" Foreground="Yellow" TextAlignment="Center"
        Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"></TextBlock>
     <Border.Background>
      <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
       <GradientStop Color="White" Offset="0"></GradientStop>
       <GradientStop Color="LightBlue" Offset="0.3"></GradientStop>
       <GradientStop Color="Blue" Offset="1"></GradientStop>
      </LinearGradientBrush>
     </Border.Background>
    </Border>
    <ControlTemplate.Triggers>
     <Trigger Property="IsMouseOver" Value="True">
      <Setter TargetName="bdr" Property="Background">
       <Setter.Value>
        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
         <GradientStop Color="LightBlue" Offset="0"></GradientStop>
         <GradientStop Color="Blue" Offset="1"></GradientStop>
        </LinearGradientBrush>
       </Setter.Value>
      </Setter>
      <Setter TargetName="tbk" Property="Foreground" Value="LightYellow"></Setter>
     </Trigger>
    </ControlTemplate.Triggers>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--TextBox样式-->
<Style TargetType="TextBox">
 <Setter Property="FontFamily" Value="Times New Roman"></Setter>
 <Setter Property="FontSize" Value="14"></Setter>
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="TextBox">
    <Border BorderBrush="Blue" BorderThickness="0.5" CornerRadius="5">
     <ScrollViewer x:Name="PART_ContentHost" Focusable="false"
         HorizontalScrollBarVisibility="Hidden"
         VerticalScrollBarVisibility="Hidden"></ScrollViewer>
    </Border>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--ContextMenu样式-->
<Style TargetType="ContextMenu">
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="ContextMenu">
    <Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
     <ItemsPresenter/>
    </Border>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--MenuItem样式-->
<Style TargetType="MenuItem">
 <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="MenuItem">
    <Border Name="border" Background="LightSkyBlue" BorderThickness="0" CornerRadius="5">
     <TextBlock Name="tbk" Background="Transparent" Padding="5,5"
        Text="{TemplateBinding Header}"></TextBlock>
    </Border>
    <ControlTemplate.Triggers>
     <Trigger Property="IsMouseOver" Value="True">
      <Setter TargetName="border" Property="Background" Value="BlueViolet"></Setter>
      <Setter TargetName="tbk" Property="Foreground" Value="White"></Setter>
     </Trigger>
    </ControlTemplate.Triggers>
   </ControlTemplate>
  </Setter.Value>
 </Setter>
</Style>

<!--TextBlock样式-->
<Style TargetType="TextBlock">
 <Setter Property="FontFamily" Value="Times New Roman"/>
 <Setter Property="FontSize" Value="14"/>
</Style>
</ResourceDictionary>

仔细观察上面定义的样式,你会发现在定义Window样式的时候指定了Key,其他的Control样式却没有指定Key。大家都知道,如果没有给Style指定Key,那么这个Style会应用到所有目标类型(TargetType)为指定类型的Control。请看下面一段文字:

WPF换肤设计原理浅析

因为在换肤的过程中,需要动态加载Window的样式,所以用DynamicResource作绑定Style="{DynamicResource WindowStyle}"。

App.xaml

程序运行的时候,默认加载规则样式的皮肤。


<Application.Resources>
 <ResourceDictionary>
  <ResourceDictionary.MergedDictionaries>
   <ResourceDictionary Source="Dictionary\Skin.RegularStyle.xaml"></ResourceDictionary>
  </ResourceDictionary.MergedDictionaries>
 </ResourceDictionary>
</Application.Resources>

后台代码


/// <summary>
 /// MenuItem的执行方法
 /// </summary>
 /// <param name="parameter"></param>
 private void RelayMenuItemEvent(object parameter)
 {
  if (parameter.ToString() == RegularStyle)
  {
   ChangeSkinResource(Skins[0]);
  }
  else if (parameter.ToString() == RoundedCornerStyle)
  {
   ChangeSkinResource(Skins[1]);
  }
 }

/// <summary>
 /// 更换皮肤资源
 /// </summary>
 /// <param name="skin"></param>
 private void ChangeSkinResource(ResourceDictionary skin)
 {
  if (Application.Current.Resources.MergedDictionaries[0].Source.IsAbsoluteUri)
  {
   if (Application.Current.Resources.MergedDictionaries[0].Source.OriginalString != skin.Source.OriginalString)
   {
    Application.Current.Resources.MergedDictionaries[0] = skin;
   }
  }
  else
  {
   if (Application.Current.Resources.MergedDictionaries[0].Source.OriginalString.ToString('\\') != skin.Source.OriginalString.ToString('/'))
   {
    Application.Current.Resources.MergedDictionaries[0] = skin;
   }
  }
 }

运行的时候在MainWindow上右键选择皮肤样式,就可以换肤了。

源码下载:http://xiazai.jb51.net/201610/yuanma/WPFSkin(jb51.net).rar

链接:stackoverflow

标签:WPF,换肤
0
投稿

猜你喜欢

  • Java Base64算法实际应用之邮件发送实例分析

    2022-08-08 04:00:04
  • Springcloud Nacos基本操作代码实例

    2023-07-07 18:24:22
  • spring Bean创建的完整过程记录

    2022-04-14 03:57:51
  • Java微信公众平台开发(13) 微信JSSDK中Config配置

    2022-12-29 15:58:24
  • SpringBoot项目部署到腾讯云的实现步骤

    2023-01-01 16:58:55
  • Java事件处理机制和适配器全面解析

    2021-10-23 04:19:32
  • Java的System.getProperty()方法获取大全

    2023-11-10 20:12:12
  • springmvc如何使用map接收参数

    2023-11-28 04:49:17
  • 使用springboot整合RateLimiter限流过程

    2022-09-12 21:42:48
  • java split()使用方法解析

    2023-10-18 01:59:23
  • IDEA 中使用 Hudi的示例代码

    2021-08-27 21:51:04
  • Java实现Excel导入导出的步骤详解

    2022-09-05 05:20:32
  • IDEA 单元测试报错:Class not found:xxxx springboot的解决

    2022-05-11 01:11:02
  • springboot对接支付宝支付接口(详细开发步骤总结)

    2023-11-10 23:07:35
  • 详解在Spring Boot框架下使用WebSocket实现消息推送

    2023-03-08 01:32:05
  • mybatis-plus查询源码详解

    2023-02-02 11:58:02
  • Springboot整合FreeMarker的实现示例

    2023-04-09 00:57:57
  • Java实现发送邮件并携带附件

    2023-07-23 19:02:25
  • 一篇文章带你了解JAVA结构化编程详情

    2022-01-15 13:06:34
  • Java中过滤器 (Filter) 和 拦截器 (Interceptor)的使用

    2023-07-07 00:20:28
  • asp之家 软件编程 m.aspxhome.com