OnApplyTemplate API not getting invoked when custom control moved to class library - wpf

I have created a sample custom control using WPF custom control project. Added Styles in the Generic.xaml file and all seem to be working fine when it was within the project. In the actual implementation I had to move this implementation to a class library which has the user controls that consume this custom control. When i moved the control i had done all the necessary items. I created Themes folder, added generic.xaml file, made sure the custom control has static constructor, made sure that Themeinfo is added to assemblyinfo.cs. However the styles are not getting invoked and OnApplyTemplate API is not called at all. I tried to keep it in a separate assembly, add that as a reference and yet it didn't work. I am running out of choice here and I got stuck. Can someone help me with the issue please. Please suggest anything i could try.
I created Themes folder, added generic.xaml file, made sure the custom control has static constructor, made sure that Themeinfo is added to assemblyinfo.cs.
[TemplatePart(Name="PART_TwoThumbContainer", Type=typeof(StackPanel))]
[TemplatePart(Name = "PART_BlackThumb", Type = typeof(Thumb))]
public class CustThumb : Control
{
static CustThumb()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustThumb), new FrameworkPropertyMetadata(typeof(CustThumb)));
}
public override void OnApplyTemplate()
{
}
}

Related

Converting dll with WinForm application to WPF

I need to convert existing plugin for CAD systems from WinForm based UI to WPF. The plugin is a basic dll library with Main method used by main application as an entry point to call this plugin. Also it includes a couple of mandatory methods to load/unload the library with plugin.
Long story short. How it looks today (WinForm based):
public class MyPluginClass : System.Windows.Forms.Form
{
private static MyPluginClass thePlugin;
public MyPluginClass()
{
InitializeComponent();
// Make the displayed window a child of the main application window
Reparent(this);
}
private void InitializeComponent()
{
//UI initialization logic
}
// The main entry point. Called when this library is loaded
public static void Main()
{
thePlugin = new MyPluginClass();
thePlugin.Show();
}
}
Once I load this dll, the parent app executes the Main methods and I get the plugin form loaded and displayed.
Now I'm trying to use WPF instead of WinForm (and I have zero experience in WPF, so apologize for silly questions if any).
First thing I tried is to apply the same logic I used with WinForms - just extend Window class:
public class MyPluginClass : Window
{
private static MyPluginClass thePlugin;
.....
public static void Main()
{
thePlugin = new MyPluginClass();
thePlugin.Show();
}
}
...and I didn't find the way to load required XAML file with my window layout. While trying to find solution, I read several posts saying that maybe it's not a good approach to create derived class based on Window. Though, examples I found so far, describe the standalone application (.exe), while in my case I'm trying to invoke WPF UI while being inside the dll file called by parent application.
Is there a way to specify the required XAML file in this case (when I derive my class from Window class) or I'm going completely wrong way?

Replicating WPF TabControl selected TabItem design time behavior for a custom control

I'm developing a custom control which shows an inline popup window and I would like to use a similar technique as the TabControl employes so that only popup windows that are selected within the designer or more commonly by placing the cursor within the popup declaration in XAML that it is visualized right within the desiger without having to run the application or change any runtime values by hand.
I've started by duplicating the implementation of the TabControl which I have successfully mimicking everything but it is all copied from Reflector output and Stylesnooper. I've renamed all of the control parts and then replaced the default templates so that the main control uses an ItemsPresenter instead of a ContentPresenter to show the individual popup controls within a Grid panel overlayed on top of one another. So far this is working great too. The problem is that somewhere along the line I lost the ability to have the designer follow the item that is selected in the XAML editor.
Either an explanation of how the TabControl's design time behavior functionality actually works to describe the selected TabItem behavior that I described above or just some pointers on how one could achieve what I'm tryign to do would be great.
To solve a similar problem, I had to create design time support for my custom tab control. Here is a link for WPF Designer Extensibility.
Basically, I created a PrimarySelectionAdornerProvider to handle click interaction and a FeatureConnector<> / FeatureProvider pair for selection changes (including selection changes made in the xaml editor).
The feature provider / connector:
[FeatureConnector(typeof(AutoTabPageSelectionFeatureConnector))]
class AutoTabPageSelectionFeatureProvider : FeatureProvider
{
public AutoTabPageSelectionFeatureProvider()
: base()
{
// sole purpose is to register the connector
}
}
class AutoTabPageSelectionFeatureConnector : FeatureConnector<AutoTabPageSelectionFeatureProvider>
{
public AutoTabPageSelectionFeatureConnector(FeatureManager manager)
: base(manager)
{
SelectionOperations.Subscribe(this.Context, SelectionChanged);
}
private void SelectionChanged(Selection selection)
{
if (selection.PrimarySelection != null)
{
// navigate tree to find parent (custom tab page and custom tab control)
for (ModelItem item = selection.PrimarySelection; item != null; item = item.Parent)
{
// once found, select appropriate tab
}
}
}
}
Edit (more info):
This Microsoft link has a number of links to walk-throughs that should help. Here are the basic steps to get started:
Create a new project, MyAssembly.VisualStudio.Design.dll.
The library should compile to the same location as MyAssembly.dll (important).
Add references to Microsoft.Windows.Design.Extensibility and Microsoft.Windows.Design.Interaction.
Add a reference to your control library.
Create a class called Metadata
Code:
internal class Metadata : IProvideAttributeTable
{
// Accessed by the designer to register any design-time metadata.
public AttributeTable AttributeTable
{
get
{
AttributeTableBuilder builder = new AttributeTableBuilder();
// Add the adorner provider to the design-time metadata.
builder.AddCustomAttributes(
typeof(MyControl), // rename to your control's name
new FeatureAttribute(typeof(MyPrimaryAdornerProvider)), // rename to whatever you will call your PrimaryAdornerProvider
new FeatureAttribute(typeof(AutoTabPageSelectionFeatureProvider)) // rename to whatever you will call your SelectionFeatureProvider
);
return builder.CreateTable();
}
}
}
Create a class MyPrimaryAdornerProvider from PrimarySelectionAdornerProvider (rename to whatever you want). See link for good walk-through.
Create the AutoTabPageSelectionFeatureProvider and AutoTabPageSelectionFeatureConnector from the example above.

