WPF高级教程(十五)对象资源

概念

对象资源是使用代码定义的一系列可以重用的对象,包括 画刷,样式,模板 等。WPF允许在代码中以及在xaml中各个位置定义对象资源。

资源集合

在FrameworkElement中定义了一个Resources属性,该属性使用Resource Dictionary类的实例填充,用于存储元素上的资源。根据我们之前学习的体系结构,定义在FrameworkElement也就表明了所有的WPF元素都具有资源的属性。

每个元素的子元素可以访问自己的和父元素的资源。所以一般我们将资源定义在窗口的资源属性中。

下面我们举例看一下资源的定义和使用

<Window.Resources>
    <SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button Background="{StaticResource RedBrush}">测试1</Button>
    </StackPanel>
</Grid>

资源层次

我们之前讲了,元素会从自己的资源属性和父元素的资源属性中查找资源进行使用,事实上窗口元素也不是最后一站,其查找层次是:

自己的资源 - 父元素的资源 - 应用程序资源 - 系统资源

其中应用程序资源和系统资源我们下面再将,我们可以先看一个问题:如果自己有一个资源,父元素中有一个同名的资源,会怎么样呢?

首先我们先问行不行,再看怎么样,能否同名呢?答案是能,下面的例子展示了这种情况

<Window.Resources>
    <SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button>
            <Button.Resources>
                <SolidColorBrush x:Key="RedBrush" Color="Blue"/>
            </Button.Resources>
            <Button.Background>
                <StaticResource ResourceKey="RedBrush"/>
            </Button.Background>
            <Button.Content>
                测试1
            </Button.Content>
        </Button>
    </StackPanel>
</Grid>

上面代码在按钮的Resources中也定义了同名的Brush,最终运行结果是 蓝色,说明元素在碰到同名资源的时候优先使用自己的,换句话说,自己的资源会覆盖父元素的资源。

我们再看下面这段代码

<Window.Resources>
    <SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
    <StackPanel>
        <Button Background="{StaticResource RedBrush}">
            <Button.Resources>
                <SolidColorBrush x:Key="RedBrush" Color="Blue"/>
            </Button.Resources>
            <Button.Content>
                测试1
            </Button.Content>
        </Button>
    </StackPanel>
</Grid>

运行结果是红色,说明资源必须是在使用之前定义的才能索引到,定义在使用之后的不能被查找到。

静态资源和动态资源

  • 静态资源只加载一次,之后的变化不会响应
  • 动态资源会随着资源的变化而变化

我们直接看一个例子,还是刚刚的例子,我们给Button设置一个Click事件,事件中写入下面的代码

private void Button_Click(object sender, RoutedEventArgs e)
{
    this.Resources["RedBrush"] = new SolidColorBrush(Colors.Blue);
}

我们测试发现,当我们使用

Background="{DynamicResource RedBrush}"

绑定的时候,按下按钮,按钮颜色变化为蓝色,当我们使用静态绑定则不会有任何颜色的变化。

两者对比

  • 静态资源加载较快,在创建窗口的时候就加载完成了
  • 动态资源消耗更大,在使用的时候才会加载

当我们碰到下面的情况的时候必须使用动态资源

  • 资源依赖于系统的属性设置(主题颜色等)
  • 需要使用代码替换资源的时候

由于两者加载时机的不同,在资源很大并且很复杂的情况之下,使用动态资源可以提升加载窗口的速度。

资源的共享与非共享

通常我们使用的资源都是共享的,即资源是一个对象,大家使用的都是这个对象,但是WPF同样允许我们使用非共享的对象,这个技术十分不常使用,大家了解即可

<SolidColorBrush x:Key="RedBrush" Color="Red" x:Shared="False"/>

使用代码访问资源

  • 在拥有Resources属性的元素上使用 FindResource 方法查找资源
  • 在拥有Resources属性的元素上使用 TryFindResource 方法查找资源,这个方法没有找到不会报异常
  • 可以给元素的Resources属性调用Add方法添加资源

应用程序资源

