《Programming WPF》翻译 第6章 2.资源与样式

原文:《Programming WPF》翻译 第6章 2.资源与样式

WPF的样式机制以来于资源体系来定位样式。正如你在第5章看到的,样式在元素的资源片段中定义,而且样式通过其名字被引用,正如示例6-18所示:

示例6-18

<Window x:Class="ResourcePlay.Window1" Text="ResourcePlay"

    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">

    <Window.Resources>

        <Style x:Key="myStyle">

            <Setter Property="Button.FontSize" Value="36" />

        </Style>

    </Window.Resources>

    <Grid>

        <Button Style="{StaticResource myStyle}">Hello</Button>

    </Grid>

</Window>

然而,如何定义一个样式,使之自动的应用到一个元素,而无需显示指定要引用的资源——这是可以实现的,而且非常有用——当你需要把一个样式应用到具有独特类型的所有元素上,而不是把资源引用添加到每个元素上。示例6-19对示例6-18做了一些修改,展示了隐式声明这一功能。

示例6-19

<Window x:Class="ResourcePlay.Window1" Text="ResourcePlay"

    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">

    <Window.Resources>

        <Style TargetType="{x:Type Button}">

            <Setter Property="Button.FontSize" Value="36" />

        </Style>

    </Window.Resources>

    <Grid>

        <Button>Hello</Button>

    </Grid>

</Window>

注意到Button标签不再有其特定的Style属性。然而,这个样式仍然通过TargetType应用到Button上,而不是定义一个key,这个样式使用x:Type来设置TargetType,于是通知XAML为这个TargetType类提供一个System.Type对象。

如果FrameworkElement没有显示指定Style,它总是会寻找一个使用其自身类型的样式资源,作为其Target类型。

如果你建立了一些非样式的资源,例如SolidColorBrush,同时设置其x:Key为某个UI元素的类型,如果试着使用该元素的类型就会发生一个错误。这是因为你创建了一个带有TargetType的Style却没有指定x:Key,x:Key隐式地设置为同TargetType一样。这个Key用于定位style。因此,通常而言,你应该避免将x:Key设置为Type类型的对象。

因为元素会在资源中搜索它的样式,你可以利用系统级别的资源。你可以定义一个样式资源在局部范围内,如果你仅仅希望影响少量的元素;或者在一个广义范围上,例如Window.Resource;或者在应用程序的范围。而且样式可能延及到系统级别。这种样式和资源之间的联系是使用皮肤和主体的关键

6.2.1皮肤和主题

皮肤和主题都是控制UI外观的技术。主题,是一种系统级别的外观,例如Windows2000的经典外观,又如Windows XP的“Luna”主题。皮肤是一个特定于应用程序的外观,正如各种各样具有不同样式的媒体播放程序,例如WinApp和Windows Media Player

皮肤和主题都可以在WPF实现,作为一组资源应用于需要该样式的控件

既然皮肤的意图在于控制一个特定应用程序的外观,它将为标准控件提供更多的样式,可以在应用程序的指定部分定义各种各样有命名的资源。例如,音乐播放器可能使用一个ListBox用来显示歌曲列表。皮肤可以为之提供一个特定的外观而不用影响应用程序中其他的ListBox。因此应用程序可以为这个ListBox设置特定的命名的样式,同时要在这个样式中支持这个样式。对于这种特定情形,提供这样一个样式是可选择的,但是在其他情形中,应用程序需要皮肤提供提供指定的资源。例如,如果应用程序中有一个工具条,皮肤可能就需要提供资源并在其中为这个工具条定义图像。

同样,主体是用于所有应用程序,因此,其必须为所有类型的控件提供模板和样式。比较而言,一个皮肤是特定于应用程序的,所以它不必提供广泛全面的一组样式。如果应用程序并不使用每一个单独的控件类型,皮肤只需要为那些在应用程序中出现的控件提供样式。示例6-20和示例6-21为一个相当简单的皮肤,展示了xaml和相应的后台代码

示例6-20

<ResourceDictionary x:Class="SimpleSkin.BlueSkin"

    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"

    >

    <Style TargetType="{x:Type Button}">

        <Setter Property="Background" Value="Blue" />

        <Setter Property="Foreground" Value="White" />

    </Style>

</ResourceDictionary>

示例6-21

using System;

using System.Windows;

namespace SimpleSkin {

    public partial class BlueSkin : ResourceDictionary {

