OnApplyTemplate user control called after dependency property callback

I am developing my first custom WPF control and I have to face some problems, here is a simplified version of the code that I am currently using:

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; } } } 

This is the template for my custom control:

 <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> 

Then I put it in the grid and try to set its Content property:

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

Here is the code behind:

 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"; } } } 

This does not work, for some reason the OnContentPropertyChanged callback is called before OnApplyTemplate, so myControl._buttonElement is assigned too late and it still doesn't matter when trying to set its contents. Why is this happening and how can I change this behavior?

I also need to provide full development-time support, but I cannot find a way to make my custom control accept additional markup for it, much like the Grid control works with ColumnDefinitions:

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

Any help would be greatly appreciated!

UPDATE

I found a document explaining why the OnApplyTemplate method is called after the control properties are set:

http://msdn.microsoft.com/en-us/library/dd351483%28v=vs.95%29.aspx

So the question is: how can I track the properties that are set (in XAML or programmatically) and the methods that are called when the control was not initialized, so that I can set / call them when the OnApplyTemplate method is called? How does the same callback / method work both before and after the initialization of control without duplication of code?

+7
source share
2 answers

UPDATE:

Instead of letting your property "push" the value changes to the elements in your template, looking for parts, you should instead bind the template to the properties of the template to be masked.

This is usually done using presenters in the template, for example. ContentPresenter binds to the property designated as “content” (it recognizes this name by looking for the [ContentProperty] attribute) and using the bindings in your template that use TemplateBinding or TemplatedParent to connect to the properties in your user control.

Then there is no question of what order you set for your properties and when the template is applied ... because it is a template that provides a "view" of the data / properties set on your control.

The user control must really know and interact with the "parts" if it needs to provide certain behavior / functionality, for example. connecting the event of clicking on the "part" button.

In this case, instead of setting Content in the constructor in the code, you should bind your template to the property. The example below showed how this was done using the Content property.

Alternatively, you can pull the properties more explicitly, for example. it could be inside your template.

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

I think it would be better to assign your “content” using the [ContentProperty] attribute, and then use ContentPresenter in your template so that it can be entered inside your button, instead of connecting Content DependencyProperty. (if you inherit ContentControl, this ensures the behavior of "content").

 [TemplatePart(Name = "PART_Button", Type = typeof (Button))] public class MyControl : Control [ContentProperty("Content")] 

and

 <ControlTemplate TargetType="{x:Type local:MyControl}"> <Button x:Name="PART_Button"> <ContentPresenter/> </Button> </ControlTemplate> 

As for what you want to specify some development-time data through XAML, for example, Grid does with ColumnDefinition .... well, that just uses the Property Element syntax to specify elements to populate the typed IList / ICollection property.

So, just create your own property, which may contain a collection of the type that you accept, for example.

 public List<MyItem> MyItems { get; set; } // create in your constructor. 
+5
source

I ran into a similar problem, OnApplyTemplate was called before OnLoaded .

The problem is with xaml binding. I IsChecked boolean value to Checked instead of IsChecked in one of my checkboxes.

0
source

All Articles