I don't understand the syntax to inherit a wpf user control

I have read multiple posts on the subject but still cannot manage to make it work.
I want 2 user controls slidertype1 and slidertype2 which should inherit from slidercommontype, all are in same namespacecommon, can someone knows the syntax for this simple use case ?
Inspiring from http://jamescrisp.org/2008/05/26/wpf-control-inheritance-with-generics/
I tried:
<namespacecommon:slidercommontype x:Class="namespacecommon.slidertype1">
but I got namespacecommon:slidercommontyp doesn't exist in xml namespace.
As long as the base class doesn't have a XAML file associated with it, it's pretty easy. Trying to incorporate the visual aspect of the user control using XAML is not really a supported scenario.
Having said that, just create your class SliderCommonType (although I would call it SliderBase or something.)
namespace MyControls {
public class SliderBase : UserControl {
}
}
Then create your two controls based on it. I'll show one example and the other should be obvious.
<Local:SliderBase x:Class="MyControls.SliderType1"
xmlns:Local="clr-namespace:MyControls">
</Local:SliderBase>
And the code-behind would look like this:
namespace MyControls {
public class SliderType1 : SliderBase {
}
}
The key point being that your XAML file has to reference the base class which requires changing the <UserControl> element to <Local:SliderBase> which in turn requires a XAML namespace import.
When you add a UserControl using the default template, you can just change the code it creates to reflect the above changes. It's much easier than trying to create it from scratch.
One last thing to note - you will need your application to compile successfully before you can use the visual designer on your derived controls. This is because the designer needs to be able to instantiate SliderBase at design-time.

How to replace a ResourceDictionary of a 3rd Party UserControl (in this case PivotViewer)

The current version of the Microsoft Live Labs PivotViewer control for SilverLight 4 has no way to style the elements of the control. Looking at the control in Reflector, I can see much of the style info is set in a ResourceDictionary in the assembly (assets/defaultcolors.xaml). What I would like to do is create my own copy of this file, then replace it at runtime on the PivotViewer control.
By subclassing the PivotViewer control and overriding OnApplyTemplate I can grab the child elements and set properties such as Background. I have not had any success Clear()'ng the MergedDictionaries and adding in my own:
public override void OnApplyTemplate() {
base.OnApplyTemplate();
/* can change things this way */
CollectionViewerView cvv = ((CollectionViewerView)((Grid)this.GetTemplateChild("PART_Container")).Children[0]);
((Grid)cvv.Content).Background = new SolidColorBrush(Colors.Black);
/* can't change things this way */
CustomDictionary gd = new CustomDictionary();
cvv.Resources.MergedDictionaries.Clear();
cvv.Resources.MergedDictionaries.Add(gd);
}
I'm afraid this isn't going to work in Silverlight because it uses only Static Resources. ( Styles Don't Update )
Changing a resource dictionary only works before InitializeComponent() is called, which is called in the constructor of the PivotViewer.
I've been trying to style the PivotViewer Control too. I hope there is another way, besides searching through the Visual Tree and changing properties.

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?
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.
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)
)]
I can't see your constructor, but don't forget to set the DefaultStyleKey:
DefaultStyleKeyProperty.OverrideMetadata(typeof(TitleBoxNew), new FrameworkPropertyMetadata(typeof(TitleBoxNew)));
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.
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" />
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
}

Resources