        public BlueSkin( ) {

            InitializeComponent( );

        }

    }

}

以上代码为一个按钮设置了前景色和背景色。一个更复杂的皮肤应该可以为更多的类型元素提供样式,并且提供更多的属性。更多的皮肤包括一些模板属性的设定,从而可以定义控件的外观。但是即使是在这个简单的例子中,底层的原理也都是一样的。示例6-22展示了一个UI,示例6-23则是这个UI的相应后台代码,允许皮肤的切换。(这个示例假设有2个皮肤类,BlueSkin和GreenSkin,都是使用示例6-20的技术定义的。)

示例6-22



<Window x:Class="SimpleSkin.Window1" Text="SimpleSkin"

    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">

    <Grid Margin="1">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto" />

            <RowDefinition Height="Auto" />

        </Grid.RowDefinitions>

        <RadioButtonList x:Name="radioSkins">

            <TextBlock>Green</TextBlock>

            <TextBlock>Blue</TextBlock>

        </RadioButtonList>

        <Button Grid.Row="1">Hello</Button>

    </Grid>

</Window>

示例6-23

using System;

using System.Windows;

using System.Windows.Controls;

namespace SimpleSkin {

    public partial class Window1 : Window {

        public Window1( ) {

            InitializeComponent( );

            EnsureSkins( );

            radioSkins.SelectionChanged += SkinChanged;

        }

        static ResourceDictionary greenSkin;

        static ResourceDictionary blueSkin;

        static bool resourcesLoaded = false;

        private static void EnsureSkins( ) {

            if (!resourcesLoaded) {

                greenSkin = new GreenSkin( );

                blueSkin = new BlueSkin( );

                resourcesLoaded = true;

            }

        }

        private void SkinChanged(object o, SelectionChangedEventArgs e) {

            switch (radioSkins.SelectedIndex) {

                case 0:

                    Application.Current.Resources = greenSkin;

                    break;

                case 1:

                    Application.Current.Resources = blueSkin;

                    break;

            }

        }

    }

}

SimpleSkin类的代码确保了皮肤只创建一次,而且简单地更换皮肤——通过设置应用程序资源字典,使之成为选中的皮肤。(第二个皮肤的源GreenSkin,在这里没有显示出来,看上去和示例6-20相同,只是用绿色取代了蓝色。)样式和系统资源随着资源的更换自动反映出来:当更改皮肤的时候更新所有有效的控件,因此,这就是我们需要的代码。图6-5显示了代码效果。

6-5

这种切换皮肤的方式有一个小障碍。除了使用皮肤资源,在应用程序级别也存储了一些资源,那么这些应用程序资源会在切换皮肤时丢失。现在,唯一的解决方案是保证每一个皮肤包含一份应用程序级别的资源副本。最好的办法是将这些副本保存在一个单独的类,并将副本和并到资源皮肤中。WPF当前的版本不支持自动和并资源字典,WPF团队的成员已经声明,他们正在考虑更容易的处理办法在未来的发布版本中。目前,只能手动处理,正如示例6-24所示。

示例6-24

ResourceDictionary skinResources = new FooSkinResources( );

ResourceDictionary nonSkinAppResources = new DrawingResources( );

foreach (DictionaryEntry de in nonSkinAppResources) {

    skinResources.Add(de.Key, de.Value);

}

如上,你可以将代码添加到加载资源的方法中。在示例6-23中,你可以在EnsureSkins方法中和并资源,将blue和green皮肤都和并到nonSkinAppResources中。

时间: 2024-08-10 17:21:36

《Programming WPF》翻译 第6章 2.资源与样式的相关文章

《Programming WPF》翻译 第5章 4.元素类型样式

原文:<Programming WPF>翻译 第5章 4.元素类型样式 命名样式非常有用,当你得到一组属性并应用到特点的元素上.然而,如果你想要应用一个统一的样式到所有确定元素类型的实例,设置TargetType而不用一个Key,如示例5-16所示. 示例5-16 <!-- no Key --> <Style TargetType="{x:Type Button}">   <Setter Property="FontSize"

《Programming WPF》翻译 第5章 2.内嵌样式

原文:<Programming WPF>翻译 第5章 2.内嵌样式 每一个“可样式化”的WPF元素都有一个Style属性,可以在内部设置这个属性--使用XAML属性-元素的语法(在第一章讨论的),如示例5-4. 示例5-4 <Button  x:Name="cell00" />   <Button.Style>     <Style>       <Setter Property="Button.FontSize"

