I have created custom window classes for dialogs like this:
public class DialogBase : Window
{
// common stuff here
}
public class DialogOkCancel : DialogBase
{
static DialogOkCancel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DialogOkCancel),
new FrameworkPropertyMetadata(typeof(DialogOkCancel)));
}
// specific stuff here
}
The styles looks like this, and contains dialog specific things such as title bar, OK/Cancel buttons etc.
<Style x:Key="DialogBaseStyle" TargetType="{x:Type base:DialogBase}" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="ShowInTaskbar" Value="False"/>
<!-- more stuff here -->
</Style>
<Style TargetType="{x:Type base:DialogOkCancel}" BasedOn="{StaticResource DialogBaseStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type base:DialogOkCancel}">
<!-- more stuff here -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This works perfectly fine during runtime. Also, the dialogs displays fine in XAML Designer.
However, in XAML Designer, the DialogOkCancel style gets applied to other all other windows as well, such as the application main window (which simply derives from plain Window). The title bar, buttons and everything. I don't really get this, as the TargetType is specific. This does not occur at runtime.
What am I not seeing here ?
Correct way to apply a style both while in designer mode and during runtime is to have declared the style in App.xaml. Best practice would be to have a MergedDictionaries, containing a reference of your various style dictionaries, which will be in other files.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Converters -->
<ResourceDictionary Source="Resources/Converters.xaml"/>
<!-- Windows -->
<ResourceDictionary Source="Resources/WindowStyles.xaml"/>
// ... and all others //
WindowStyles.xaml can contain the exact styles you wrote above. They will be applied to all DialogOkCancel windows, both in designer and runtime.
Hope that helped.
Related
I have an app where I set a theme (ResourceDictionary) on the main Window. Then all children "inherits" styles, themes etc. via the DynamicResourceExtension. This works.
However, i have a custom Popup control to handle long press animation on buttons:
class LongPressAnimationControl : Popup
{
static LongPressAnimationControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LongPressAnimationControl),
new FrameworkPropertyMetadata(typeof(LongPressAnimationControl)));
}
/* Various DP's, events, properties, methods ETC. */
}
with a default style in Generic.xaml:
<Style TargetType="{x:Type controls:LongPressAnimationControl}">
<Setter Property="Delay" Value="00:00:00.500" />
<Setter Property="Duration" Value="00:00:01.000" />
<Setter Property="Image" Value="{StaticResource Image.LongPress}" />
<Setter Property="EasingFunction"><Setter.Value><CubicEase/></Setter.Value></Setter>
</Style>
This works. the properties change to the ones set in the style.
However, i would like the popup to use a style defined in the Theme.xaml so the properties etc. can be themed.
If i move the style to my Theme.xaml, then the style is not picked up (properties does not change).
I've also tried to add a key to the Style, and a resource reference in code, like so:
public LongPressAnimationControl()
{
SetResourceReference(StyleProperty, "LongPressAnimationControlStyle");
//....
}
Anyone with an idea of how to solve this?
You should include Theme.xaml in App.xaml resource, so WPF can find the resource.
I am in the process of creating a base window class for most of my windows to derive from. Obviously the best solution for this was a separate class, and a style that applies to it.
The issue is that the <Style ../> I have is not being applied when it is in App.Resources. That is, if it's defined in an external ResourceDictionary, and merged into App.xaml's resources, or a local dictionary and merged, or placed inline into App.Resources. The <Style ../> is, however, applied when it is placed into Themes/Generic.xaml.
The problem can be demonstrated without doing anything special at all in the base window, apart from overriding the DefaultStyleKeyProperty.
Below is ThemeWindow:
public class ThemeWindow : Window
{
static ThemeWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ThemeWindow), new FrameworkPropertyMetadata(typeof(ThemeWindow)));
}
}
Here is the very simple <Style ../> I am trying to apply (it makes the Window background red, nothing more):
<Style TargetType="{x:Type testing:ThemeWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type testing:ThemeWindow}">
<Grid>
<Grid.Background>
<SolidColorBrush Color="Red"/>
</Grid.Background>
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The MainWindow that uses ThemeWindow, is simply the following XAML:
<testing:ThemeWindow x:Class="Testing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testing="clr-namespace:Testing"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="125,83,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</testing:ThemeWindow>
Now, as stated, if you place that Style in its own ResourceDictionary, and include it like this:
<App.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/ThemeWindow.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</App.Resources>
.. it does not work. If you inline the style straight into App.Resources, it does not work.
The only situation I can find it working is to call the ResourceDictionary xaml Generic.xaml, and place it into the Themes/ directory of the application.
I am wondering exactly why this is happening.
My only theory is that when WPF sees a control type, it will head over to Themes, and scan all ResourceDictionarys for the type, then fall back to Generic.xaml and load it. This doesn't explain why it would not load if the <Style /> is available in a merged ResourceDictionary though. Note that it does work if the MergedDictionary is placed into Generic.xaml, for obvious reasons.
I'm perfectly fine with having to merge the ResourceDictionary into Generic.xaml if that's what I have to do. I just want to get down at the technical details as to why it needs to be like this.
Screenshots of this not working / working:
I have a simple workaround that would allow you to set your Style in you app.xaml.
Define your style in app.xaml like this :
<Style x:Key="{x:Type testing:ThemeWindow}" TargetType="{x:Type testing:ThemeWindow}">
And change your ThemWindow to this :
public class ThemeWindow : Window
{
static ThemeWindow()
{
StyleProperty.OverrideMetadata(typeof(ThemeWindow), new FrameworkPropertyMetadata(GetDefautlStyle()));
}
private static Style GetDefautlStyle()
{
if (defaultStyle == null)
{
defaultStyle = Application.Current.FindResource(typeof(ThemeWindow)) as Style;
}
return defaultStyle;
}
private static Style defaultStyle = null;
}
It does not really solve the question, but that would allow you to achieve what you need !
EDIT : Looking at DefaultStyleKey reference, it's clearly stated that it's used for theme style lookup. That explains why it won't find it in app.xaml or any other dictionary. It will only search in Theme dictionaries. So you either have to define your style in a Theme Dictionary, or to use the Style property directly as in the above example.
I come around the following solution already discussed in stackoverflow. it would required to add in the load component when load of the application.
Refer Solution
Is there anyway to turn off a style programatically?
As an example, I have a style that is linked to all textboxes
<Style TargetType="{x:Type TextBox}">
I would like to add some code to actually stop the style elements being used, so basically reverting back to the default control style.
I need a way to make a switch in my styles, so I can switch between Windows default style and my custom style through C# code.
Is there anyway to do this?
Thanks
Working Solution
Switching between themes in WPF
For setting the style to default,
In XAMl use,
<TextBox Style="{x:Null}" />
In C# use,
myTextBox.Style = null;
If style needs to be set as null for multiple resources, see CodeNaked's response.
I feel, all the additional info should be in your question and not in the comments. Anyways, In code Behind I think this is what you are trying to achieve:
Style myStyle = (Style)Application.Current.Resources["myStyleName"];
public void SetDefaultStyle()
{
if(Application.Current.Resources.Contains(typeof(TextBox)))
Application.Current.Resources.Remove(typeof(TextBox));
Application.Current.Resources.Add(typeof(TextBox),
new Style() { TargetType = typeof(TextBox) });
}
public void SetCustomStyle()
{
if (Application.Current.Resources.Contains(typeof(TextBox)))
Application.Current.Resources.Remove(typeof(TextBox));
Application.Current.Resources.Add(typeof(TextBox),
myStyle);
}
You could inject a blank Style that would take precedence over your other Style. Like so:
<Window>
<Window.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<Grid.Resources>
<Style TargetType="TextBox" />
</Grid.Resources>
</Grid>
</Window>
In the example above, only the Grid's implicit Style would be applied to TextBoxes in the Grid. You could even add this to the Grid programmatically, something like:
this.grid.Resources.Add(typeof(TextBox), new Style() { TargetType = typeof(TextBox) });
I know the answer has been accepted, but i want to add my solution which works awesome in the following scenario:
One main application using mahapps.metro
additional project imported from the main application with no reference to mahapps.metro, it is imported as a plugin (loading compiled .dll on the fly)
using the < ToolBar> re-styles everything to null therefore the mahapps.metro styles are not being applied to items inside the toolbar.
usercontrol is used to provide custom controls to the main application.
in the user control root set the resources:
<UserControl.Resources>
<Style x:Key="ButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" />
<Style x:Key="ComboBoxStyle" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}" />
</UserControl.Resources>
then the toolbar code can be the following
<ToolBar>
Block Template:
<ComboBox Style="{StaticResource ComboBoxStyle}"/>
<Button Content="Generate!" Style="{StaticResource ButtonStyle}"/>
</ToolBar>
this successfully applies the main application style to the controls inside the < ToolBar>
In Xaml, you can override this by setting a style explicitly. In code-behind, you can also set the style explicitly.
<TextBox Style="{StaticResource SomeOtherStyle}"/>
myTextBox.Style = Application.Resources["SomeOtherStyle"];
<Style x:Key="abc" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<button x:name="btn">my button!!</button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<Window ... Style="{StaticResource styleMainWindow}">
How can i use the button btn?
come up to your expectations,
MessageBox.Show(this.btn1.name);
was occured an error at compile time. and also btn1 didn't show up in intelisense.
Try the FindName method on the ControlTemplate class.
Assuming this is your Control's context:
var button = (Button)this.Template.FindName("btn", this);
I guess mjk6026, you have misunderstood WPF templates from actual member elements of a window.
It is true that when we name a UI element (x:Name) that is not part of any template, we can access that element by name in the code behind.
For templates you would have to use Template type's FindName() method.
So assuming that `this' means the window to which you have applied your style, the way you can access the button is (Button)this.Template.FindName("btn", this)
Let me know if this answers your question...
I want my WPF application to be skinnable, by applying a certain XAML template, and the changes to be application wide, even for dynamic controls or controls that aren't even in the visual/logical tree.
What can I use to accomplish this type of functionality? Are there any good resources or tutorials that show how this specific task can be done?
The basic approach to take is using resources all through your application and dynamically replacing the resources at runtime.
See http://www.nablasoft.com/alkampfer/index.php/2008/05/22/simple-skinnable-and-theme-management-in-wpf-user-interface/ for the basic approach
The replacing of resource will work but I found "structural skinning" to be more powerfull! Read more about it on CodeProject...
http://www.codeproject.com/KB/WPF/podder1.aspx
I have found the way to apply generic templates to all controls without using template keys. The solution is to use the type of the control as the Style key.
Example:
<Application.Resources>
<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
<Setter Property="Button.Background" Value="CornflowerBlue"/>
<Setter Property="Button.Template">
<Setter.Value>
<ControlTemplate x:Name="MyTemplate">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
here the Style key is x:Key="{x:Type Button}", so the style will be applied to all controls of type button without the control declaring the Style property to be a static or dynamic resource.