Wpf 在依赖项属性回调后调用的ApplyTemplate上的自定义控件

Wpf 在依赖项属性回调后调用的ApplyTemplate上的自定义控件,wpf,custom-controls,Wpf,Custom Controls,我正在开发我的第一个WPF自定义控件,我面临一些问题,下面是我当前使用的代码的简化版本: using System.Windows; using System.Windows.Controls; namespace MyControls { [TemplatePart(Name = "PART_Button", Type = typeof (Button))] public class MyControl : Control { public stati

我正在开发我的第一个WPF自定义控件,我面临一些问题,下面是我当前使用的代码的简化版本:

using System.Windows;
using System.Windows.Controls;

namespace MyControls
{
    [TemplatePart(Name = "PART_Button", Type = typeof (Button))]
    public class MyControl : Control
    {
        public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyControl), new PropertyMetadata(null, OnLabelPropertyChanged));

        private Button _buttonElement;

        public object Content
        {
            get { return this.GetValue(LabelProperty); }
            set { this.SetValue(ContentProperty, value); }
        }

        static MyControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof (MyControl), new FrameworkPropertyMetadata(typeof (MyControl)));
        }

        private static void OnContentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            MyControl myControl = sender as MyControl;
            if (myControl != null && myControl._buttonElement != null)
                myControl._buttonElement.Content = e.NewValue;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this._buttonElement = this.Template.FindName("PART_Button", this) as Button;
        }
    }
}
这是我的自定义控件的模板:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MyControls">
    <Style TargetType="{x:Type local:MyControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyControl}">
                    <Button x:Name="PART_Button" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

然后我将其放入网格中,并尝试设置其内容属性:

<Grid x:Name="layoutRoot">
    <controls:MyControl x:Name="myControl" />
</Grid>

以下是背后的代码:

using System.Windows;

namespace MyControls
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();

            this.myControl.Content = "test";
        }
    }
}
使用System.Windows;
命名空间MyControls
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共主窗口()
{
this.InitializeComponent();
this.myControl.Content=“测试”;
}
}
}
这不起作用,由于某些原因,OnContentPropertyChanged回调在OnApplyTemplate之前被调用,因此myControl.\u ButtonneElement分配得太晚,在尝试设置其内容时仍然为null。为什么会发生这种情况?我如何改变这种行为

我还需要提供完整的设计时支持,但我无法找到一种方法使自定义控件接受一些额外的标记,就像网格控件对ColumnDefinitions所做的那样:

<Grid x:Name="layoutRoot">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
</Grid>

任何帮助都将不胜感激

更新

我找到一个文档,解释了在设置控件属性后调用OnApplyTemplate方法的原因:

所以问题是:如何跟踪设置的属性(在XAML中或以编程方式)以及在控件尚未初始化时调用的方法,以便在调用OnApplyTemplate方法时设置/调用它们?同一回调/方法在控件初始化前后如何工作而不复制代码?

更新:

与“属性”不同,您应该让模板绑定到正在模板化的控件上的属性,而不是通过查找零件将值的更改推送到模板中的元素中

通常,这是使用模板中的演示者完成的,例如,
ContentPresenter
绑定到指定为“内容”的属性(它通过查找
[ContentProperty]
属性来查找该名称),并在模板中使用使用
TemplateBinding
TemplatedParent
连接到自定义控件上的属性的绑定

那么,设置属性的顺序和应用模板的时间就没有问题了……因为正是模板为控件上设置的数据/属性提供了“外观”

如果自定义控件需要提供某些行为/功能(例如,将单击事件挂接到按钮“部件”上),则它只需要真正了解“部件”并与之交互

在这种情况下,您应该让模板绑定到属性,而不是在代码隐藏中设置构造函数中的内容。我在下面给出的示例说明了通常如何使用内容属性来实现这一点

或者,您可以更明确地拉出属性,例如,这可能位于模板内

 <Label Content="{TemplateBinding MyPropertyOnMyControl}" .....

 <Button Content="{TemplateBinding AnotherPropertyOnMyControl}" .....


对于希望能够通过XAML指定一些设计时数据的用户,就像Grid对ColumnDefinition所做的那样……这只是使用属性元素语法来指定填充IList/ICollection类型属性的项

因此,只需创建您自己的属性,即可保存您接受的类型的集合,例如

public List<MyItem> MyItems { get; set; }    // create in your constructor.
public List MyItems{get;set;}//在构造函数中创建。

我遇到了类似的问题,
onapplymetplate
onload
之前被调用


问题是由于xaml中的绑定。我将布尔属性绑定到选中的
,而不是在我的一个复选框上选中的
IsChecked

我描述的场景是我真正需要的一个简化版本:我正在包装一个第三方图表控件,我需要公开所有主要属性来管理它的外观、轴、系列,所以使用ContentProperty不是一个解决方案。感谢您澄清自定义控件中属性的使用,这对我帮助很大!老实说,使用加载的事件似乎不是正确的方法。。。为什么在使用其他Microsoft或第三方控件时不需要这样做?对于我来说,使用TemplateBinding并不总是可行的,当添加轴和系列时,我需要访问基础图表控件,因此模板部分必须已经可用。我使用这里介绍的技巧部分解决了这个问题,但是现在绑定出现了问题,因为没有将子控件添加到逻辑树中。我在axes/serie CollectionChanged事件处理程序中调用AddLogicalChild/RemovelogicChild,但这不起作用。是否尝试调用base.OnApplyTemplate();在您的Template.FIndName(..)之后,同样的错误也是由缺少样式引起的。我有一个带有Style=“{StaticResource MyChbStyle}”的复选框,而MyChbStyle没有在任何地方定义。因此,我认为这个错误通常是由xaml中的一些错误引起的。
<ControlTemplate TargetType="{x:Type local:MyControl}">
<Button x:Name="PART_Button">
<ContentPresenter/>
</Button>
</ControlTemplate>
public List<MyItem> MyItems { get; set; }    // create in your constructor.