I have a class like this:
public class MyWindow : Window
{
static MyWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(typeof(MyWindow)));
}
}
and my MainWindow.xaml looks like this:
<Window.Resources>
<Style TargetType="{x:Type local:MyWindow}">
<Setter Property="Title" Value="Test" />
<Setter Property="Background" Value="Green" />
</Style>
</Window.Resources>
<Grid>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="215,124,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
</Grid>
I didn't include it to keep things brief, but the button_Click method just new's up a MyWindow and does a Show() on it.
Issues:
VS2015 designer shows the backround of the MAIN window (which is
still a Window, NOT a MyWindow) as green, but doesn't change the
title. The MAIN window does NOT show green at runtime, so I'm not sure why the designer is picking that up.
The MyWindow that gets shown by the button handler doesn't
get its title set and the background is Black / uninitialized.
Is there a way to define a window using a resource dictionary like I'm trying to do? I'm trying to do it this way so a user of my control (which needs a host Window) can customize the host window if they need to.
The following code implies that a window has a default style:
static MyWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(typeof(MyWindow)));
}
This default style should be defined in a resource dictionary called "generic.xaml", located in a folder called "Themes" at the root of your project. These names are by convention. So you should move your style to Themes/Generic.xaml. You may also want to base your style on the default one:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication7">
<Style TargetType="{x:Type local:MyWindow}" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="Title" Value="Test" />
<Setter Property="Background" Value="Green" />
</Style>
</ResourceDictionary>
How to create a custom window in WPF: https://blog.magnusmontin.net/2013/03/16/how-to-create-a-custom-window-in-wpf/
Related
If I have a simple childcontrol that already have a style defined for a element in the childcontrol.
Can I change that style of that element from the parent control?
If I dont set the style in the childcontrol I can override it from the parent control, but I cant seem to get it to work when the style is set.
I can't change the code in the childcontrol.
The example childcontrol:
<UserControl
x:Class="TestingThemes.ChildControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TestingThemes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<Style x:Key="aw" TargetType="Button">
<Setter Property="Foreground" Value="Blue" />
</Style>
</UserControl.Resources>
<Grid>
<Button
Width="100"
Height="50"
Content="Kalle Anka"
<!-- If I remove the style I can override it from parent control -->
Style="{DynamicResource aw}" />
</Grid>
</UserControl>
The parentcontrol:
<Grid>
...
<local:ChildControl>
<local:ChildControl.Resources>
<!--<Style BasedOn="{StaticResource local:ResourceKey=aw}" TargetType="Button">-->
<Style TargetType="Button">
<Setter Property="Foreground" Value="Red" />
</Style>
</local:ChildControl.Resources>
</local:ChildControl>
</Grid>
This is possible, but you must change the location of the initial Style.
The XAML engine will traverse the logical tree to lookup StaticResource/DynamicResource resources for an element (e.g., Button) starting locally, with the local ResourceDictionary.
This means, you want the default Style that is defined inside the UserControl to be found at the very end of the lookup route - after any potential custom styles that are intended to override the default Style. To accomplish this, you must move the Style either to App.xaml or Generic.xaml.
Additionally, the reference must be DynamicResource in order to defer the lookup to the moment the logical tree is composed. The overriding Style can be defined anywhere up the route towards the tree root.
Since Generic.xaml provides a different name scope, you would have to define a unique static (global in terms of the XAML name scopes) ComponentResourceKey which you would have to use as the style's x:Key value (in case you plan to move the default Style to the Generic.xaml file).
Because of the aforementioned XAML resource lookup behavior, creating a custom Control is always prefereable over a UserControl when you plan to make the control customizable (like in your case). The default Style of a custom Control is located in the Generic.xaml by default, thus you would have never ran into your current issue.
ChildControl.xaml
<UserControl>
<Grid>
<Button Style="{DynamicResource aw}" />
</Grid>
</UserControl>
App.xaml
The last lookup location before the Generic.xaml. If a Style with the same key was not found yet, the Style found in App.xaml will be applied.
<Style x:Key="aw" TargetType="Button">
<Setter Property="Foreground" Value="Blue" />
</Style>
ParentControl.xaml
<ChildControl>
<ChildControl.Resources>
<Style x:Key="aw" TargetType="Button">
<Setter Property="Foreground" Value="Red" />
</Style>
</ChildControl.Resources>
</ChildControl>
I am trying to make a library (with WPF) that will contain some of my basic templates for windows , buttons and some another new costume controls and templates for later use in projects .
I am still new to WPF . I understood that i should create a new ResourceDictionary in my external library and put there all my styles and their dynamic variables . And later make a MergedResourceDictionary in my main application project that will give me access to use my usual styles and controls .
Well i success to create a basic experimental style with a template which uses two variables as DynamicResource , Just for testing i made a <Brush x:Key="brush_back_standard">RoyalBlue</Brush> . This is used for regular back coloring . Then i declared to use this brush dynamically in the style itself : <Grid Background="{DynamicResource brush_back_standard}">, And It works well .
My problem begins when i am trying to change the brush value a runtime : Style.Resources[ "brush_back_standard" ] = Brushes.Aqua;. To simulate the case that the user will change the theme in the settings of the application later at runtime . So that didn't work at all , and i got the exception : ResourceDictionary is read-only and cannot be modified. . So if it's really readonly then it's sort of useless to me because the user cannot change the template at runtime . I need to find some way to let the user change anything in the window template at run time .
Full style code (external library) :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">
<Style x:Key="window_standard"
TargetType="{x:Type Window}">
<Style.Resources>
<system:Double x:Key="thickness_shadow">10</system:Double>
<Brush x:Key="brush_back_standard">RoyalBlue</Brush>
</Style.Resources>
<Setter Property="WindowStyle"
Value="None" />
<Setter Property="AllowsTransparency"
Value="True" />
<Setter Property="BorderThickness"
Value="{DynamicResource thickness_shadow}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid Background="{DynamicResource brush_back_standard}">
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode"
Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip"
Property="Visibility"
Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Accessing the library in my main application project :
<Application x:Class="diamond.sandbox.executer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="window_main.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/diamond.core;component/xaml/standard.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The window_main.xaml which is magically affected by the style :
<Window x:Class="diamond.sandbox.window_main"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="window_main" Height="250" Width="500"
Style="{DynamicResource window_standard}"
WindowStartupLocation="CenterScreen"
Loaded="initialise">
Initialise method (After window_main is loaded) :
private void initialise( object p_sender , RoutedEventArgs p_args )
{
Style.Resources[ "brush_back_standard" ] = Brushes.Aqua;
}
Well, you can't modify a Style (not even its Resources) at all because it has been frozen. The Style is a Freezable, and will be frozen by WPF when added to a ResourceDictionary. Instead, you should just modify the Window's Resources:
private void initialise( object p_sender , RoutedEventArgs p_args )
{
Resources[ "brush_back_standard" ] = Brushes.Aqua;
}
As I have multiple Windows in my application, I am looking for a solution that does not require me to set a binding on each Window.
I created a ResourceDictionary which has a style for the Window Background:
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="AliceBlue"/>
</Style>
In my XAML, I set the ResourceDictionary:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
There is no error, but my Window color stays white.
This appears to be caused by a combination of the order in which WPF loads/processes styles from nested ResourceDictionary, and the specifics of the Window class.
Assume MainWindow is defined as per your post. Now put the following in Templates.xaml:
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="{x:Type Window}" x:Key="myStyle">
<Setter Property="Background" Value="Green"/>
</Style>
If MainWindow has no style defined, then you will see that in the designer it appears with a red background. The designer is parsing the whole Xaml and loading the resource dictionary, and then drawing the results. The style is read before the window is drawn, and so the red background is applied.
When you run the application, the window is created before the ResourceDictionary is applied. It looks for a default style (a style with x:Key="{x:Type Window}") before the nested ResourceDictionary is processed, and finds nothing. Therefore at runtime, the window appears with default colour. (This is the behaviour described in the comments above.) Remember that the style with x:Key="{x:Type Window}" has a default value that matches the Windows style.
This is borne out if you use myStyle explicitly. If you add to your Window definition the attribute Style="{StaticResource myStyle}" you'll find that the designer fails, but you also get a run-time error, because myStyle hasn't been created at the time that the Window needs it. If you switch to Style="{DynamicResource myStyle}" then you'll see that it works as you hope, because DynamicResource will update once the ResourceDictionary has been parsed and the style included.
So, applying this, you can fix the problem in one way by adding this to your Window element: Style="{DynamicResource {x:Type Window}}" - but this is cludgy. The better solution is to include your resource dictionary in the app.xaml file, where it will be parsed before any window is opened and thus available to all:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The real issue here is that your Window is not really a Window: it is a class that derives from Window and will in fact be MainWindow, Window2, etc... This means that the automatic style wireup for a Window will never work in this way, and some level of manual binding will unfortunately always be required.
This is the solution I used in my application. It lets me keep all my window styles together, and requires just a couple lines after the <Window.Resources> section.
Do your Style like so:
<Style x:Key="MyWindowStyle">
<Setter Property="Window.Background" Value="AliceBlue"/>
</Style>
Then, in your Window, after </Window.Resources> include the following:
<Window.Style>
<Style BasedOn="{StaticResource MyWindowStyle}"/>
</Window.Style>
Add a new brush in your resource dictionary
<SolidColorBrush x:Key="WindowBackground" Color="AliceBlue" />
and in your WPF window simply set the required resource to the window background property
<Window x:Class="GDD.Presentation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300"
Background="{StaticResource WindowBackground}">
Some information related to problem: http://www.11011.net/archives/000692.html
Specific situation is : there is some generic textblock (key equals to type) styles declared in app.xaml which is in third party app, they used by all contentpresenters in my views ignoring my own styles.
I've found few possible solutions:
Explicitly assign all elements a style with overriden template & add resource dictionary with my styles to contentpresenter resources.
Add datatemplate for string, but there is a problem with access text detection ( may be solved by placing contentpresenter with ref to my own resources, isn't good solution because we increasing visual tree just to fix this problem)
Probably any other solutions?
P.S.: There is alot of views already exist, so first option is alot of work!
To reproduce create new wpf project and modify next files:
App.xaml add generic style:
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="20"/>
</Style>
</Application.Resources>
MainWindow.xaml content is:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<Button Content="Hello world">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Access_Text"/>
<MenuItem Header="NormalText"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
<TextBlock Text="WELCOME TO BLACK MESA"/>
</StackPanel>
Add Dictionary.xaml resource dictionary and add next style inside:
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="8"/>
</Style>
Not sure where you might do it as I don't understand the structure of your App, but if you want to remove the style from the Application Resources you could do it programatically like this
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ResourceDictionary dic = App.Current.Resources;
dic[typeof (TextBlock)] = null;
}
}
Using the XAML you provided in a test WPF project this leaves me with the default font size of 12 rather than 20.
I would like to make a library of some of my commonly used WPF controls, and one of these controls is a CustomWindow which inherits from the Window class. How can I get my CustomWindow to use a default appearance that is defined in the library with it?
I can replace
<Window x:Class="..." />
with
<MyControls:CustomWindow x:Class="..." />
and it works for the window behavior, but not the appearnce.
EDIT
Here is a simplified version of what I have so far:
Custom Window Control. Located in the Control library.
public class CustomChromeWindow: Window
{
static CustomChromeWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomChromeWindow),
new FrameworkPropertyMetadata(typeof(CustomChromeWindow)));
}
}
Window Style. Located in a Generic.xaml, a ResourceDictionary in the Themes folder of the control library
<Style TargetType="local:CustomChromeWindow">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="Background" Value="Red" />
</Style>
Test Window. Startup window of a separate project that references the control library
<local:CustomChromeWindow
x:Class="MyControlsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyControls;assembly=MyControls"
Title="MainWindow" Height="350" Width="525"
>
<Grid>
<TextBlock Text="This is a Test" />
</Grid>
</local:CustomChromeWindow>
What I end up getting is a window with the regular WindowStyle and a black background.
Use this xaml:
<Window x:Class="MyNamespace.CustomWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MyControls="MyNamespace">
<Window.Style>
<Style TargetType="MyControls:CustomWindow">
...
</Style>
</Window.Style>
<ContentPresenter />
</Window>
You may want to design a new theme for the window. If so place following theme in (your library)\Themes\Generic.xaml resource file:
<Style TargetType="{x:Type MyControls:CustomWindow}">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="True" />
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyControls:CustomWindow}">
<Border>
...
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Try adding this to the AssemblyInfo.cs file in your class library
[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)
)]
How about this (put this in the default Cctor) :
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWindow)
, new FrameworkPropertyMetadata(typeof(CustomWindow)));`