I am busy building a custom control. The Theme has a Generic.xaml that consist of a MergedDictionary referencing 2 resource dictionaries (1 - Generic brush and 2 - ControlTemplate) which is situated in a Generic folder.
I have a tester project (standard Window) added to the solution to test the control.
Issue:
When I add the control for the first time - the look of the control looks correct. However when I do some changes on the control and rebuild it - the control disappear from the window and I have to Unload the Window project and Reload it again to make the control's look reappear again.
When I run debugger the control does appear correctly - it is just in design mode that it becomes difficult to work with.
Is there a solution / workaround for this occurrence that does not involve unloading and reloading the window on each rebuild?
EDIT
I have run a test where I copied all the info from separate resource dictionaries into the Generic.Xaml and commented out the Merge Dictionary. It seems the issue does not lie with MergeDictionary operations as the problem is still there - but perhaps with ComponentResourceKey / or static properties. One of my ResourceDictionaries for instance contain a lot of the following
<SolidColorBrush x:Key="{x:Static keys:Disabled.ForeGroundKey}" Color="Gray"/>
Where ForeGroundKey is linked to a static class with for example:
public static class Normal
{
static ComponentResourceKey _background = new ComponentResourceKey(typeof(G2ListBox),"ContainerBackground");
public static ComponentResourceKey BackGroundKey
{ get { return _background; } }
}
I guess seeing that the theme work sometimes mean that there is nothing wrong with the above approach and there is something wrong with how VS handles the rebuild of the control. What I do not understand though is why doesn't VS recognize either the old values / new values, instead it ignores all values linked to ComponentResourceKey - Ps. during runtime the control works perfectly.
Well it seems like I have 3 options here.
Use the method I have been using all along:
Define ComponentResourceKey in C# Class
public static class Normal
{
static ComponentResourceKey _background = new ComponentResourceKey(typeof(G2ListBox), "Normal.Background");
public static ComponentResourceKey BackGroundKey
{ get { return _background; } }
}
Assign Key in Resource Dictionary
<SolidColorBrush x:Key="{x:Static keys:Normal.BackGroundKey}" Color="Yellow"/>
Use Key in Resource Dictionary:
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static keys:Normal.BorderBrushKey}}"/>
The advantage is that when you have multiple themes to assign and use the resource key seems simplistic and chances of errors occurring in typing over the name is reduced as you have the help of intelisence.
The disadvantage is that when you are working on a large project, and you make a small adjustment to your control - none of the ComponentResourceKeys are loaded and your project looks completely lookless at design time. To fix this issue, one either has to reboot VS or unload the project that uses your control and reload it again. Ps this is only at design time. Running the project will give the correct result. This is a silly problem to have in VS!!
return to the more verbose method of defining Component resource keys in XAML i.e.
Define and Assign the resourceKey.
<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:G2ListBox}, ResourceId=Normal.Background}" Color="Yellow"/>
Use the resourceKey:
<Setter Property="BorderBrush" Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type local:G2ListBox}, ResourceId=Normal.Background}}"/>
The Advantage is VS is now working everytime in design time. Also you do not have to create a separate C# class to hold all your resourcekeys
The Disadvantage is that you have to remember the ResourceId names for each resourceId and type it out as it was defined. Also using this in a control with multiple themes becomes frustrating.
Use a mixture of the 2 above i.e.
You still Define the ResourceKey in C# library
You Assing the ResourceKey as per method 2. But the ResourceId is the "text" field assigned in the C# class and not the x:static method i.e.
public static class Normal
{
static ComponentResourceKey _background = new ComponentResourceKey(typeof(G2ListBox), "Normal.Background");
public static ComponentResourceKey BackGroundKey
{ get { return _background; } }
}
//To assign
<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:G2ListBox}, ResourceId=Normal.Background}" Color="Yellow"/>
//Thus Normal.Background and not Keys:Normal.Background!! where keys = referenced to the C# Class
Then to use
<Setter Property="Background" Value="{DynamicResource {x:Static keys:Normal.BackGroundKey}}"/>
//i.e we now can reference the C# class and have intelisence
The advantage is that 1 you have a static class that hold all the ResourceKeys in C# (goes slightly against lookless philosophy). you also have access to proper intelisence at least in the using side. But best of all VS works perfectly in design time.
This however does not shorten the assign side at all and the disadvantages therefore are still that you need to type out verbose text to assign a color to the resourcekey. Having multiple themes each with its own set of colors means that you have only shorten the Style of the control a bit and seems silly to use this method
Thus if you want the best solution and do not care about the design time look I'd prefer option1.
If you prefer design time visuals then I'd go for method 2 unless you have to define the style at a number of places too, then option 3 will suffice.
Alternatively create a designtime ResouceDictionary and a compile time ResourceDictionary. Where at designtime method 3 is used and compile time method 2 -> Not sure how to do this automatically. I am doing this by use of Merged Dictionaries, and uncommenting the proper dictionary when Control is ready to be compiled, and deleting the designtime ResourceDictionary.
Hope this helps someone, some day as I had to spend the whole day trouble shooting this (I thought there was something wrong with my control - turns out there is something wrong with VS).
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 know how to expand all nodes from a treeview:
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="TreeViewItem.IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
the only problem is that it takes about a minute to expand all nodes... that's probably because there are so many items. How could I speed up this process?
Edit:
So I have a list: List<ScanItems> MyFilesList
ScanItem is a class that has properties such as: FullName, Name, Size, DateCreated, ComparePath and other specific properties that I need that's why I did not used the FileInfo class.
ScanFile is a class that inherits from ScanItems so it is just like it with the addition of other custom properties.
ScanDir is another class that inherits also from ScanItem and it has the following property:
public List<ScanItem> items{get;set;}
the reason why I have included that property is so that I can have another list withing an item.
Look at this question regarding how to populate a treeview from a list of files.
so now I hope I explain my self correctly on how I am binding that list to the treeview.
Now let me explain how I added the files to MyFilesList. I created a recursion method to look for files in a directory. If the curent directory contained a file then add a ScanFile item. If it contained a Folder then add a ScanDir object and call the same method again passing the list of ScanDir. So this process takes about 8 seconds to scan my external hard drive. after that method get executed my list may contain just 4 items but one of those items will contain a list of maybe 20 items and so forth just like a folder may have 5 items and if one of those 5 items happens to be a folder that folder can have additional items.
So when I execute TreeView.DataContext = MyFilesList the treeview gets populated in less than a second. But when I include:
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="TreeViewItem.IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
that style inside the treeview the treeview takes to long to load.
Take a look at the posts made by Bea Stollnitz about treeviews and performance.
Relevant post would be:
How can I expand items in a TreeView? Part I / Part II / Part III
Are there any tricks that will help me improve TreeView’s performance? Part I / Part II / Part III
She does a good job explaining in detail all of the options you can try out. Too much content to put it all here.
Have you tried looping through the treeviewitems and expanding them "manually" by setting
IsExpanded = true;
If that doesn't work then try a work around and add to your ScanDir ( I presume that's the only class which you expand? ) a property IsExpanded (or similar) and bind to it in your template. Not the best of solutions practise wise but if it would work...
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)]
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
I am trying to set the default Style for every window in my WPF Windows application in my app.xaml. So far i have this in app.xaml:
<Application.Resources>
<ResourceDictionary>
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
<Setter Property="Background" Value="Blue" />
</Style>
</ResourceDictionary>
</Application.Resources>
I can get the window to appear with this style when running the app (but not in VS designer) by specifically telling the window to use this style via:
Style="{DynamicResource WindowStyle}
This works, but is not ideal. So how do I:
Have all windows automatically use the style (so i don't have to specify it on every window)?
Have VS designer show the style?
Thanks!
To add on to what Ray says:
For the Styles, you either need to supply a Key/ID or specify a TargetType.
If a FrameworkElement does not have an
explicitly specified Style, it will
always look for a Style resource,
using its own type as the key
- Programming WPF (Sells, Griffith)
If you supply a TargetType, all instances of that type will have the style applied. However derived types will not... it seems. <Style TargetType="{x:Type Window}"> will not work for all your custom derivations/windows. <Style TargetType="{x:Type local:MyWindow}"> will apply to only MyWindow. So the options are
Use a Keyed Style that you specify as the Style property of every window you want to apply the style. The designer will show the styled window.
.
<Application.Resources>
<Style x:Key="MyWindowStyle">
<Setter Property="Control.Background" Value="PaleGreen"/>
<Setter Property="Window.Title" Value="Styled Window"/>
</Style>
</Application.Resources> ...
<Window x:Class="MyNS.MyWindow" Style="{StaticResource MyWindowStyleKey}"> ...
Or you could derive from a custom BaseWindow class (which has its own quirks), where you set the Style property during the Ctor/Initialization/Load stage once. All Derivations would then automatically have the style applied. But the designer won't take notice of your style You need to run your app to see the style being applied.. I'm guessing the designer just runs InitializeComponent (which is auto/designer generated code) so XAML is applied but not custom code-behind.
So I'd say explicitly specified styles are the least work. You can anyways change aspects of the Style centrally.
Know this is years later, but since the question is still up here...
Create a resource dictionary in your project (Right-click the project...)
I'll create a new folder under the Project called "Assets" and
put "resourceDict.XAML in it.
Add the code to resourceDict.XAML:
<Style x:Key="WindowStyle" Target Type="Window" >
<Setter Property="Background" Value="Blue" />
</Style>
In your Project XAML file add the following under Window:
<Window.Resources>
<ResourceDictionary>
<!-- Believe it or not the next line fixes a bug MS acknowledges -->
<Style TargetType="{x:Type Rectangle}" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Assets/resourceDict.XAML" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
</Window.Resources>
ref the following web site: Trouble referencing a Resource Dictionary that contains a Merged Dictionary
"There is a bug: if all your default styles are nested in merged dictionaries three levels deep (or deeper) the top dictionary does not get flagged so the search skips it. The work around is to put a default Style to something, anything, in the root Dictionary."
And it seems to fix things reliably. Go figure...
And finally, under Window, maybe after Title, but before the final Window '>' :
Style="{DynamicResource windowStyle}"
And you'll need to add the code in steps 3 & 4 to every project to which you want the style to apply.
If you wanted to use a gradient background rather than a solid color, add the following code to the resourceDict.XAML:
<LinearGradientBrush x:Key="windowGradientBackground" StartPoint="0,0"
EndPoint="0,1" >
<GradientStop Color= "AliceBlue" Offset="0" />
<GradientStop Color= "Blue" Offset=".75" />
</LinearGradientBrush>
And modify your Style Setter for the background color to read:
<Setter Property="Background" Value="{DynamicResource
windowGradientBackground}" />
Steps 3 & 4 need to be repeated in each project.XAML file as described above, but hey, you get uniform Windows across the Solution! And the same process could apply to any controls you want to have a uniform look as well, buttons, whatever.
For anyone coming into this late, hope this helps as I'm sure the original posters got this all figured out years ago.
Paul
The designer is not working because you're specifying a DynamicResource. Please change this to StaticResource and all will be well.
To apply to all windows, you should remove the x:Key from the style. Setting the TargetType implicitly sets the x:Key to whatever is in TargetType. However, in my tests, this is not working, so I am looking into it.
If I set the TargetType to x:Type TextBlock, the designer works perfectly, it just seems to be the Window that is showing different behaviour.
You can add this code to your App.xaml.cs file:
FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata
{
DefaultValue = Application.Current.FindResource(typeof(Window))
});
After this, the style applied to the Window type will also apply to all types derived from Window
I investigated this one for some days now and made it work through the Constructor of my custom Window Class:
public class KWindow : Window
{
public KWindow()
{
this.SetResourceReference(StyleProperty, typeof(KWindow));
}
static KWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(KWindow), new FrameworkPropertyMetadata(typeof(KWindow)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// gets called finally
}
}
Hope it helps someone
For those this struggling with a solution to the problem: How can I have a custom style be automtically applied to all my Window derived types? Below is the solution I came up with
NOTE: I really didn't want to derive from the Window type or have to insert XAML on each window to force a style update etc. for reasons specific to my project (consumers of my product us my generic reusable style library and create their own layout/windows etc.) so I was really motivated to figure a solution out that worked that I was willing to live with any side effects
Need to iterate through all the instantiated windows and simply force them to use the new custom style you have defined for the Window type. This works great for windows that are already up but when a window or child window is instantiated it won't know to use the new/custom type that has been declared for its base type; the vanilla Window type. So the best I could come up with was to use the LostKeyBoardFocus on the MainWindow for when it loses Focus to a ChildWindow (IOW When a child window has been created) and then invoke this FixupWindowDerivedTypes().
If someone has a better solution for "detecting" when any kind of window derived type is instantiated and thus call the FixupWindowDerivedTypes() that would be great. There may be something useful with handling the WM_WINDOWPOSCHANGING in this area as well.
So this solution is not elegant per say but gets the job done without me having to touch any code or XAML related to my windows.
public static void FixupWindowDerivedTypes()
{
foreach (Window window in Application.Current.Windows)
{
//May look strange but kindly inform each of your window derived types to actually use the default style for the window type
window.SetResourceReference(FrameworkElement.StyleProperty, DefaultStyleKeyRetriever.GetDefaultStyleKey(window));
}
}
}
}
//Great little post here from Jafa to retrieve a protected property like DefaultStyleKey without using reflection.
http://themechanicalbride.blogspot.com/2008/11/protected-dependency-properties-are-not.html
//Helper class to retrieve a protected property so we can set it
internal class DefaultStyleKeyRetriever : Control
{
/// <summary>
/// This method retrieves the default style key of a control.
/// </summary>
/// <param name="control">The control to retrieve the default style key
/// from.</param>
/// <returns>The default style key of the control.</returns>
public static object GetDefaultStyleKey(Control control)
{
return control.GetValue(Control.DefaultStyleKeyProperty);
}
}
Considering Gishu's answer I figured out one more workaround. But it could be little bit weird.
If you use MVVM pattern you could remove code-behind of your window and x:Class markup in XAML file. So you will get an instance of window or your custom window but not a some instance of 'MainWindow' class that is derived from 'Window' class and marked as partial.
I'm making VS-like window so I had to inherit window class and extend it functionality. In that case it will be possible to make new window class as partial that would allow us to make code-behind without inheritance.
you will save all the styles in one xaml file(example design.xaml)
and then call that (design.xaml) xaml file in all the pages like this way
Like:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Design.xaml"/>
</ResourceDictionary.MergedDictionaries>