(6)WPF资源、样式、模板

1、WPF资源

  WPF资源系统,以键值对的形式存储一些公有对象、样式和模板等资源,并且可以通过资源字典对资源进行组织和合并,以达到资源复用的目标。

1.1、资源使用示例

  每个元素控件都有一个Resource属性,用来定义自己的资源,通常只在窗口级别上定义资源,因为每个元素都可以访问它自己的资源集合中的资源,也可以访问所有父元素的资源集合中的资源。

  如下在窗口级别上定义了一个字符串资源,并通过使用该StaticResource。

<Window x:Class="ResourceDemo.ResourceUse"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="REsource" Height="100" Width="350"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <Window.Resources>
        <!--定义一个字符串资源-->
        <sys:String x:Key="nameStr">Heisenbery</sys:String>
    </Window.Resources>
    <StackPanel>
        <!--通过资源key来对资源进行使用-->
        <TextBlock Text="{StaticResource nameStr}" Margin="10"/>
    </StackPanel>
</Window>

1.2、静态和动态引用资源

  资源的使用分析静态引用和动态使用

  • StaticResource:在第一次创建窗口时,一次性地设置完毕
  • DynamicResource:如果资源发生了改变,则会重新应用资源

示例如下:

<Window x:Class="ResourceDemo.DynamicResource"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DynamicResource" Height="300" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="RedBrush" Color="Red"></SolidColorBrush>
    </Window.Resources>
    <StackPanel Margin="5">
      <Button Background="{StaticResource RedBrush}" Margin="5" FontSize="14" Content="Use a Static Resource"/>
        <Button Background="{DynamicResource RedBrush}" Margin="5" FontSize="14" Content="Use a Dynamic Resource"/>
        <Button Margin="5" FontSize="14" Content="Change the RedBrush to Yellow" Click="ChangeBrushToYellow_Click"/>
    </StackPanel>
</Window>

对应改变资源按钮的后台代码如下所示:

private void ChangeBrushToYellow_Click(object sender, RoutedEventArgs e)
{
    // 改变资源
    this.Resources["RedBrush"] = new SolidColorBrush(Colors.Yellow);
}

运行上面程序,你将发现,当点击Change按钮之后,只改变了动态引用资源按钮的背景色,而静态引用按钮的背景却没有发生改变,具体效果图如下所示:

                

1.3、资源字典

  如果某些资源在在多个地方被用到,就可以创建一个资源字典文件,将这些资源组合在一起,然后在Resource的ResourceDictionary属性进行合并使用。

  资源字典文件是一个简单的XAML文档,可以通过右键项目->添加资源字典的方式来添加一个资源字典文件,资源字典示例如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="blueBrush" Color="Blue"/>
    <FontWeight x:Key="fontWeight">Bold</FontWeight>
</ResourceDictionary>

  为了使用资源字典资源,需要将其合并到应用程序中资源集合位置,当然你也可以合并到窗口资源集合中,但是通常是合并到应用程序资源集合中,因为资源字典的目的就是在于多个窗体中共享,资源字典合并示例如下:

<Application x:Class="ResourceDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="DynamicResource.xaml">
    <Application.Resources>
        <!--合并资源字典到Application.Resources中-->
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Generic.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

  资源字典的使用和自定定义的资源使用方式一样,示例如下:

<Window x:Class="ResourceDemo.ResourceUse"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="REsource" Height="100" Width="350"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <Window.Resources>
        <!--定义一个字符串资源-->
        <sys:String x:Key="nameStr">Heisenbery</sys:String>
    </Window.Resources>
    <StackPanel>
        <!--使用资源字典中定义的资源-->
        <Button  Margin="10" Background="{StaticResource blueBrush}" Content="Blue Button" FontWeight="{StaticResource fontWeight}"/>
        <!--通过资源key来对资源进行使用-->
        <TextBlock Text="{StaticResource nameStr}" Margin="10"/>
    </StackPanel>
</Window>

  如果资源字典需要被多个应用程序使用,可以将资源字典文件编译成一个单独的程序集,应用程序可以通过引用程序集的方式来共享资源。

  这种方式下,应用程序可以通过创建一个ResourceDictionary对象来使用资源:

  ResourceDictionary resourceDic = new ResourceDictionary();
  // ReusableDictionary.xaml是资源字典文件
  resourceDic.Source = new Uri("ResourceLibrary;component/ReusableDictionary.xaml", UriKind.Relative);
  SolidColorBrush blueBrush =(SolidColorBrush)resourceDic["BlueBrush"];

2、WPF样式

  在开发过程中,对于某种控件,可能在多个使用的地方,依赖属性和模板属性等内容都一样;在这种这种情况下,可以将相同的依赖属性和模板属性等内容封装在一个样式里,使用该控件的地方就可以复用该样式,避免重复代码。

