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


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

    5-14



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

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

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

    示例5-31

    <Window.Resources>
      
    <ControlTemplate x:Key="ButtonTemplate">
        
    <Rectangle />
      
    </ControlTemplate>
      
      
    <!-- let's just try one button for now -->
      
    <Button Template="{StaticResource ButtonTemplate}"  />
      
    </Window.Resources>

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

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

    <ControlTemplate x:Key=”ButtonTemplate”>
                
    <Rectangle Fill=”Orange” />
    </ControlTemplate>

    5-15

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

    5-16


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

    5.7.1控件模板和样式

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

    示例5-32

    <Window.Resources>
      
    <Style TargetType="{x:Type Button}">
        
        
    <Setter Property="Template">
          
    <Setter.Value>
            
    <ControlTemplate>
              
    <Rectangle Fill="Orange" />
            
    </ControlTemplate>
          
    </Setter.Value>
        
    </Setter>
      
    </Style>
      
    </Window.Resources>

    <!-- No need to set the Template property for each button -->
    <Button  x:Name="cell00" />


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

    5-17



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

    5.7.2模板绑定

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

    示例5-33

    <Style TargetType="{x:Type Button}">
      
    <Setter Property="Background" Value="White" />
      
      
    <Setter Property="Template">
        
    <Setter.Value>
          
    <ControlTemplate x:Key="ButtonTemplate">
            
    <Rectangle Fill="{TemplateBinding Property=Background}" />
          
    </ControlTemplate>
        
    </Setter.Value>
      
    </Setter>
      
    </Style>


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

    5-18



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

    5.7.3内容推荐者

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

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

    示例5-34

    <Style TargetType="{x:Type Button}">
      
    <Setter Property="Background" Value="White" />
      
      
    <Setter Property="Template">
        
    <Setter.Value>
          
    <ControlTemplate>
            
    <Grid>
              
    <Rectangle Fill="{TemplateBinding Property=Background}" />
              
    <ContentPresenter
                
    Content="{TemplateBinding Property=ContentControl.Content}" />
            
    </Grid>
          
    </ControlTemplate>
        
    </Setter.Value>
      
    </Setter>
      
    </Style>


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

        <ControlTemplate TargetType="{x:Type Button}">
          
    <Grid>
            
    <Rectangle Fill="{TemplateBinding Property=Background}" />
            
    <ContentPresenter
              
    Content="{TemplateBinding Property=Content}" />
          
    </Grid>
        
    </ControlTemplate>

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

        <ControlTemplate TargetType="{x:Type Button}">
          
    <Grid>
            
    <Rectangle Fill="{TemplateBinding Property=Background}" />
            
    <!-- with TargetType set, the template binding for the -->
            
    <!-- Content property is no longer required -->
            
    <ContentPresenter />
          
    </Grid>
        
    </ControlTemplate>

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

    5-19



    5.7.4真实的工作

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

        <Style TargetType="{x:Type Button}">
          
    <Setter Property="Background" Value="White" />
          
    <Setter Property="Padding" Value="10,5" />
          
          
    <Setter Property="Template">
            
    <Setter.Value>
              
    <ControlTemplate TargetType="{x:Type Button}">
                
    <Grid>
                  
    <Rectangle Fill="{TemplateBinding Property=Background}" />
                  
    <ContentPresenter
                    
    Content="{TemplateBinding Property=Content}"
                    Margin
    ="{TemplateBinding Property=Padding}" />
                
    </Grid>
              
    </ControlTemplate>
            
    </Setter.Value>
          
    </Setter>
          
        
    </Style>

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

    5-20



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

  • 相关阅读:
    Ansible命令介绍之ansible
    Ansible命令介绍
    Ansible配置文件讲解
    博客搬家。新博客地址 http://fangjian0423.github.io/
    SpringMVC源码分析系列
    MyBatis拦截器原理探究
    通过源码分析MyBatis的缓存
    ThreadLocal原理及其实际应用
    logstash搭建日志追踪系统
    Mybatis解析动态sql原理分析
  • 原文地址:https://www.cnblogs.com/Jax/p/1137264.html
Copyright © 2020-2023  润新知