天天看点

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

如果仔细的看我们当前的TTT游戏,会发现Button对象并没有完全为我们工作。哪些TTT面板有内圆角?

图5-14

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

这里,我们真正需要的是能够保持按钮的行为,如支持内容和点击事件,但是我们想要接管这些按钮的外观。WPF允许这种方式,因为内在的控件创建的时候是缺少外观性的,例如,他们提供行为,但是外观可以被完全包装在客户端控件的外面。

还记得我们是如何使用数据模板,来为非可视化对象提供外观的么?我们能够使用控件模板对控件做同样的事情,这将是一组StoryBoard,触发器,以及大多数重要的提供控件外观的元素。

为了修复我们的按钮外观,我们创建了一个控件模板的资源。让我们从示例5-31出发,这是一个带有简单的矩形,和以后考虑如何显示实际的按钮内容。

示例5-31

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

<Window.Resources>

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

  <ControlTemplate x:Key="ButtonTemplate">

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

    <Rectangle />

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

  </ControlTemplate>

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

  <!-- let's just try one button for now

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

 -->

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

  <Button Template="{StaticResource ButtonTemplate}" 

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

 />

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

</Window.Resources>

5-15显示了设置一个单独按钮的Template属性的结果。

注意到按钮过去样子的痕迹(保留在图5-15中)。不幸的是,看不到我们的矩形的痕迹。问题在于,缺少一个显示的填充设置,这个矩形的默认填充是透明的,显示grid的黑色背景。让我们将其设置为喜欢的万圣节颜色:

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

<ControlTemplate x:Key=”ButtonTemplate”>

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

            <Rectangle Fill=”Orange” />

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

</ControlTemplate>

图5-15

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

现在我们在这个地方,如图5-16所示。

图5-16

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

注意,拐角处是如何成直角的?而且,一旦你点击了按钮,你不会获得压下的效果。(而且我没有意味“一个不爽的感觉”)

5.7.1控件模板和样式

注意到我们在控件模板上取得的一些成果,让我们将其复制到其它按钮上。我们可以手动设置每个按钮上的模板属性,或者,作为最普通的,我们可以用按钮的样式包装这个模板控件。如示例5-32。

示例5-32

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

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

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

    <Setter Property="Template">

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

      <Setter.Value>

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

        <ControlTemplate>

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

          <Rectangle Fill="Orange" />

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

        </ControlTemplate>

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

      </Setter.Value>

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

    </Setter>

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

  </Style>

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

<!-- No need to set the Template property for each button -->

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

<Button 

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

 x:Name="cell00" />

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

正如示例

5-32所示,模板属性和使用样式设置是一样的。图5-17显示了结果。

图5-17

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

仍然,橙色是不和谐的,尤其是因为白色背景样式上的设定。我们可以用模板绑定来解决这个问题。

5.7.2模板绑定

为了回到我们的白色按钮,我们可以硬编码将矩形填充为白色,但是如果样式要改变它呢(正如在我们中断的动画)?取代以硬编码填充矩形,我们使用模板绑定来将模板应用到控件属性中,正如示例5-33所示。

示例5-33

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

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

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

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

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

  <Setter Property="Template">

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

    <Setter.Value>

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

      <ControlTemplate x:Key="ButtonTemplate">

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

        <Rectangle Fill="{TemplateBinding Property=Background}" />

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

      </ControlTemplate>

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

    </Setter.Value>

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

  </Setter>

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

</Style>

模板绑定就像数据绑定,除了绑定的属性来自被你替换了模板的控件(称为模板化的父级别)。在我们的情形中,像

Background,HorizontalContentAlignment等等,是美丽的游戏,用来模板绑定自父级别。同时,像数据绑定,模板绑定是相当小巧的——用来保持模板中的条目属性是最新的,随着外界的属性改变,如被样式和动画设置等等。举例来说,图5-18显示了混淆矩形的Fill属性到按钮的Background属性的效果——仍然适当地通过我们的点击动画和鼠标盘旋的行为。

图5-18

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

尽管如此,我们还没有彻底到达。如果我们将要改变图画样品,这样图5-18已经变为了一个可玩的游戏,我们不得不显示所有的移动。为了这么做,我们需要一个内容推荐者。

5.7.3内容推荐者

如果你曾经被广告牌或汽车站长椅上写着的“这里是你的广告!”所驱动,然后这就是所有你需要知道的理解内容推荐者。内容推荐者等价于WPF中的“这里是你的内容”,允许内容由插入的ContentContainer控件保持在运行期。

在我们的情形中,内容是可视化的PlayerMove对象。取代以复制所有的工作到按钮新的控件模板中,我们只想要去除它在正确的地方。内容推荐者的工作是获取内容——由内容模板化的父级别提供,以及所有必须要显示的事物,包括样式,触发器等等。内容推荐者可以添加到你的模板中——无论在哪里看到的模板(包括多次,如果它使你愉快,例如生成一个下拉阴影)。在我们的情形中,我们在示例5-34中组成一个内容推荐者,使用第2章的技术在grid中放一个矩形。

示例5-34

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

      <ControlTemplate>

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

        <Grid>

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

          <Rectangle Fill="{TemplateBinding Property=Background}" />

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

          <ContentPresenter

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

            Content="{TemplateBinding Property=ContentControl.Content}" />

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

        </Grid>

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

在示例

5-34中,内容推荐者的Content属性绑定到ContentControl.Content属性,为了内容成功使用。作为使用样式,我们可以避免给模板绑定属性名称加上类的前缀,通过在ContentTemplate元素上设置TargetAttribute属性。

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

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

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

      <Grid>

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

        <ContentPresenter

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

          Content="{TemplateBinding Property=Content}" />

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

      </Grid>

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

    </ControlTemplate>

进一步,在恰当的位置使用TargetType属性,你可以一起去除显示地模板绑定到Content,属性上,同时它会进行自动设置。

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

        <!-- with TargetType set, the template binding for the -->

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

        <!-- Content property is no longer required -->

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

        <ContentPresenter />

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

内容推荐者是我们需要的全部,使得我们的游戏回到具有功能性,正如图5-19所示。

图5-19

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

5.7.4真实的工作

最后一小块工作是获取右间隙。由于内容推荐者没有自身的Padding属性,我们不能直接绑定Padding属性(它也没有Background属性,这是为什么我们使用Rectangle和其Fill属性)。因为这些属性并不匹配内容推荐者,你不得不找到映射或者组合提供这些功能的元素。例如,padding是控件中一定数量的空白,另一方面,Margin是控件周围一定数量的空白。由于他们都是同样的类型,System.Windows.Thickness,如果我们可以映射按钮中的Padding到内容控件的外面。我们的TTT游戏看起来就会很漂亮:

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

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

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

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

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

      <Setter Property="Padding" Value="10,5" />

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

      <Setter Property="Template">

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

        <Setter.Value>

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

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

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

            <Grid>

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

              <Rectangle Fill="{TemplateBinding Property=Background}" />

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

              <ContentPresenter

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

                Content="{TemplateBinding Property=Content}"

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

                Margin="{TemplateBinding Property=Padding}" />

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

            </Grid>

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

          </ControlTemplate>

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

        </Setter.Value>

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

      </Setter>

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

    </Style>

图5-20显示了我们最终的TTT变体。

图5-20

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

就像Padding和Margin间的映射,建立一个元素提供给你想要的外观,并且从父级别的模板绑定到相应的属性,将要做很多的工作来创建你自己的控件模板。