Lasse O
Lasse O

Reputation: 349

OnApplyTemplate not called in Custom Control

I have a Custom Control which uses some PART controls:

 [TemplatePart(Name = "PART_TitleTextBox", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_TitleIndexText", Type = typeof(Label))]
    [TemplatePart(Name = "PART_TimeCodeInText", Type = typeof(TextBlock))]
    [TemplatePart(Name = "PART_TimeCodeOutText", Type = typeof(TextBlock))]
    [TemplatePart(Name = "PART_ApprovedImage", Type = typeof(Image))]
    [TemplatePart(Name = "PART_CommentsImage", Type = typeof(Image))]
    [TemplatePart(Name = "PART_BookmarkedImage", Type = typeof(Image))]
    public class TitleBoxNew : Control
    {
        static TitleBoxNew()
        { 
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(TitleBoxNew),
                new FrameworkPropertyMetadata(typeof(TitleBoxNew)));
        } 

        public TitleBoxNew() { }

        // ... rest of class
    }

This control is overriding OnApplyTemplate:

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

      InitializeEvents();
}

Which works well, most of the time. I have added the control inside a custom tab control in a window and somehow OnApplyTemplate is never called for that control! Why doesn't this work as I expect?

Upvotes: 17

Views: 17659

Answers (6)

Hazem Elamir
Hazem Elamir

Reputation: 51

beside all mentioned above which is correct and you should check for,

actually OnApplayTemplete() is called but after all property changed calls and I don't know why, it should be called first thing

so if you are using your PARTs names to get element it will get errors because wpf will not find until OnApplayTemplete() called so, you must add if (your part element != null) condition in front of any code depend on the PARTs names

then inside OnApplayTemplete() method it self recall all your logical method again and it will work fine

    TextBox textBox;
    public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                //onther way to get elements & PARTs
                textBox= Template.FindName("PART_TitleTextBox", this) as TextBox;

                MyMethod();
    
            }
    private void MyMethod()
    {
if (textBox == null) return;
//your logical code here
    }

Upvotes: 0

juFo
juFo

Reputation: 18577

Answer of @MoMo is correct, but additionally:

E: It is expected that the Themes/Generic.xaml is in the root of your project. If this is not the case and your Generic.xaml is not in the root then you have to create a directory Themes with Generic.xaml in the root (Generic.xaml is just of type ResourceDictionary). In that Generic.xaml you need to reference to the location of your Generic.xaml.

e.g.:

<ResourceDictionary Source="/Foo.Bar;component/Controls/FooControl/Themes/Generic.xaml" />

Upvotes: 2

Maxim Gershkovich
Maxim Gershkovich

Reputation: 47189

I am going to add my own answer because both the above answers are incomplete as I have been struggling with this problem for some time now.

As said by MoMo and Kai G above:

A. The controls template and styles should be located in the Generic.xaml file a folder called Themes of the root of your project.

B. Make sure your namespaces are correct in the Generic.xaml

C. Set the style key in the constructor of your control.

D. Ensure the theme attribute is in the assemblyinfo.cs file

BUT you also need to ensure that your Generic.xaml file is set for the Build Action as Page:Do Not copy.

If you fail to do this or the value was somehow set to something other than this, the OnApplyTemplate() method will not be invoked.

Upvotes: 3

MoMo
MoMo

Reputation: 8200

The other two answers are correct...but not complete. According to this post (and my experience of just resolving this issue) there are 4 things you need to check: (for some reason the code blocks in this post wouldn't stay formatted if I used numbers or dashes...so letters it is)

A. The controls template and styles should be located in the Generic.xaml file a folder called Themes of the root of your project.

B. Make sure your namespaces are correct in the Generic.xaml

C. Set the style key in the constructor of your control. It is also widely recommended you put the following in a static constructor.

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

D. Ensure the following is in your assemblyinfo.cs

 [assembly: ThemeInfo(ResourceDictionaryLocation.None, 
 //where theme specific resource dictionaries are located
 //(used if a resource is not found in the     
 // or application resource dictionaries)
 ResourceDictionaryLocation.SourceAssembly 
 //where the generic resource dictionary is located
 //(used if a resource is not found in the page,
 // app, or any theme specific resource dictionaries)
 )]

Upvotes: 35

Kai G
Kai G

Reputation: 3512

For anyone else who might stumble upon this post, I was having the same issue and I managed to solve it by adding the following into the AssemblyInfo.cs of the project containing my custom control:

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
    //(used if a resource is not found in the page, 
    // or application resource dictionaries)
    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
    //(used if a resource is not found in the page, 
    // app, or any theme specific resource dictionaries)
)]

My control's template is located in the file Theme/Generic.xaml in the same project as the control.

Upvotes: 51

Matt DeKrey
Matt DeKrey

Reputation: 11942

I can't see your constructor, but don't forget to set the DefaultStyleKey:

DefaultStyleKeyProperty.OverrideMetadata(typeof(TitleBoxNew), new FrameworkPropertyMetadata(typeof(TitleBoxNew)));

Upvotes: 3

Related Questions