2012-08-06 10 views
7

ich meine erste WPF-Steuerelement zu entwickeln und ich bin vor einige Probleme, hier ist eine vereinfachte Version des Codes ich zur Zeit mit:Custom Control OnApplyTemplate nach Abhängigkeitseigenschaft Callback

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

Dies ist die Vorlage für meine benutzerdefinierte Steuerung:

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

ich es dann in einem Gitter setzen und versuchen, seine Content-Eigenschaft zu setzen:

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

Hier ist der Code hinter:

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

Dies funktioniert nicht, aus irgendeinem Grunde der OnContentPropertyChanged Rückruf vor OnApplyTemplate genannt wird, so myControl._buttonElement zu spät zugewiesen ist und es ist immer noch null, wenn man versucht seinen Inhalt zu setzen. Warum passiert das und wie kann ich dieses Verhalten ändern?

Ich brauche auch vollen Entwurfszeit-Unterstützung zur Verfügung zu stellen, aber ich kann nicht einen Weg zu machen, meine individuelle Kontrolle übernimmt einige zusätzliche Markup, ähnlich wie das Grid-Steuerelement funktioniert mit ColumnDefinitions finden:

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

Jede Hilfe wäre sehr geschätzt!

UPDATE

fand ich ein Dokument, das, warum die OnApplyTemplate Methode erklärt, nachdem die Steuerung aufgerufen wird properies eingestellt:

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

Die Frage ist also: Wie kann ich den Überblick über das halten Eigenschaften, die festgelegt werden (in XAML oder programmgesteuert) und Methoden, die aufgerufen werden, wenn das Steuerelement nicht initialisiert wurde, sodass ich sie beim Aufruf der OnApplyTemplate-Methode festlegen/aufrufen kann? Wie kann derselbe Callback/Methode vor und nach der Steuerungsinitialisierung funktionieren, ohne den Code zu duplizieren?

+0

haben Sie versucht, base.OnApplyTemplate() aufzurufen; nach Ihrem Template.FIndName (..)? – sam

Antwort

5

UPDATE:

Statt Ihre „Eigenschaft“ Wertänderungen in Elemente in der Vorlage für Teile, indem Sie drücken, sollten Sie stattdessen Ihre Vorlage binden an Eigenschaften auf der Kontrolle Templat wird.

Normalerweise geschieht dies unter Verwendung von Präsentatoren innerhalb einer Vorlage, z. ContentPresenter bindet an die Eigenschaft, die als "Inhalt" bezeichnet wird (durch Ausfindigmachen des Namens [ContentProperty]) und durch Verwendung von Bindungen in Ihrer Vorlage, die TemplateBinding oder TemplatedParent verwenden, um eine Verbindung zu den Eigenschaften Ihres benutzerdefinierten Steuerelements herzustellen.

Dann gibt es kein Problem darüber, welche Reihenfolge Sie Ihre Eigenschaften festlegen und wann die Vorlage angewendet wird .... weil es die Vorlage ist, die das "Aussehen" für die Daten/Eigenschaften bietet, die auf Ihrem Steuerelement festgelegt sind.

Ein benutzerdefiniertes Steuerelement sollte nur wirklich wissen und interagieren mit "Teile", wenn es bestimmtes Verhalten/Funktionalität, z. Haken des Klickereignisses auf einer Schaltfläche "Teil".

In diesem Fall sollten Sie, anstatt den Inhalt im Konstruktor in code-behind zu setzen, Ihre Vorlage an die Eigenschaft binden lassen.Das Beispiel, das ich unten gegeben habe, zeigte, wie dies im Allgemeinen mit einer Content-Eigenschaft gemacht wurde.

Alternativ könnten Sie Eigenschaften expliziter herausziehen, z. Dies könnte in Ihrer Vorlage sein.

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

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

denke ich, es wäre besser, Ihren „Inhalt“ mit [Content] Attribute zu bezeichnen, und dann einen Content in Ihrer Vorlage verwenden, so dass es in Ihrem Knopf injiziert werden kann, anstatt Sie Einhaken Ihre Content DependencyProperty. (Wenn Sie von ContentControl erben, liefert das das "content" -Verhalten).

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

und

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

Was Sie wollen in der Lage sein, einige Entwurfszeit-Daten über XAML angeben wie Grid mit Column ist .... gut, dass unter Verwendung von nur Property Elemente Syntax der angeben Elemente zum Füllen einer IList/ICollection typisierten Eigenschaft.

Erstellen Sie einfach Ihre eigene Eigenschaft, die eine Sammlung des Typs halten kann, den Sie akzeptieren, z.

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

Das Szenario, das ich beschrieben habe, ist eine viel vereinfachte Version dessen, was ich wirklich brauche: Ich wickle ein Drittanbieter-Diagrammsteuerelement ein und muss alle Haupteigenschaften verfügbar machen, um Aussehen, Achsen, Serien usw. zu verwalten, so dass ContentProperty nicht verwendet wird eine Lösung. Vielen Dank für die Klärung der Verwendung von Eigenschaften in benutzerdefinierten Steuerelementen, es hat mir sehr geholfen! – Kupido

+0

Ehrlich gesagt, das Loaded-Ereignis zu verwenden, scheint nicht der richtige Weg zu sein ... Warum ist dies nicht nötig, wenn Sie andere Microsoft- oder Drittanbieter-Steuerelemente verwenden? – Kupido

+0

Die Verwendung von TemplateBinding ist für mich nicht immer möglich, wenn ich Achsen und Serien hinzufügen muss, um auf das zugrunde liegende Chart-Steuerelement zugreifen zu können, muss der Template-Teil bereits verfügbar sein. Ich habe teilweise mit dem hier beschriebenen Trick http://stackoverflow.com/questions/1298898/wpf-customcontrol-onapplytemplate-called-after-propertychangecallback gelöst, aber jetzt habe ich Probleme mit Bindungen, weil untergeordnete Steuerelemente nicht hinzugefügt werden logischer Baum. Ich rufe AddLogicalChild/RemoveLogicalChild in der Achsen/serie CollectionChanged-Ereignishandler aber das funktioniert nicht. – Kupido

0

ich ähnliches Problem aufgetreten ist, wurde OnApplyTemplate vor OnLoaded genannt.

Das Problem wurde aufgrund der Bindung in XAML. Ich habe die boolesche Eigenschaft an Checked anstelle von IsChecked auf einer meiner Checkboxen gebunden.

+0

Derselbe Fehler wurde auch durch fehlenden Stil verursacht. Ich hatte eine Checkbox mit Style = "{StaticResource MyChbStyle}" und MyChbStyle wurde nirgendwo definiert. Daher denke ich, dass dieser Fehler in der Regel durch einen Fehler in XAML verursacht wird. – JanBrus