Wpf 在依赖项属性回调后调用的ApplyTemplate上的自定义控件
我正在开发我的第一个WPF自定义控件,我面临一些问题,下面是我当前使用的代码的简化版本: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
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.