How can I change the default style of WPF controls for a single assembly? - c#

I've written a WPF plugin for some off-the-shelf product, and in this plugin I've used a theme/style to change the minimal width of all buttons like so:
<Style TargetType="Button">
<Setter Property="MinWidth" Value="80" />
</Style>
In the newest version of said off-the-shelf product they migrated from winforms to WPF themselves. Now when my plugin is loaded the style that previously just affected my plugged-in forms now affects all buttons in the application. This renders most UI's unusable.
I know I can use dictionary key based resources to make this style specific to my buttons, but this means I have to change each and every button in my plugin by hand, as well as not forget to set the style of each button in the future (and other elements this problem applies to). There are other options to make the style specific to a set of buttons, as seen in Is it possible to set a style in XAML that selectively affects controls? But I'm looking for a way to let my style affect only those of my plugin (so a bit more coarse than talked about in the referenced question). This plugin consists of multiple windows/views (tied together with Caliburn.Micro).
Is there a way to easily scope a style to for instance an assembly or namespace? I'd really like to define my resources just once. Currently it's defined at the Application.Resources level, if there's one more appropriate I'd like to hear that too.

With a ResourceDictionary, we can set default style wich will be applied without define style in Xaml.
DictionayNew.xaml :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<!-- default button -->
<Style TargetType="{x:Type Button}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="MinWidth" Value="80" />
</Style>
<!-- new button style -->
<Style x:Key="ActionButton" TargetType="{x:Type Button}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="Height" Value="23" />
</Style>
<!-- new button style based on previous style -->
<Style x:Key="BigActionButton"
BasedOn="{StaticResource ActionButton}"
TargetType="{x:Type Button}">
<Setter Property="MinWidth" Value="150" />
<Setter Property="Height" Value="30" />
</Style>
</ResourceDictionary>
In your Xaml, use the dictionary :
<Window x:Class="CheckDoublonImageBing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryNew.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
</Grid>
</Window>
Then, use Button as usual or with new style
<Button Content="Refresh" />
<Button Content="Delete selected" Style="{DynamicResource ActionButton}" />
With no style defined, button will have default style defined in the dictionary.
EDIT :
You can set merged dictionary by code like this :
ResourceDictionary myResourceDictionary = new ResourceDictionary();
myResourceDictionary.Source = new Uri("DictionayNew.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(myResourceDictionary);

You need to specify a Key for your Style and apply the Style to all your Buttons.
<Style TargetType="Button" x:Key="MyButtonStyle">
<Setter Property="MinWidth" Value="80" />
</Style>
<Button Style="{StaticResource MyButtonStyle}"/>
Without a Key the Style is used for all Buttons in the application (as you have already noticed).

Related

how to declare resources in wpf using prism?

Earlier I was working with WPF whenever I've to declare resources, I declare in App.xaml and call them in the entire project where I need them but now I'm Using Prism Technology, and I don't know where to declare resources and how to call them.
You can declare your resources the same way prior to using PRISM. It can be declared in any xaml file or control e.g. App.xaml, MainWindow.xaml, UserControl.xaml, Grid, TextBox etc. The only difference it makes is on the scope of the resource:
For Global accessibility in all of the Windows and User Controls of the Project, you can have your resource in the App.xaml file
<Application.Resources>
<Style TargetType="{x:Type Control}" x:Key="LargeFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</Application.Resources>
For accessibility at a Window level, you can have your resource in your xaml file for Window
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="LargeFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</Window.Resources>
You could even have it at a Control level, for example
<DockPanel.Resources>
<Style TargetType="{x:Type Control}" x:Key="LargeFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</DockPanel.Resources>
You can call it by:
<Label Content="Name" Style="{StaticResource LargeFontSize}" />

How to easily allow users to update Styles used be elements in XAML (UWP)

This is for a Windows 10 UWP. I need to allow users to update values on Styles that are associated with elements used throughout the application (i.e allow users to change the font size of various textblocks, background color stackpanels etc.) .
I currently have all my Styles in a separately file.
My App.xaml is as below:
<Application
x:Class="MyTestApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
My Styles.xaml (partial) is as below:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:MyTestApp.Views"
xmlns:x1="using:System">
<Style x:Key="HeaderTextBlocks" TargetType="TextBlock" BasedOn="{StaticResource TitleTextBlockStyle}">
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Margin" Value="10,4,0,0"/>
</Style>
<Style x:Key="RegularTextBlocks" TargetType="TextBlock" BasedOn="{StaticResource TitleTextBlockStyle}">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Margin" Value="0,0,0,0"/>
</Style>
</ResourceDictionary>
I refer to these styles on controls throughout the application using like this:
<TextBlock Style="{StaticResource HeaderTextBlocks}" />
I have created a Settings page (settings.xaml) which has textboxes for the users to update various style settings.
But I am not sure how I could bind these to the settings on the various styles on the styles.xaml file so that the styles are updated and the controls referring to the styles are updated when the user changes the value.
<TextBox Header="Font Size of Header TextBlocks" Text="{x:Bind HeaderTextBlocks.FontSize ???, Mode=TwoWay}" />
<TextBox Header="Font Size of Regular TextBlocks" Text="{x:Bind RegularTextBlocks.FontSize???, Mode=TwoWay}" />
Could someone please point me in the right direction? I am trying to do this with minimal (or no code behind) as possible.
Unfortunately this kind of user-defined styling is not easily available in UWP. You can however implement a kind of styling solution using data binding.
First step is to create a class like CustomUISettings which implements INotifyPropertyChanged and has properties like HeaderFontSize, etc.
Now on app start create an instance of this class and add it as app resource:
Application.Current.Resources["CustomUISettings"] = new CustomUISettings();
Now you can bind to the properties in this class anywhere in your code:
<TextBox FontSize="{Binding HeaderFontSize, Source={StaticResource CustomUISettings}}" />
You must use the classic {Binding} markup extension, because {x:Bind} does not support Source setting.
To modify the UI settings you can just retrieve the instance anywhere and set the properties as you see fit:
var customUISettings = (CustomUISettings)Application.Current.Resources["CustomUISettings"];
customUISettings.HeaderFontSize = 50;
You must make sure that all properties in CustomUISettings class fire the PropertyChanged event. You can see how to implement INotifyPropertyChanged interface for example here.

Catel: Create new DataWindowButtonStyle based on another 3rd party style

REF: Catel docs
Using Visual Studio 2013, .NET 4.5.1, Catel 4.4.
I have a DataWindow with the default Close button. I would like to style that button using the MahApps.Metro Flat Button style. If I had direct access to the DataWindow Close button (I think it's created at runtime), I'd apply the Flat Button style in the XAML, but I don't.
In the Catel docs, the comment says we should specify our own DataWindowButtonStyle. Could I get an example of creating a new DataWindowButtonStyle where I can base it on the MahApps.Metro Flat Button style?
Please let me know if you have any questions or need more information.
Thanks!
Edit: Here's what I've tried:
<catel:DataWindow.Resources>
<!-- Compile Error: A 'DynamicResourceExtension' cannot be set on the BasedOn property of type 'Style'.
<ResourceDictionary>
<Style x:Key="DataWindowButtonStyle" BasedOn="{DynamicResource MetroFlatButton}" />
</ResourceDictionary>
-->
<!-- This compiles and runs, but doesn't work.
<ResourceDictionary>
<Style x:Key="DataWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow" />
</Style>
</ResourceDictionary>
-->
<!-- This compiles and runs, but doesn't work.
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Catel.MVVM;component/themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="DataWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow" />
</Style>
</ResourceDictionary>
-->
<!-- Compiles and runs, but doesn't work. -->
<Style x:Key="DataWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow" />
</Style>
</catel:DataWindow.Resources>
You can simply define a new DataWindowButtonStyle resource and it should be applied automatically.
The custom DataWindowButtonStyle must be defined in App.xaml application resources.
App.xaml:
<Application.Resources>
<Style x:Key="DataWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Foreground" Value="Red"/>
<Setter Property="MinHeight" Value="50"/>
</Style>
</Application.Resources>
I don't know why setting background property is not working. But other properties are working.

Trying to apply multiple styles to a DataGridRow

I have WPF application, i created DataGrid style in App.xaml to apply whole application.
App.xaml
<Style TargetType="DataGrid" x:Key="GridStyle1">
<Setter Property="HorizontalGridLinesBrush" Value="LightGray" />
<Setter Property="VerticalGridLinesBrush" Value="LightGray" />
<Setter Property="AlternatingRowBackground" Value="WhiteSmoke" />
<Setter Property="RowHeight" Value="30" />
<Setter Property="RowStyle">
<Setter.Value>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Cyan" />
</Trigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>
Then in window :
window1.xaml
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="FontStyle" Value="Italic" />
</Style>
The problem that window style does not apply ( font style not italic )
It looks like you want to apply a global style to every DataGridRow in every DataGrid in multiple windows, and you also want to apply additional styling to DataGridRow in one or more DataGrids in one particular window.
If you've learned CSS before, you may expect stylesheets to be cumulative: In CSS, if you apply tr.style1 globally and tr.style2 locally, you get both, with tr.style2 winning the toss in any cases where they set the same attribute.
That's not how styles work in XAML. In XAML, an element may inherit styling from its parent, but it can have at most one Style of its own. Additionally, as you've found, Style has a BasedOn property. You can base one style on another, and get the cumulative effects of both.
Lastly, there are several ways to apply a style. You've found that you can apply them to every element of a given type in a given scope.
Unfortunately, because everything depends on context, the way XAML styles are applied can be very confusing at first (and at second, and sometimes third). Particularly when you are using one style (GridStyle1) to apply another style. It's not always obvious what overrides what.
It's best to keep things as simple as possible. We'll get rid of that RowStyle setter, because we don't need it. We'll just create a global DataGridRow Style that applies by default to every DataGridRow everywhere, and then we'll override that specifically in window1.xaml.
App.xaml
<Application.Resources>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="Background" Value="Cyan" />
</Style>
</Application.Resources>
window1.xaml
<Window.Resources>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="FontStyle" Value="Italic" />
</Style>
</Window.Resources>
That will apply to every DataGridRow in that window. The BasedOn attribute there will refer to whatever style has already been defined for DataGridRow in any containing context -- commonly, that means App.xaml, and if we don't add anything else, that'll be the case here.
The difference between this and what you had is that you were applying the Cyan Background style in a different way: The DataGridRow style you applied in App.xaml was applied via the RowStyle setter on your DataGrid style. That style was BasedOn WPF's pre-existing default Style for DataGridRow, and then it was forcibly applied to every DataGridRow in every DataGrid that used the GridStyle1 style.
The DataGridRow style you defined in window1.xaml would have applied, if DataGrid.RowStyle hadn't already been set in GridStyle1.
But as we've seen, you don't need to use RowStyle to apply a style globally to every DataGridRow. You can do that with the default style for that type, as in my App.xaml fragment above. DataGrid.RowStyle is useful for individually overriding the global DataGridRow style on one particular DataGrid. But you don't want to do that globally! So your styles in App.xaml should look like this:
App.xaml
<Style TargetType="DataGrid" x:Key="GridStyle1">
<Setter Property="HorizontalGridLinesBrush" Value="LightGray" />
<Setter Property="VerticalGridLinesBrush" Value="LightGray" />
<Setter Property="AlternatingRowBackground" Value="WhiteSmoke" />
<Setter Property="RowHeight" Value="30" />
</Style>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="Background" Value="Cyan" />
</Style>
And again, here's the Style in window1.xaml
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="FontStyle" Value="Italic" />
</Style>
Extra Credit
The styles above should solve your problem.
But there are other ways to approach this stuff. Unless you're very comfortable with what we did above, what follows may just add confusion, so if you start reading this and you find that the more you read, the less you understand -- then stop reading! It can wait!
You could also make all text in a DataGrid be italic, but that changes the headers too so I don't think it's what you want:
<DataGrid
FontStyle="Italic"
/>
If you want to apply that Italic style on just one grid in window1.xaml, here's how to do that. If we add an x:Key attribute to a Style, it won't be applied to every DataGridRow in scope. Instead, it's just sitting there, waiting to be used by name as a StaticResource.
window1.xaml
<Window.Resources>
<Style x:Key="ItalicDataGridRowStyle"
TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="FontStyle" Value="Italic" />
</Style>
</Window.Resources>
<-- ... -->
<!-- One grid with italic rows -->
<DataGrid
x:Name="dataGrid1"
RowStyle="{StaticResource ItalicDataGridRowStyle}"
/>
<!-- And another grid with default rows -->
<DataGrid
x:Name="dataGrid2"
/>
And here's another way to apply styling to the rows in just one grid in window1.xaml:
<!-- Yet another grid -->
<DataGrid
x:Name="dataGrid3"
>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource ItalicDataGridRowStyle}">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="Wheat" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
Finally, you could have set RowStyle in GridStyle1, and then explicitly set RowStyle on specific grids in specific windows, as above. That would work. You could have also created a new DataGrid style in window1.xaml (based on GridStyle1) which set RowStyle to something else.

WPF Style at Windows or App level yields different results

I'm trying to learn Styling in WPF and encountered a funny thing:
There is a difference when I apply a style at application or (main) window level.
When I define the following resource in the App.xaml:
<Application.Resource>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontStyle" Value="Italic" />
</Style>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Application.Resource>
the GroupBox caption is bold and italic.
When I instead define the styling in the MainWindow.xaml:
<Window.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontStyle" Value="Italic" />
</Style>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Window.Resources>
The GroupBox caption box is only bold and not italic.
Can anybody explain this behavior?
In picking TextBlock you have unearthed something. TextBlock is not derived from Control, and thus behaves slightly differently.
See https://stackoverflow.com/a/27065140/4258144 :
there is a curious rule in WPF implicit styles are only inherited
across template boundaries by elements which inherit from the Control
class
I guess you can add to that, "unless it is globally specified in App.xaml".
UPDATE:
Following comments, here's a look at a GroupBox visual tree, taken from Snoop.

Categories