《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间的映射,建立一个元素提供给你想要的外观,并且从父级别的模板绑定到相应的属性,将要做很多的工作来创建你自己的控件模板。

posted @ 2008-04-04 11:52  包建强  Views(1000)  Comments(2Edit  收藏  举报