2.1、样式示例

  如下是一个button样式的示例:

<Window x:Class="StyleDemo.ReuseFontWithStyles"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ReuseFontWithStyles" Height="300" Width="300">
    <Window.Resources>
        <!--带有key标签的样式-->
        <Style TargetType="Button" x:Key="BigButtonStyle">
            <Setter Property="FontFamily" Value="Times New Roman" />
            <Setter Property="FontSize" Value="18" />
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
    </Window.Resources>
    <StackPanel Margin="5">
        <!--如果不显式指定样式key将不会应用样式-->
        <Button Padding="5" Margin="5">Normal Button</Button>
        <Button Padding="5" Margin="5" Style="{StaticResource BigButtonStyle}">Big Button</Button>
        <TextBlock Margin="5">Normal Content.</TextBlock>
        <!--使其不引用事先定义的样式-->
        <Button Padding="5" Margin="5" Style="{x:Null}">A Normal Button</Button>
    </StackPanel>
</Window>

运行效果如下:

2.2、样式触发器

  样式触发器是指:在样式中将某些要设置的内容和属性或事件绑定,一旦属性或事件发生时,自动执行所设置的内容。

1)样式属性触发器

  如下是属性触发器的示例:

<Window.Resources>
    <!--在BigFontButton样式基础上在修改-->
    <Style x:Key="BigFontButton">
        <Style.Setters>
            <Setter Property="Control.FontFamily" Value="Times New Roman" />
            <Setter Property="Control.FontSize" Value="18" />
        </Style.Setters>
        <!--样式触发器-->
        <Style.Triggers>
            <!--获得焦点时触发-->
            <Trigger Property="Control.IsFocused" Value="True">
                <Setter Property="Control.Foreground" Value="Red" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

2)样式事件触发器

 如下是事件触发器的示例,一旦触发MouseEnter事件,则动态改变按钮的FontSize属性来形成动画效果,具体的XAML代码如下所示:

<Window x:Class="StyleDemo.EventTrigger"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="EventTrigger" Height="300" Width="300">
    <Window.Resources>
        <Style x:Key="BigFontButton">
        <!--在BigFontButton样式基础上在修改-->
            <Style.Setters>
                <Setter Property="Control.FontFamily" Value="Times New Roman" />
                <Setter Property="Control.FontSize" Value="18" />
                <Setter Property="Control.FontWeight" Value="Bold" />
            </Style.Setters>
            <Style.Triggers>
                <!--定义事件触发器-->
                <EventTrigger RoutedEvent="Mouse.MouseEnter">
                    <!--事件触发时只需的操作-->
                    <EventTrigger.Actions>
                        <!--把动画放在动画面板中-->
                        <BeginStoryboard>
                            <!--在0.2秒的时间内将字体放大到22单位-->
                            <Storyboard>
                                <DoubleAnimation
                                  Duration="0:0:0.2"
                                  Storyboard.TargetProperty="FontSize"
                                  To="22"  />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
                <!--鼠标移开触发的事件-->
                <EventTrigger RoutedEvent="Mouse.MouseLeave">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <!--在1秒的时间内将字体尺寸缩小到原来的大小-->
                            <!--如果目标字体尺寸没有明确指定,则WPF将默认使用第一次动画之前按钮的字体尺寸-->
                            <Storyboard>
                                <DoubleAnimation
                                  Duration="0:0:1"
                                  Storyboard.TargetProperty="FontSize"  />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
        </Style>     
    </Window.Resources>
    <StackPanel Margin="5">
        <Button Padding="5" Margin="5" Style="{StaticResource BigFontButton}" >A Big Button</Button>
        <TextBlock Margin="5">Normal Content.</TextBlock>
        <Button Padding="5" Margin="5" >A Normal Button</Button>
    </StackPanel>
</Window>

此时的运行效果如下图所示:

 

4、模板

   在WPF有三种模板:控件模板、数据模版和面板模板。它们都继承于FrameworkTemplate基类,继承结构如下:

System.Object;
    System.Windows.Threading.DispatcherObject;
        System.Windows.FrameworkContentElement;
            System.Windows.Controls.ControlTemplate;
            System.Windows.DataTemplate;
            System.Windows.Controls.ItemsPanelTemplate
  • 控件模板(ControlTemplate):定义控件的外观表现,由Template属性指定。
  • 数据模板(DataTemplate):定义内容控件或列表控件的的各个项中显示数据,由CellTemplate或ContentTemplate或ItemTemplate属性指定
  • 布局模板(ItemsPanelTemplate):定义ItemsControl类型控件的布局,由ItemsPanel属性指定。

1)控件模板(ControlTemplate)

在WPF中按钮的默认控件是长方形的,我们可以通过创建一个新的控件模板来改变按钮的外观,下面的例子就实现了通过控件模板的方式自定义了一个圆形的按钮。具体的XAML代码如下所示:

