Difficulties with ThemeInfo in WPF? - wpf

I am stuck with ThemeInfo attributes inside my AssemblyInfo.cs file. I am trying to write a custom control. The custom control is in a dll called "MyCustomControls.dll". Futhermore the control itself derives from another custom control which is inside another dll called "MyAnotherCustomControls.dll".
I read on MSDN that ThemeInfo must be declared with 2 parameters responsible for control theme specific location and generic specific location, though I have no idea what is "theme" and what "generic". How can I understand those two better?
If somebody could explain me an example from scratch what is "generic" and what "theme". Futhermore when to use "generic" or "theme". How are those two used by WPF system at all? I really need an explaination in plain english so please save your time if you are about to post me an MSDN link. I read msdn documentation about ThemeInfo and I dont get it.
Also can somebody please tell me how to just by using ThemeInfo tell my "MyCustomControls.dll" to use dictionary resources defined in "MyAnotherCustomControls.dll"? Is that even possible to do just by ThemeInfo or do I have to deal with MergedDirectories in "MyCustomControls.dll"? I would like the WPF system take care of locating resources so that I can use style keys from "MyAnotherCustomControls.dll"without the need to add merged directory in "MyCustomControls.dll".

I read on MSDN that ThemeInfo must be declared with 2 parameters responsible for control theme specific location and generic specific location, though I have no idea what is "theme" and what "generic". How can I understand those two better?
Basically the WPF framework will look for a xaml resource whose name matches the OS theme name. So if you're running on XP with the blue theme that will be "luna.normal.xaml". If it does not find one with that exact name, it will look for the "generic.xaml". In actuality I think they look for the "classic.xaml" first if they don't find one that matches the OS specific one and then they look for generic.xaml. You can think of the generic.xaml as the default resources.
The ThemeInfo attribute simply tells WPF where those resources are defined. There are 3 options:
None - You aren't providing either the "theme" or "generic" resources. This is really an optimization so the WPF framework doesn't bother looking for them. You wouldn't typically use this for the GenericDictionaryLocation but you might use this for the ThemeDictionaryLocation in case you weren't going to define OS theme specific resources (i.e. you didn't want your control to look different under different OS themes). Typically control vendors will define a resource dictionary for each possible OS theme so that the controls look consistent with the other controls and windows running in that operating system.
ExternalAssembly - This means that you are defining a separate assembly for the resources. So if you look at the WPF framework you will see that they use this for the PresentationFramework assembly for the ThemeDictionaryLocation. Then they defined a separate assembly for each OS theme they want to support (e.g. PresentationFramework.Aero.dll, PresentationFramework.Luna.dll, etc.). The name of the assembly it will search for is the name of the defining assembly plus the name of the theme.
SourceAssembly - This means that the resources are defined within the assembly itself. So in that assembly you would have a "themes" folder which contains the resource dictionaries.
This article on MSDN about control authoring really isn't that bad about providing information about this.
Also can somebody please tell me how to just by using ThemeInfo tell my "MyCustomControls.dll" to use dictionary resources defined in "MyAnotherCustomControls.dll"? Is that even possible to do just by ThemeInfo or do I have to deal with MergedDirectories in "MyCustomControls.dll"? I would like the WPF system take care of locating resources so that I can use style keys from "MyAnotherCustomControls.dll"without the need to add merged directory in "MyCustomControls.dll".
You can't use ThemeInfo to tell WPF that it should look for your resources in some arbitrary assembly. That being said if you don't set or override the DefaultStyleKey, as is typically done when defining a custom control, then it should continue to use the resources for the base class that did have its DefaultStyleKey set/overridden for the default resources.
It should be noted however that local Style resolution (i.e. when the Style property of your control is not set and WPF looks from the place at which the element is sited and walks up the visual/logical tree looking for a Style that might implicitly affect that element) will always look for Style whose Key matches the exact type of the class. So if have a Style whose TargetType (and therefore the default Key when that is placed in a ResourceDictionary) is TextBox defined in the Resources of your Window, it will affect all TextBox instances within that Window (unless they have a Style closer in the visual tree - i.e. defined in the resources of an element between itself some ancestor - or its Style property is set). However, if you have a class that derives from TextBox (e.g class MyTextBox : TextBox) then it will not pick up/use that Style. Instead it will be looking for a Style whose TargetType/Key is typeof(MyTextBox). One way to hack around that would be to set the Style property to a DynamicResource to the base type. e.g.
public MyTextBox()
{
this.SetResourceReference(StyleProperty, typeof(TextBox));
}
Basically this sets a local value on the control's Style property that is doing a dynamic resource lookup for a Style whose Key (and therefore TargetType for Styles where the x:Key is not set) is the specified type (TextBox in this case).
As you noted the alternative is to define the xaml files locally in your assembly for each theme that the base class' assembly defines and then add a ResourceDictionary to its MergedDictionaries that uses the pack uri notation to reference the resources in the base class's assembly. If you are setting the DefaultStyleKey then you will likely need to define a Style whose TargetType is your class' type in each of those ResourceDictionaries and then set the BasedOn to a StaticResource where the resource key is the type of the base class. It sounds though like you shouldn't need to do this.

Related

Set the Style of a control programatically in Silverlight 4?

I am new to Silverlight, but I couldn't find anything about this when I googled it.
I have a button that I am trying to set the style programatically. I have the style defined in a XAML file and I want to pull the style into C# so I can dynamically create a button and assign it this style. So far, this is what I am trying:
button.Style = (Style)Resources["CloseButtonStyle"];
However, it just makes the button have no style. Is there an easy way to do this? I feel like this should be obvious, but I can't get it to work.
You are assuming that your Resources property on the current object is the one that contains the defined style. However, I assume, given the symptoms of your issue, that CloseButtonStyle is actually defined further up the control hierarchy.
Instead, you need to traverse your control hierarchy until you find the resource (or if you know the object that defines it, just refer directly to that object). Unfortunately, Silverlight doesn't include FindResource call like WPF, but it's not too difficult to implement your own.
I can call button1.Style = (Style)Resources["NonExistentKey"]; and it makes my button have no style at all as well, point being that the resource is probably not being found, you won't get an exception.
You directly access the Resources property, but is the style really in the immediate resource dictionary of your Window/UserControl/whatever-you-have?

please explain what is themes and generic.xaml? [duplicate]

I've been trying to figure out how to organize my ResourceDictionary files for reuse and sharing with other members of my team.
I keep coming across "Generic.xaml", but if I look on MSDN for Generic.xaml or just do a Google search, I only seem to get blog posts and forum questions that happen to mention it--I can't seem to hit upon anything really authoritative and clear.
What is the difference between Generic.xaml and MyRandomlyNamedResourceDictionary.xaml? It seems like either way, I have to reference ResourceDictionaries stored in libraries with the Source attribute. E.g.,:
<Application.Resources>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml"
</Application.Resources>
So what advantage does Generic.xaml provide exactly? Does it have any purpose if I'm not trying to give my application multiple "looks" (i.e., if I have only one theme)?
Every Control in WPF has a default Style that provides, among other things, the Control's default ControlTemplate. WPF looks for the default style in a special resource dictionary in the Themes folder in the same assembly as the control. The key for the default style is provided by the Control.DefaultStyleKey dependency property, the default value of which is overridden in each sub-class of Control.
The name of the resource dictionary depends on the current Windows theme e.g. on Vista using the Aero theme, the dictionary is called Aero.NormalColor.xaml, on XP using the default theme it is Luna.NormalColor.xaml. If the style is not found in the theme dictionary, it looks in Generic.xaml i.e for controls whose look doesn't depend on the theme.
This only applies to any custom controls you have defined i.e. classes derived from Control, directly or indirectly. You can change the default style for a standard control by deriving from it and calling DefaultStyleKeyProperty.OverrideMetadata in the static constructor, but you then have to supply the full style including ControlTemplate.
Note that you can tell WPF to look in an external assembly for your default style by using the ThemeInfo attribute. The external assembly must be named <YourAssembly>.<ThemeName>.dll e.g. PresententationFramework.Aero.dll.
For a generic.xaml file (case insensitive) to be something special, two conditions must be met:
It must be in the Themes sub-root folder in the project
The assembly must be marked with the ThemeInfoAttribute (usually in AssemblyInfo.cs)
Then it serves as the default lookup location for any default styles you wish to apply to your Controls. Note also that for a style to be the default it must declare both its TargetType and x:Key as the Type of Control which is to be styled.
If you wish to add entire themes and theme switching to your application, that is accomplished with some coding, this technique merely defines the default resource dictionary.

Why can't I use the Name attribute on UserControl in the same assembly?

When I created a WPF UserControl and tried to use it, I got the following compiler error:
Because 'UserControl1' is implemented in the same assembly, you must set the x:Name attribute rather than the Name attribute.
The message is clear on what to do to fix it, but what is its cause? Why can't I use Name here?
x:Name is simply a more explicit way of saying "The name attribute in this specific XML namespace". The fact that WPF can't compile it without being given this hint because it's in the same assembly is just a limitation of how they wrote the parser.
If you are asking why it is this way, I do not know for sure because I didn't write it. It probably has something to do with it needing to be able to resolve the Name attribute (read: Dependency Property) to something concrete BEFORE building your UserControl1, in other words, a catch-22.
In the beginning the XAML compiler was written to enable to creation of “trees” of .net objects, there were 101 projects within Microsoft that used XAML. The XAML compiler does not know anything about WPF.
The “Name” property is defined by WPF and is not known about by the XAML compiler. WPF maps the name property to be the same as the “Name TAG” that is supported by the XAML compiler. The “x” in “x:name” is saying use “name” as defined by the XAML xml schema, “Name” says find a property called “name” on the given object. See In WPF, what are the differences between the x:Name and Name attributes? for more details.
The XAML compiler is very limited in what it can do with a user control without having to load the assembly that defines the user control. As the XAML needs to be compiled before the assembly can be loaded, the xaml compiler clearly can’t load the assembly for a control that is implemented in the same assembly. Therefore the XAML compiler does not even know the item is a user control.
Properties that are defined on a user control (or its parent class) can therefore not be accessed. “Name” is a property that is defined in the parent (or super-parent) of a custom control.
The XAML compiler could just say “Name is not defined as a property”; if it did, think about how many people will fail to get a simple custom control working! Therefore the XAML compiler has a special case that gives a more useful error message, by “guessing” what the code means. Its guess are mostly correct.
Anything apart from the most simple user control needs to be in its own assembly, however user simple control are so common that a special case was considered worthwhile for them.

How to expose xaml properties?

I created a ComboBox subclass and added my functionality.
Now I want to expose external properties of the TextBox for example:
<a:MyComboBox SpellCheck.IsEnabled="True" TextBox.SelectedText="{Binding X}" />
Is this possible, I maybe didn't choose the wrong particular property but I guess you understand what I mean.
Is this possible?
Do I have to create all the properties individually?
This is not possible in XAML. XAML does not allow you to address sub-properties of individual members using a property path syntax like the one you describe. (Something similar is possible for certain inheritable properties such as backgrounds and font sizes, but that uses an inheritance mechanism -- affecting all contained controls -- rather than addressing a specific sub-element, and wouldn't work for TextBox.SelectedText anyway.)
So yes, you will need to declare custom properties on the MyComboBox class to surface the features of the TextBox that you need to access from outside the MyComboBox. On the plus side, this is good discipline for encapsulation: remember that a future developer might apply a custom template to your MyComboBox that doesn't include a TextBox (or the member isn't named TextBox, or the member named TextBox is actually a RichTextEditor instead of a plain TextBox...). Explicit properties ensure that MyComboBox defines clearly what behaviour and state such a template needs to respect, and lets the template decide how to implement that, rather than the author of the template being constrained always to expose a TextBox.

What is so special about Generic.xaml?

I've been trying to figure out how to organize my ResourceDictionary files for reuse and sharing with other members of my team.
I keep coming across "Generic.xaml", but if I look on MSDN for Generic.xaml or just do a Google search, I only seem to get blog posts and forum questions that happen to mention it--I can't seem to hit upon anything really authoritative and clear.
What is the difference between Generic.xaml and MyRandomlyNamedResourceDictionary.xaml? It seems like either way, I have to reference ResourceDictionaries stored in libraries with the Source attribute. E.g.,:
<Application.Resources>
<ResourceDictionary
Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml"
</Application.Resources>
So what advantage does Generic.xaml provide exactly? Does it have any purpose if I'm not trying to give my application multiple "looks" (i.e., if I have only one theme)?
Every Control in WPF has a default Style that provides, among other things, the Control's default ControlTemplate. WPF looks for the default style in a special resource dictionary in the Themes folder in the same assembly as the control. The key for the default style is provided by the Control.DefaultStyleKey dependency property, the default value of which is overridden in each sub-class of Control.
The name of the resource dictionary depends on the current Windows theme e.g. on Vista using the Aero theme, the dictionary is called Aero.NormalColor.xaml, on XP using the default theme it is Luna.NormalColor.xaml. If the style is not found in the theme dictionary, it looks in Generic.xaml i.e for controls whose look doesn't depend on the theme.
This only applies to any custom controls you have defined i.e. classes derived from Control, directly or indirectly. You can change the default style for a standard control by deriving from it and calling DefaultStyleKeyProperty.OverrideMetadata in the static constructor, but you then have to supply the full style including ControlTemplate.
Note that you can tell WPF to look in an external assembly for your default style by using the ThemeInfo attribute. The external assembly must be named <YourAssembly>.<ThemeName>.dll e.g. PresententationFramework.Aero.dll.
For a generic.xaml file (case insensitive) to be something special, two conditions must be met:
It must be in the Themes sub-root folder in the project
The assembly must be marked with the ThemeInfoAttribute (usually in AssemblyInfo.cs)
Then it serves as the default lookup location for any default styles you wish to apply to your Controls. Note also that for a style to be the default it must declare both its TargetType and x:Key as the Type of Control which is to be styled.
If you wish to add entire themes and theme switching to your application, that is accomplished with some coding, this technique merely defines the default resource dictionary.

Resources