I have a control for which i want to declare resources in a xaml file. if this was a user control i could put the resources in an <UserControl.Resources> block and reference them in the code via this.Resources["myResourceKey"] how do i achieve the same functionality in a control. at the moment the only link to xaml i have is through the controls static constructor, to reference the style (and control template)
static SlimlineSimpleFieldTextBlock() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(SlimlineSimpleFieldTextBlock), new FrameworkPropertyMetadata(typeof(SlimlineSimpleFieldTextBlock)));
}
but even if i add a block to the xaml <Style.Resources> I dont seem able to reference them (as the Style is null at the OnApplyTemplate stage) and even if i did it would mean if someone eles overrode the style i would lose my resources.
Construct your resource key using ComponentResourceKey. Normal resource keys are searched for only up the visual tree and in your application resources. But any resource key that is a ComponentResourceKey is also searched for in the theme dictionary for the assembly containing the type. (This is also true for Type objects used as resource keys.)
In your Themes/Generic.xaml of the assembly containing a control called "Sandwich" you might have:
<SolidColorBrush x:Key="{ComponentResourceKey local:Sandwich, Lettuce}"
Color="#00FF00" />
<ControlTemplate x:Key="{ComponentResourceKey local:Sandwich, PeanutButter}" ...>
...
</ControlTemplate>
You can reference these resources in code like this:
var lettuce = (Brush)FindResource(
new ComponentResourceKey(typeof(Sandwich), "Lettuce"));
var penutButter = (ControlTemplate)FindResource(
new ComponentResourceKey(typeof(Sandwich), "PeanutButter"));
You can also refer to these resources in XAML like this:
<Border Background="{StaticResource ResourceKey={ComponentResourceKey local:Sandwich, Lettuce}}" />
Both of these forms of reference work from anywhere that FindResource can be used, which is inside the code or XAML for any object derived from FrameworkElement, FrameworkContentElement or Application.
Additional notes
The search algorithm for a ComponentResourceKey resource involves only the assembly contaning the specified type, not the type itself. Thus a control of type Soup could use a ComponentResourceKey of {ComponentResourceKey local:Sandwich,Seasonings} if the Soup and Sandwich classes were in the same assembly. As long as everything about the ComponentResourceKey matches exactly and the resource is actually in the same assembly as the given type, the resource will be found.
Also note that although it is possible to use pack URI to load a ResourceDictionary from another assembly, it is a bad idea to do so. Unlike the Themes/Generic.xaml solution you actually have to modify the application using your controls, and it also suffers from multiple-inclusion and overridability problems.
Whenever you are using Themes/Generic.xaml you must have your ThemeInfoAttribute set correctly on that assembly. You can start with this in your control library's AssemblyInfo.cs:
[assembly:ThemeInfoAttribute(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
Related
I am new to WPF and MVVM, actually started just a week back and I am trying to code up an application using both WPF and MVVM, while coding up an example I came across the following statement <vm:SimpleViewModel x:Key="viewModel"/> and I am trying to reason about it. I understand what 'x:' refers to, its the default XAML namespace mentioned in the XAML file and I have created a namespace for my own ViewModel class that the UI will be interacting with and I have given it an alias "vm" and SimpleViewModel is the ViewModel for my application, the statement for the purposes of reference is xmlns:vm="clr-namespace:MVVM_Tutorial".
My Reasoning for the statement <vm:SimpleViewModel x:Key="viewModel"/> is that a Window is a XAML element and has a resource dictionary that it refers to resolve and refer to certain elements, hence inside its resource dictionary which is defined in the "x:" namespace we are assigning a variable called "Key" whose value is the SimpleViewModel class defined in the "vm:" namespace. Just want to know if I am right with my reasoning or is there something that I am missing and would want to know proceeding further from here.
XAML is just markup that describes an object graph. Code is also markup that describes an object graph. I can say this
var window = new Window();
window.DataContext = new MyNamespace.MyViewModel();
or I can write the exact same thing like this
<Window xmlns:blahblah="clr-namespace:Normal.Xmlns.Deleted.For.Brevity"
xmlns:this="clr-namespace:MyNamespace">
<Window.DataContext>
<this:MyViewModel />
<!-- snip -->
Any object that can be instantiated in code can be used in xaml. There are some restrictions (e.g., default public constructor without arguments), but for the most part this is true. XAML just defines an object graph that is deserialized at runtime.
Since any type can be referred to in xaml, you could, hypothetically, have moved that instance of MyViewModel to a resource dictionary and referred to it via a StaticResource or a DynamicResource. Note, anything you put in a resource dictionary has to have a key, assigned via x:Key:
<Window xmlns:blahblah="clr-namespace:Normal.Xmlns.Deleted.For.Brevity"
xmlns:this="clr-namespace:MyNamespace"
DataContext="{DynamicResource lolderp}">
<Window.Resources>
<this:MyViewModel x:Key="lolderp" />
<!-- snip -->
XAML is a subset of XML, and uses XML namespaces to map to code namespaces in the current, or other, assemblies. It's how the framework knows what object MyViewModel refers to. To learn more, read this link on msdn.
I'm sure someone else can chime in with more clarification...
In the xaml file, the references of
"xmlns:[something]="clr-namespace:[yourProjectOrLibrary]".
Since your code-behind can be verbose with long name space references, and your SOLUTION may be made up of multiple projects (such as different DLLs), when the XAML is processed, it uses the "xmlns" as a reference to whatever "yourProjectOrLibrary" is... In your case the project/class "MVVM_Tutorial".
Now, the "vm". This is just an "alias" within the xaml, so anytime it is referencing a
The xaml knows where it originates to get resolution to control, properties, types, etc.
As for the "x:Key" part... Not positive, but when I was first building out my customized themes, also ran into confusion about the x:Key. My interpretation of this was found to be x:Key is like a private reference, but by being given the name ..x:Key="viewModel"... is making this "name" available later within the xaml file.
This "key" can then be referenced later in the xaml... For example,
<ControlTemplate x:Key="CTButton" TargetType="{x:Type Button}" >
<!-- Start border of button to have a rounded corners -->
</ControlTemplate>
Then later within the theme, I could reference this "Key"... in my case "CTButton". So if I wanted multiple controls to use / derive from same control template, I could have them reference it...
<someControl>
<Style>
<Setter Property="Template" Value="{StaticResource CTButton}" />
</Style>
</someControl
Again, I don't get EVERYTHING about all the xaml markup, but hopefully clarifies this for you some.
I'm creating the custom control. And suddenly faced a question: why to put control's style separately from the control?
I mean that using the standard way you must: derive from base control (for example, from TextBox) and add a style for it in general.xaml.
But why can't we do just like this:
<TextBox x:Class="CustomTest.CoolTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox.Style>
<Style>
<Setter Property="TextBox.FontSize" Value="20" />
</Style>
</TextBox.Style>
</TextBox>
And code-behind:
public partial class CoolTextBox : TextBox
{
public CoolTextBox()
{
InitializeComponent();
}
}
Update #1
I'm not writing my own library but creating a custom control inside my main executable. Application support themes but they differ only by colors. So each theme is a set of brush resources and my style will refer them using DynamicResource.
What I want to know is the drawbacks of that solution. I mean performance, simplicity of usage and etc.
WPF allows changing themes at runtime, means the style shall be stored separatly from the controls. Futhermore the control shall be lookless when designining in order to other programmers to have their custom styles though somewhere there should be a default style which must be stored separatly in a Generic.xaml file. If your app doesn't support changing themes then you can define the style wherever you wish.
If you are writing a library of custom controls I suggest you to stick to standards.
Here is a link how shall a custom controls be created:
http://wpftutorial.net/HowToCreateACustomControl.html
In addition to dev hedgehog's answer about performance, I found the following drawbacks:
Style object is own for each instance of control. So you end up with number of clones of the same Style object.
You cannot override style using BasedOn property. Only completely replace is possible.
I use a class library (WPF user control library) to host some user controls which other (C#-) applications in the project solution consume. I want these controls to use XAML ControlTemplates residing at the top scope of the class library. The ControlTemplates do not have to be consumed outside the class library.
Here a template declaration:
<ControlTemplate TargetType="{x:Type Button}" x:Key="TemplateImageButtonSmall">
<Grid>
<Image Name="img" Source="/PSCommonUI;component/Images/Buttons/ButtonMinus_normal.png"/>
</Grid>
</ControlTemplate>
Then I have a user control in the class library, containing:
<Button Height="57" Margin="10,0,6,5" Name="button3" Template="{StaticResource TemplateImageButtonSmall}" Width="82">
In an application, I can use the App.xaml file for defining the templates. However, in a class library I don't have this option.
I have searched the web and found some answers including the use of a generic.xaml file, ComponentResourceKey, merging resource files and other stuff I find exaggeratedly complicated.
Also I read that theme definitions (resources in general) shouldn't reside in a class library.
But if I need some themes ONLY in this class library for the there hosted controls, how is best practice then?
Thanks in advance,
Julian
I am not sure what you meant, however, if you want child UIElements from a specific UIElement and below to use control templates, then you can define the templates in a resource dictionary and merge the dictionary into the top control that you want the dictionary to be visible to.
Edit:
The assembly just contains the classes and resources within it. It has no events of its own (e.g. OnApplicationLoaded).
A control's XAML can contain resources of its own (e.g. control templates) for consumption by itself and child controls and thus define default styling.
Your application can merge the resource dictionaries into any level of the tree (application, window, control, ...) and thus override defaults.
If you want the styling to be dynamic (overrable by importing resource dictionaries) then using the DynamicResource keyword your XAML. If your resource is defined in the same XAML and can not be overridden then use the StaticResource keyword.
Add a resource dictionary to your class library and define your resources (templates) there. It doesn't have to be generic.xaml.
Then in each user control or other .xaml file reference the resource dictionaries you require using Xaml similar to:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="... path to dictionary 1"/>
<ResourceDictionary Source="... path to dictionary 2"/>
<ResourceDictionary Source="... etc"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
You can then use resource keys from the merged dictionaries.
I'm trying to learn Silverlight here, creating a custom control template, however VS2010 refuses to recognize the ControlTemplate type in markup code, even though I have referenced the System.Windows and System.Windows.Controls assemblies (which is by default when basing the project on the standard Silverlight Application template). I'm trying to recreate this seen on another SO stack.
I've tried putting this code directly into a file (i.e. ImageButton.xaml) and nothing else:
<ControlTemplate x:Key="ImageButtonTemplate">
<Image Source="{TemplateBinding Content}" />
</ControlTemplate>
It's a bit hard to answer this question authoritatively without knowing a little more context, such as what type of file you are placing this in, and what the exact error is from Visual Studio. I imagine that you're getting an error such as:
The type 'ControlTemplate' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.
or possibly:
Property 'Content' does not support values of type 'ControlTemplate'
These are caused by placing the template in the wrong place - for example, if you create a new UserControl (via Add -> New Item) and delete the contents of the file and paste in your code, then you will get this error, since the xaml has no references to ControlTemplate.
The best place to put your ControlTemplate is somewhere reusable, such as a new "Resource Dictionary" (again, add it via Add -> New Item -> Silverlight Resource Dictionary) and then place your code inside the <ResourceDictionary ...></ResourceDictionary> tags.
If you want to place it within a UserControl (the source of the second error) then you should add it to the Resources section of that control, for example:
<UserControl.Resources>
<ControlTemplate x:Key="ImageButtonTemplate">
<Image Source="{TemplateBinding Content}" />
</ControlTemplate>
</UserControl.Resources>
I do not know how to use a defined Application Style Resource in code.
I have defined:
<Application.Resources> <Style x:Key="OrangeButton" TargetType="{x:Type Button}">
I am using this resource in the XAML section of my application like:
<Button Name="Button_Start" style="{StaticResource OrangeButton}" Margin="0">
and it is working fine, and I can also change the Button Style by code
Button_Start.Style = CType(FindResource("RedButton"), Style)
but only in the VB file that was automatically created when I created my new project. If I add a new class file and try do the same it says:
Name 'FindResource' is not declared
My problem is therefore, how to make use of Application resources in all the different class files in my application.
Peter
The FindResource method is defined by the FrameworkElement class, so it will only be available if your class extends that or you have an instance of FrameworkElement from which to start your resource lookup.
However, if you know your resource resides at the Application level, you can use either TryFindResource or Resources as follows (C#, but should be easy to infer the VB):
object resource;
if (Application.Current.TryFindResource("RedButton", out resource))
{
Style indirectStyle = (Style)resource;
}
//or use this
Style directStyle = Applications.Current.Resources["RedButton"] as Style;
Thank you for the answer, I am not sure I understand all you are writing, but I have it to work now with the code
MyButton.Style = CType(Application.Current.Resources("GreenButton"), Style)
Very nice.
Peter