<Window x:Class="TemplateDemo.ControlTemplate"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ControlTemplate" Height="300" Width="300">
    <Window.Resources>
        <!--定义控件模板,并使用key标记-->
        <ControlTemplate x:Key="roundButtonTemplate" TargetType="Button">
            <Grid>
                <Ellipse Name="ell" Fill="Orange" Width="100" Height="100"></Ellipse>
                <!--使用模板绑定来绑定按钮的内容-->
                <ContentPresenter Content="{TemplateBinding Button.Content}" VerticalAlignment="Center" HorizontalAlignment="Center"></ContentPresenter>
            </Grid> 
            <!--定义模板触发器-->
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter TargetName="ell"  Property="Fill" Value="Yellow"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Window.Resources>
    <StackPanel Margin="10">
        <Button Content="Round Button" Template="{StaticResource roundButtonTemplate}"></Button>
    </StackPanel>
</Window>

此时,你就可以看到按钮是一个圆形的了,并且当鼠标移动到按钮上时,会触发模板触发器来改变Ellipse的填充色,具体的运行效果如下图所示:

2)数据模板(DataTemplate)

下面的例子就实现了ListBox的数据模板,具体的XAML代码如下所示:
<Window x:Class="TemplateDemo.DataTemplate"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local ="clr-namespace:TemplateDemo;assembly=TemplateDemo"
        Title="DataTemplate" Height="300" Width="300">
    <Window.Resources>
        <!--创建数据模板 不需要指定TargetType-->
        <DataTemplate x:Key="personDataTem">
            <Border Name="blueBorder" Margin="3" BorderThickness="3" BorderBrush="Blue" CornerRadius="5">
                <Grid Margin="3">
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <TextBlock Name="nametxt" FontWeight="Bold" Text="{Binding Name}"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding Age}"></TextBlock>
                </Grid>
            </Border>
            <!--定义数据模板触发器-->
            <DataTemplate.Triggers>
                <Trigger SourceName="blueBorder" Property="IsMouseOver" Value="True">
                    <Setter TargetName="blueBorder" Property="Background" Value="LightGray"/>
                    <Setter TargetName="nametxt" Property="FontSize" Value="20"/>
                </Trigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </Window.Resources>
    <StackPanel Margin="5">
        <ListBox Name="lstPerson" HorizontalContentAlignment="Stretch" ItemSource = "{Binding persons}" ItemTemplate="{StaticResource personDataTem}"></ListBox>
    </StackPanel>
</Window>

后台代码

public partial class DataTemplate : Window
{
    ObservableCollection<Student> persons = new ObservableCollection<Student>() 
    { 
        new Student() { Name ="LearningHard", Age=25},
        new Student() { Name ="HelloWorld", Age=22}
    };
    
    public DataTemplate()
    {
        InitializeComponent();
    }
}

public class Student : INotifyPropertyChanged
{
    public string ID { get { return Guid.NewGuid().ToString(); } }

    public string Name { get; set; }

    public int Age { get; set; }


    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
}

其运行效果如下图所示:

3)布局模板(ItemsPanelTemplate)

  每种ItemsControl都有其默认的ItemsPanelTemplate。对于 ListBox,默认值使用 VirtualizingStackPanel。 对于 MenuItem,默认值使用 WrapPanel。 对于 StatusBar,默认值使用 DockPanel。

  当然也可以使用的自定义的ItemsPanelTemplate,在上面数据模板例子的基础上,使用布局模板,具体的XAML实现如下所示:

<Window x:Class="TemplateDemo.ItemsPanelTemplate"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ItemsPanelTemplate" Height="300" Width="300">
    <Window.Resources>
        <!--定义DataTemplate-->
        <DataTemplate x:Key="personDataTem">
            <Border Name="blueBorder" Margin="3" BorderThickness="3" BorderBrush="Blue" CornerRadius="5">
                <Grid Margin="3">
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <TextBlock Name="nametxt" FontWeight="Bold" Text="{Binding Name}"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding Age}"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>
        <!--定义ItemsPanelTemplate-->
        <ItemsPanelTemplate x:Key="listItemsPanelTem">
            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        </ItemsPanelTemplate>
    </Window.Resources>
    
    <!--使用ItemsPanelTemplate只需要赋值给ItemsPanel属性即可-->
    <ListBox Name="lstPerson" ItemSource = "{Binding persons}" ItemsPanel="{StaticResource listItemsPanelTem}" ItemTemplate="{StaticResource personDataTem}" />
</Window>

此时程序运行的效果如下图所示,从下图结果可以看出,此时ListBox中的项不再是自上而下排列了,而是从左向右排列的。

摘自

https://www.cnblogs.com/zhili/tag/WPF/

posted @ 2020-08-10 19:46  代码吸血虫  阅读(990)  评论(0)    收藏  举报