《Programming WPF》翻译 第5章 1.不使用样式

原文:<Programming WPF>翻译 第5章 1.不使用样式 作为一个样式如何使其在WPF使用的例子,,让我们看一下TTT简单的实现,如示例5-1. 示例5-1 <!-- Window1.xaml --> <Window     x:Class="TicTacToe.Window1"     xmlns="http://schemas.microsoft.com/winfx/avalon/2005"     xmlns:x=&q

《Programming WPF》翻译 第6章 1.创建和使用资源

原文:<Programming WPF>翻译 第6章 1.创建和使用资源 资源这个词具有非常广泛的意义.任何对象都可以是一个资源.一个在用户界面中经常使用的Brush或者Color可以是一个资源.一段文本或者一个图形也可以是一个资源.没有什么特殊的对象不可以成为一个资源.资源的底层处理机制确保了获取你所需要的资源成为可能,而不闭关心这个资源是什么:同时,这套机制可以简单的识别和定位对象. 资源管理的核心是ResourceDictionary这个类.这是一个相当简单的集合类,就像一个普通的Has

《Programming WPF》翻译 第6章 3.二进制资源

原文:<Programming WPF>翻译 第6章 3.二进制资源 尽管ResourceDictionary和系统级别的资源适合于作为数据存在于对象中,然而,并不是所有的资源都能很好的满足这个模型.能够处理二进制流通常是很有用的.例如,图像,声频和视频,都是有效地二进制的代表,但是这些资源在xaml内都没有相应的标签,而且毕竟这些对象通常表现为底层数据的包装.标记语言本身代表了一种挑战:xaml页面必须编译到我们的应用程序中.因此,需要一种处理二进制流的方法. WPF并未引进任何新技术处理二

《Programming WPF》翻译 第9章 5.默认可视化

原文:<Programming WPF>翻译 第9章 5.默认可视化 虽然为控件提供一个自定义外观的能力是有用的,开发者应该能够使用一个控件而不用必须提供自定义可视化.这个控件应该正好工作,当以它最直接的方式使用时.这意味着控件应该提供一组默认的值. 这些默认的可视化存储在组件的二进制资源中,使用的源文件为theme"generic.xaml.如果你在Visual Studio 2005中创建了一个WPF 控件库的工程,这将自动添加这个文件到你的工程中,并且设置它的Build Act

《Programming WPF》翻译 第6章 4.应用程序全球化

原文:<Programming WPF>翻译 第6章 4.应用程序全球化 如果你打算发布你的应用程序到全球各地,你可能需要为不同地区的用户界面准备不同的版本.至少,这需要解决将文本翻译成适当的语言:同样需要解决UI改变的问题.你可能需要特定的外观适应为本地化的文化习俗.或者,你可能会发现原始的外观在翻译后并不能正常工作,因为词的长度是不一样的.(虽然WPF的外观体系避免了这一问题,更易于创建更弹性的外观.) 为你的软件在不同的市场创建不同的版本是可能的.尽管如此,更加普遍的办法是创建一个单独的

《Programming WPF》翻译 第6章 5.我们进行到哪里了?

原文:<Programming WPF>翻译 第6章 5.我们进行到哪里了? WPF提供了资源工具,让我们运用在用户界面中,动态并具有一致性.我们可以在资源字典中存储任意资源,并且可以遍及应用程序引用这些资源.WPF的样式机制依赖于资源字典--通过为控件设置属性和模板,基于应用程序的皮肤或当前的系统配置主题.而且,对于二进制资源,包含了编译后的BAML版本的xaml文件,WPF使用明显的本地化ResourceManager体系,为终端用户选取最适合的资源作为用户界面的文化.

《Programming WPF》翻译 第5章 7.控件模板

原文:<Programming WPF>翻译 第5章 7.控件模板 如果仔细的看我们当前的TTT游戏,会发现Button对象并没有完全为我们工作.哪些TTT面板有内圆角? 图5-14 这里,我们真正需要的是能够保持按钮的行为,如支持内容和点击事件,但是我们想要接管这些按钮的外观.WPF允许这种方式,因为内在的控件创建的时候是缺少外观性的,例如,他们提供行为,但是外观可以被完全包装在客户端控件的外面. 还记得我们是如何使用数据模板,来为非可视化对象提供外观的么?我们能够使用控件模板对控件做同样的