我们可以在App.xaml中定义资源,我们之前学过,App.xaml中有一个Application元素,同样,我们可以在Application的Resources属性中添加资源。

应用程序资源用于在不同的窗口中共享资源。

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <SolidColorBrush x:Key="AppBrush" Color="AliceBlue"/>
    </Application.Resources>
</Application>

系统资源

如果我们想要构建一个跟随系统主题颜色变化变化,跟随系统字体变化,跟随系统字号变化的软件,就必须使用系统资源。

系统资源其实就是系统提供的一系列静态的资源,有下面三个类

  • SystemColors 系统颜色设置
  • SystemFonts 字体设置
  • SystemParameters 屏幕设置 鼠标设置 阴影 拖放等

如果不要求跟随系统设置变化实时变化,我们可以使用下面的方法进行系统资源的使用

<Button Background="{x:Static SystemColors.WindowTextBrush}">Test</Button>

或是在代码中使用

// 使用系统颜色
btn.Background = new SolidColorBrush(SystemColors.WindowTextColor);

// 使用系统画刷
btn.Background = SystemColors.WindowTextBrush;

这里如果我们需要系统变化的时候变化这个资源,就需要使用到动态绑定,明显这里动态绑定不是那么方便,如何做呢,我们使用下面的代码

<Button x:Name="btn" Background="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">Test</Button>

最后我们总结一下,静态的颜色资源都提供了三个版本

  • 颜色版本,供我们构建画刷
  • 画刷版本,可以直接使用
  • Key版本,供我们动态绑定

资源字典 Resource Dictionary

上文中我们学习了对象资源,知道了资源集合,也知道了资源使用的优先级,查找的顺序,也知道了资源定义的几个位置以及系统内置的资源,但是我们都是现成的资源属性,我们之前讲了资源属性是由 Resource Dictionary 来构建的,它是一系列资源的集合,那我们就来学学 Resource Dictionary,学习了资源字典之后,我们就能将一系列的资源放到一个字典中,一起共享到别的项目中。

创建资源字典

创建资源字典我们可以像创建一个类一样,创建新建项,选择资源字典,系统就会帮我们创建一个资源字典,如下面的代码

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp1">
    
</ResourceDictionary>

需要注意的是,资源文件的生成属性可以设置成Page或者Resource都可以。

使用资源字典

  1. 将本项目中的资源文件共享到项目中所有地方(修改App.xaml)

    <Application x:Class="StyleStudy.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:StyleStudy"
             StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Generic.xaml"></ResourceDictionary>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    在项目中的其他地方,可以直接使用这个资源

    <TextBlock Text="{StaticResource nameStr}" FontWeight="{StaticResource fontWeight}"></TextBlock>
    
  2. 引用别的项目中的资源

    要想引用别的项目中的资源首先需要引用资源所在项目,然后下面介绍两种引用的方法

    // 使用代码引用别的项目中的资源(只能用在cs代码中)
    ResourceDictionary resource = new ResourceDictionary();
    resource.Source = new Uri("Views;component/Brushes.xaml", UriKind.Relative);
    // resource指向了资源文件,使用字典的取值语法就可以获取相应的资源
    textBox.Foreground = (SolidColorBrush)resource["MainColor"];
    
    // xaml中引入别的项目的资源文件
    <Application x:Class="StyleStudy.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:StyleStudy"
         StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Generic.xaml"></ResourceDictionary>
                    <ResourceDictionary Source="/StyleStudy.Views;component/Themes/Default/Generic.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    注意要使用MergedDictionaries就可以将资源字典整个提升到项目级别在多窗口中共享,结合我们之前讲的Uri的语法,就可以Merge其他项目的资源字典,这个在多项目的解决方案中十分有用。

    在不同程序集之间共享资源还有一种办法就是使用ComponentResourceKey,这种方法相较于上面两种办法来说要复杂的多,我们这里就不详细讲了,感兴趣的同学可以自行查找相关资料进行学习,然而,掌握了上面讲的方法在实际的开发中就完全够用了,尤其是MergedDictionaries一定要理解吃透。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章