65.9K
CodeProject 正在变化。 阅读更多。
Home

UserControl/Control:如何获取模板中元素的引用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (4投票s)

2010 年 9 月 15 日

Ms-PL

2分钟阅读

viewsIcon

21065

在这篇文章中,我们将探索如何通过使用 FindName 方法来访问模板子元素,即使是在 UserControl 上也可以。

当你想创建自己的自定义控件时,你有两个选择:创建一个UserControl或继承自“Control 的类”(ContentControl, ItemsControlsControl 本身)。这样做时,你肯定需要访问模板的视觉部分代码,以便为其添加良好的行为。

在这篇文章中,我们将探索如何通过使用 FindName 方法来访问模板子元素,即使是在 UserControl 上也可以。

创建控件

我不会解释如何创建自定义控件,所以这里是它的基本代码

[TemplatePart(Name = "PART_MyGrid", Type = typeof(Grid))]
  public class MyCustomControl : ContentControl
  {
    private Grid myAimedGrid;
 
    static MyCustomControl()
    {
	  //Overrides the style by ours
      DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
          new FrameworkPropertyMetadata(typeof(MyCustomControl)));
    }
  }

这是我们在“Themes\generic.xaml”文件中为其通用视觉主题定义模板的方式。请注意,我们添加了一个名为 Grid 的元素:“PART_MyGrid”。稍后我们将从代码中查找它。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:FindNamesApplication.MyContentControl">
  <Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:MyCustomControl }">
          <Grid x:Name="PART_MyGrid" Background="Black" Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}">
            <ContentPresenter Content="{TemplateBinding Content}" />
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary> 

现在,我们如何从后台代码中找到网格?只需在正确的时刻访问模板:当模板被应用时。为此,我们将重写 OnApplyTemplate() 方法,并使用 FindName 方法按名称直接访问网格。然后我们可以随心所欲地操作它。

public override void OnApplyTemplate()
{
  //Effectively apply the template
  base.OnApplyTemplate();
 
  //Find the grid in the template once it's applied
  myAimedGrid = base.Template.FindName("PART_MyGrid", this) as Grid;
 
  //We can subscribe to its events
  myAimedGrid.PreviewMouseDown += 
     new MouseButtonEventHandler(myAimedGrid_PreviewMouseDown);
}
 
void myAimedGrid_PreviewMouseDown(object sender,
 System.Windows.Input.MouseButtonEventArgs e)
{
  //Proof 
  MessageBox.Show("Mouse preview Down on the grid !");
} 

顺便说一句,当你创建一个专注于可重用性的自定义控件时,你必须使用 TemplatePart 属性来声明它的不同部分

[TemplatePart(Name="PART_MyGrid",Type=typeof(Grid))]
public class CustomControl : ContentControl
{
// ....
} 

创建用户控件

现在是文章中最难的部分:你创建一个用户控件作为应用程序的可重用部分。为此,你创建 C# 文件和 XAML 文件,并且由于你希望对其进行自定义,因此将其 ContentTemplate 设置如下

/// <summary>
/// Interaction logic for MyCustomUserControl.xaml
/// </summary>
public partial class MyCustomUserControl : UserControl
{
  private Grid myAimedGrid;
 
  public MyCustomUserControl()
  {
    InitializeComponent();
  }
} 

XAML 文件如下所示

<UserControl x:Class="FindNamesApplication.MyUserControl.MyCustomUserControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        mc:Ignorable="d" d:DesignHeight="300"
        d:DesignWidth="300">
    <UserControl.ContentTemplate>
        <DataTemplate>
            <Grid x:Name="PART_MyGrid" Background="Black" 
            Width="{TemplateBinding Width}"
                    Height="{TemplateBinding Height}">
                <ContentPresenter Content="{TemplateBinding Content}" />
            </Grid>
        </DataTemplate>
    </UserControl.ContentTemplate>
</UserControl> 

然后,正如你之前看到的,你重写 OnApplyTemplate 并使用 FindName 方法获取子元素:这不会起作用!实际上,你将得到的只是“null”或有时是 InvalidOperationException。为什么?因为通过设置 controlTemplate,你定义了一个 DataTemplate,然后我们的 UserControl 使用它来应用到其内部 ContentPresenter。因此,通过在 UserControl 上使用 findName,我们在 UserControl 的模板中搜索名为“PART_MyGrid”的元素,而不是在我们创建并实际使用的模板中搜索。

所以解决方案是在正确的元素上寻找元素,即 UserControl 模板的 ContentPresenter。为此,我们将使用 VisualTreeHelper 找到它以获取 ContentPresenter,然后使用 FindName 方法,并将其作为参数。这是代码

public override void OnApplyTemplate()
{
  base.OnApplyTemplate();
 
  //The ContentPresenter is the second child of the UserControl...
  ContentPresenter presenter = (ContentPresenter)
    (VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0));
 
  //Be sure that the template is applied on the presenter
  presenter.ApplyTemplate();
 
  //get the grid from the presenter
  myAimedGrid =
    presenter.ContentTemplate.FindName("PART_MyGrid", presenter) as Grid;
 
  //We can subscribe to its events
  myAimedGrid.PreviewMouseDown
    += new MouseButtonEventHandler(myAimedGrid_PreviewMouseDown);
}
 
void myAimedGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
  //Proof 
  MessageBox.Show("Mouse preview Down on the grid !");
} 

相关链接

以下是一些关于该主题的进一步链接

结论

正如我们所看到的,没有什么是不可能的,一旦看到,实现这些不同的解决方案非常容易……祝你编码愉快!源代码解决方案已链接到文章。

© . All rights reserved.