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
}
Related
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()
{
}
}
I am working with a very large Silverlight 5 application that needs to implement theming. Unfortunately I can't use the C1 (Component One) or Silverlight Toolkit theme mechanisms due to the enormity of xaml and code changes I would have to implement. I am forced to do something a bit out of the box.
As a starting point I created a demo project by referencing a post on Stack Overflow Using Mef to Import a WPF DataTemplate written by #Scott Whitlock. The post described how to dynamically load a Silverlight/WPF resource dictionary and add it to the App.Current.Resources.MergedDictionaries collection within the Silverlight/WPF application.
I created 4 projects. The first being the Silverlight 5 application itself, the second, third, and forth are silverlight class libraries for defining all the theme particulars. Each class library has an entry point which is a derived type of ResourceDictionary.
On AppStart event, the application loads the default theme class library, which is essentially a blank slate with all default styles defined in Silverlight. By loading I mean the the DefaultTheme resource dictionary defined within the class library is added to the App.Current.Resources.MergedDictionaries collection.
When the user selects another theme from a combo box within the app, the code removes the existing default theme and adds the blue or red, or whatever other theme's entry point resource dictionary to the App.Current.Resources.MergedDictionaries collection.
However, even though no errors have been thrown when this action occurs, the styles themselves are never re-applied. I have verified that each theme has the same style keys across the board.
Any ideas on how to force the App.Current.RootVisual re-apply the styles from the newly added resource dictionary after a "theme switch" ?
Thanks,
Try searching for the current ResourceDictionary first and removing it before adding the new ResourceDictionary.
string themeName = "White";
string oldThemeName = "Black";
string oldResourcePathString = String.Format("/Library.Name;component/Themes/{0}Theme.xaml", oldThemeName);
StreamResourceInfo sriOldTheme = Application.GetResourceStream(new Uri(oldResourcePathString, UriKind.Relative));
if (sriOldTheme != null)
{
StreamReader sr = new StreamReader(sriOldTheme.Stream);
object resourceObject = XamlReader.Load(sr.ReadToEnd());
ResourceDictionary resource = resourceObject as ResourceDictionary;
if (resource != null)
{
Application.Current.Resources.MergedDictionaries.Remove(resource);
}
}
string resourcePathString = String.Format("/Library.Name;component/Themes/{0}Theme.xaml", themeName);
StreamResourceInfo sriTheme = Application.GetResourceStream(new Uri(resourcePathString, UriKind.Relative));
if (sriTheme != null)
{
StreamReader sr = new StreamReader(sriTheme.Stream);
object resourceObject = XamlReader.Load(sr.ReadToEnd());
ResourceDictionary resource = resourceObject as ResourceDictionary;
if (resource != null)
{
Application.Current.Resources.MergedDictionaries.Add(resource);
}
}
I never tested the code, so check for typos, but this should work whether you set the ResourceDictionary in App.xaml or programatically from MainPage.xaml.cs
I've added a XAML file to a Windows Phone 8 project. Its build action is "Page". I want to load the XAML as a text string (to feed into XamlReader.Load()). How can I accomplish this?
It's not available as a separate file in the XAP package; it's probably somewhere in the DLL.
When set to Page, the compiler will compile the XAML into BAML and add the BAML file as a resource to the assembly.
If you wish to get the original XAML back out from the BAML resource at runtime, then you will need to deserialize the BAML, and then serialize your object to XAML.
You can have a look at the Baml2006Reader, or a better option would be to use Application.LoadComponent which is what the InitializeComponent method uses internally. InitializeComponent is called by the partially generated class for your XAML code behind.
var uri = new Uri("/MyAppName;component/MyXaml.xaml", //Note extension: XAML, not BAML
UriKind.Relative);
Page rootObject = new Page(); //Assuming XAML root element is Page - it could be anything
Application.LoadComponent(rootObject, uri);
(assuming the root element of your XAML file is a Page).
You can then serialize the Page to a XAML string using the XamlWriter:
string xaml = XamlWriter.Save(rootObject);
Note that this is the XamlWriter in the System.Windows.Markup namespace, not System.Xaml. If your XAML has WPF types, then you should use this XamlWriter to avoid errors.
The desired scenario I'm trying to address goes like this:
there is a set of common styles defined in the MyCompany.Styles.dll assembly.
there is a set of custom controls in the MyCompany.Controls.dll assembly that use the styles in the MyCompany.Styles.dll assembly.
any application that uses the controls in the MyCompany.Controls.dll assembly will not need to reference any ResourceDictionary in the MyCompany.Styles.dll assembly in the application's app.xaml, but the styles will just resolve given the fact that they are referenced and used by the MyCompany.Controls.dll assembly.
Is this possible to do and if so, how?
Thanks much, eugen
It is possible, so I'm posting the solution for anyone who stumbles upon my original question.
The fix is to merge the desired styles into the ResourceDictionary of each of the custom controls in the MyCompany.Controls.dll assembly. While I weren't successful doing this in XAML, all it took was to add the call below into each custom control's constructor:
this.MergeInResources("/MyCompany.Styles;component/default.xaml");
where the extension method that does the magic is below:
/// <summary>
/// Loads the resources pointed to by the (relative) path
/// <paramref name="resourcePath"/> into the Merged Dictionaries
/// of <paramref name="control"/>.
/// </summary>
internal static void MergeInResources(this FrameworkElement control,
string resourcePath)
{
if (String.IsNullOrWhiteSpace(resourcePath))
throw new ArgumentNullException("resourcePath");
Uri uri = new Uri(resourcePath, UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(uri);
control.Resources.MergedDictionaries.Add(dictionary);
}
Error handling and ResourceDictionary caching + sharing is left to the reader.
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.