Ok I would think this would be fairly simple, but it seems almost impossible to do. I have a resource dictionary in my app and I define a style like so:
<Style x:Key="appBackground" TargetType="Grid">
<Setter Property="Background" Value="#ebf2f3"/>
</Style>
My end goal would be to allow users to select a color and let them change the appearance of the app. I would like to still use my resourcedictionary to control the styles, so they can pick like background color, main text color, and accent color and remain consistent throughout the app. But I cannot find a way to edit the resourceDictionary. I have tried to use the System.Windows.Markup.XamlWriter.Save method which was talked about in this SO post (How to dynamically add key and value to the ResourceDictionary in wpf?) but when I do it adds:
<s:String x:Key="appBackground">Blue</s:String>
Any ideas or suggestions out there??
You can make value of your setter a binding to static resource, that you would be dynamically changing like in the answer you referenced.
<Color x:Key="myAppBackground">#ebf2f3</Color>
<Style x:Key="appBackground" TargetType="Grid">
<Setter Property="Background" Value="{StaticResource myAppBackground}"/>
</Style>
After this you would be just changing value of myAppBackground.
Another suggestion, though it might not solve your problem since it's a bit different. You still make values of setters a binding, but instead of dynamically changing the values you would have 2 ResouceDictionaries with same keys, but different values. You would merge the one you want with the rest in your application and apply that. I used this approach to load different color themes on application startup, but if you need to do this many times it might be slow and not efficient approach.
Related
I've done my best to ensure this isn't an exact duplicate of other questions and have tried quite a few possible solutions. Maybe I'm just not doing the right searches.
The problem
I have a resource dictionary with a bunch of default styles. For example, most control types have a default height of 26 to provide some consistency in my layout.
So for example, I have the following style for TextBlock
<Style TargetType="TextBlock">
<Setter Property="Height" Value="26"/>
</Style>
The problem I have is that the Telerik RadGridView uses TextBlock controls to display the column header text and these controls are adopting the default style and end up with a height of 26.
I would like to get the RadGridView, and all of it's child controls, to ignore the styles in my resource dictionary.
I tried this but it didn't work
I found quite a few suggestions to set the style to Null for controls that you want to ignore global styles. I tried the following but it didn't work as it doesn't seem to apply to the child controls inside of the RadGridView.
<telerik:RadGridView Style="{x:Null}">
...
</telerik:RadGridView>
This works but may not be the best solution
I found the following question which had a solution I was able modify and use
Setting style based on existence of an ancestor type
Using the answers in that question I created a converter to check if a control has an ancestor of a specific type. The code is pretty much the same as in the above question so to keep this question from getting too long I won't paste it here.
I modified my style to this
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource HasAncestorTypeConverter},
ConverterParameter={x:Type telerik:RadGridView}}"
Value="False">
<Setter Property="Height" Value="26"/>
</DataTrigger>
</Style.Triggers>
</Style>
Although this works, I was concerned about performance as my application grows and there's more and more controls. I have similar triggers for many control types that could be used in the RadGridView.
Every control is going to be recursively checking if it has a RadGridView as an ancestor. Most won't, so they need to search all the way up to their base container before knowing they don't have a RadGridView as an ancestor.
My question
So after that the lead up, my questions is whether there's a better way to do this? Is there something I can wrap the RadGridView with that will tell it, and it's child elements, to ignore the styles in my resource dictionary?
Yes there is better way to do this: define empty style either in Resources of RadGridView itself, or if you want to apply that to all RadGridViews - define it in resources of RadGridView style itself, like this:
<Style TargetType="telerik:RadGridView">
<Style.Resources>
<Style TargetType="TextBlock" />
</Style.Resources>
</Style>
That will prevent all children of RadGridView to inherit global TextBlock style (instead they will use "default" TextBlock style)
I'm styling my DataGrid using an style provided by Microsoft at this page. I'm not changing this style except a single line as following that enables alternative row back color.
<!--Style and template for the DataGrid.-->
<Style TargetType="{x:Type DataGrid}">
<Setter Property="AlternationCount" Value="2"/>
... and the rest of the style
I realized a very strange behavior of this style. When you scroll up/down the data grid for few times and then you see that alternative row back color is mixed! such as following image:
I expect a sequence of light-colored + dark-colored rows which is true before scrolling; but after scrolling everything is randomly mixed-up. Before applying this style I was explicitly setting alternative row color at DataGrid definition like following which I never experienced such behavior.
<DataGrid ItemsSource="{Binding Source={StaticResource itemsSource}}" AutoGenerateColumns="False" AlternatingRowBackground="#FF58C9FD" RowBackground="#00000000"/>
Does anyone have a suggestion where should I be looking for the problem ?
That's a known virtualization problem on WPF DataGrid.
Here is a workaround, but be aware that it can cause serious performance problems if your dealing with large amounts of data on that DataGrid.
There is a good explanation on the mechanics behind this behavior here.
Gabriel mentioned that this behavior is a known issue that raises when you try to create a brand-new style for data grid (or in general any item collection).
I tried creating an style based on default style as suggested by Gabriel like following:
<Style TargetType="{x:Type DataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="AlternationCount" Value="2"/>
<Setter Property="AlternatingRowBackground" Value="OrangeRed"/>
</Style>
I tried overriding all properties I need via this style, and it works fine. Maybe there is a trick/tweak missing in styles provided by MSDN (which is duplicated in most of styles found on net) that fixes this issue. We still appreciate if anyone updates us.
Meanwhile, I suggest to override default styles by your customization and avoid any brand-new style.
So, I'm making a GUI thingy with WPF in C#. It looks like this:
It's not done right now. Those 2 rows are my attempt at making a sort of data table, and they are hard-coded in the XAML.
Now, I am implementing the add new fruit button functionality in the C#.
I have the following style in the XAML that governs what the background images of the rows should look like:
<Style x:Key="stretchImage" TargetType="{x:Type Image}">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Stretch" Value="Fill"/>
</Style>
So, in the code, I create an image for each column, col0, col1, and col2, and if I use the following code,
col0.Style = (Style)Application.Current.Resources["stretchImage"];
col1.Style = (Style)Application.Current.Resources["stretchImage"];
col2.Style = (Style)Application.Current.Resources["stretchImage"];
it adds a new row that looks like this:
As you can see, it's not quite right...
And stretching the window exacerbates the problem:
It seems to not be respecting the style's "Stretch" property.
But then, if I instead change my style loading code to
col0.Style = (Style)FindResource("stretchImage");
col1.Style = (Style)FindResource("stretchImage");
col2.Style = (Style)FindResource("stretchImage");
It works beautifully:
(Again, the app isn't finished, so don't worry about that), but my main question is: What's the difference between Application.Current.Resources[] and FindResource()? Why does one seem to ignore some of the properties while the other doesn't? And how, if at all possible, might I get Application.Current.Resources[] to work properly?
Resources can be defined on almost any element in the visual tree. FrameworkElement.FindResource() will walk up the tree looking for the resource at each node, and eventually make it all the way to Application. Application.Current.Resources[] skips all of this and goes straight for the resources on the Application. You almost always want to use FindResource() so you can override styles at various points.
According to MSDN, the standard way of setting your app theme is to set RequestedTheme="Dark" or RequestedTheme="Light" on the toplevel Application instance.
This works great for simple apps but many times I find myself wanting to change the theme of an individual page or even an individual control (e.g. a light-themed text edit page vs a dark-themed image viewer in the same app).
XAML controls have 10s or 100s of visual states and color definitions and setting each one of them by hand is tedious and difficult to get 100% right!
Is there an easy way to set a dark or light theme on an individual control?
For a XAML windows store app, the default look-and-feel of controls is defined in Common/StandardStyles.xaml. If you've ever looked at that file, you'll notice a ton of references like {StaticResource ApplicationForegroundThemeBrush}. Looks promising...
Unfortunately, these "theme brushes" aren't defined anywhere in your app, and there's no easy way to set a light or dark override for individual controls. However, there is an answer.
Fortunately, there's an excellent blog post by Joe White on the default theme colors, which I've turned into a resource dictionary that you can find here. Dropbox only does xml previews so you'll have to rename the file.
Copying these resources to your app won't help by itself though. To make use of them, you need to surgically override the default control styles to use them!
One way to do this is to create a new resource dictionary, e.g. at Common/CustomStyles.xaml, and merge that into the app's resources like so:
<Application
x:Class="My.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--
Styles that define common aspects of the platform look and feel
Required by Visual Studio project and item templates
-->
<ResourceDictionary Source="Common/ThemeColors.xaml"/>
<ResourceDictionary Source="Common/StandardStyles.xaml"/>
<ResourceDictionary Source="Common/CustomStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Notice that our default theme is Light. If we'd like to make a Dark-themed TextBlock, let's copy the visual style from StandardStyles.xaml and add it to our CustomStyles.xaml and make a few changes.
<Style x:Key="BasicRichTextStyleDark" TargetType="RichTextBlock">
<Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrushDark}"/>
<Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
<Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
<Setter Property="TextTrimming" Value="WordEllipsis"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Typography.StylisticSet20" Value="True"/>
<Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
<Setter Property="Typography.CaseSensitiveForms" Value="True"/>
</Style>
Notice that we have appended Dark to the style name and the theme brush definitions! You can do this via find and replace "ThemeBrush}" => "ThemeBrushDark}" in your CustomStyles.xaml file.
Now you can create a dark-themed text block like so:
<TextBlock Style="{StaticResource BasicRichTextStyleDark}"/>
Of course, you can use this technique for any other type of control as well. A little tedious, but the results are correct and it's not nearly as bad as trying to define every color manually!
There's no magic here. We're just defining some colors and overriding a default style with one that we copy-pasted and modified to use our colors.
A solution that surprisingly does not seem to be mentioned is just to use RequestedTheme="Dark" or RequestedTheme="Light" on the individual control elements.
I do that for an app where I needed to set some appbarbuttons to the dark setting (which is white foreground) - because the Foreground property did not set both the circle and the glyph itself to white:
<AppBarButton Label="Reload all articles" RequestedTheme="Dark" >
This way, I just force the control to use the theme I decide.
In folder Common you have a StandardStyles.xaml file.
Here you can find all standard styles default for Metro Applications.
You need to uncomment styles what you want to use and add to control as StaticResource.
I've also faced the same problem while Designing my App "Contact Book", because i also wanted the change between Dark & Light themes. For your que : "How do I mix Light and Dark themes in a C#/XAML Windows Store (Metro UI) App?", I have a superb solution.
I have made two pages with same code & same content. I added both those pages one above another and then I've made a dynamic change between those two kind of themes Light & Dark. I gave an option to user whether they want Dark layout or Light layout & as per user's choice I've applied the theme. I succeeded in this case.
Second solution:
If you want dynamic look and feel for your application, then you have
to made your own static layout type and then you can bind your that
static resource to the page on which you want to apply that kind of
style.
pardon if you don't find your answer here, but i thought it might help you to dynamically change between "Dark" & "Light" themes setting in your win 8 metro app.
I hope that this question has not been asked elsewhere, but I'm having difficulty finding the right search terms to bring up any already-existing answers to this problem.
I'm implementing a program in C# WPF (MVVM) with many interview-style screens where the user inputs scientific data in an orderly manner. We would like to have the Textbox's and DataGrid's change Foreground and Background colors on an individual basis based on whether the data in that control has been inputted by the user, inputted as a default value by the program, or is a template value from another file the user imported. On top of this, we would like the UI to respond to validation checks from IDataErrorInfo implemented in the ViewModel.
Thus, the data displayed in a TextBox could be blue if it is a templated value, green if a program default, black if user inputed, and red if IDataErrorInfo says it is bad data.
My initial answer for implementing this was to create a custom class:
class AdornerString{
private string _myString;
private bool _isTemplate;
private bool _isDefault;
public string MyString{
get{
etc.
}
set{
etc.
}
}
// accessor properties and Constructors omitted for brevity
}
Then I have all my TextBox.Text properties in the View bound like so:
<TextBox Text="{Binding Path=someAdornerString.MyString,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
and apply a style with DataTriggers that responds to someAdornerString's properties to create the appropriate colors.
However, IDataErrorInfo on the ViewModel doesn't validate the TextBox anymore. Why is this so, and is there a better way to solve my problem? I can't validate inside the AdornerString class.
A possible work-around, though undesirable:
The only other solution I can think of is to have two properties in the ViewModel for each field entered by the user- one for the data itself and the other being the same custom class as above minus the string. However, this means I can't generalize the style used for the textboxes. Each TextBox would have to have a custom style something like this:
<TextBox.Style>
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource OtherStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=**instanceCustomClass**.IsDefault}"
Value="True">
<Setter Property="Foreground"
Value="Green" />
</DataTrigger>
<Trigger Property="Validation.HasError"
Value="true">
<Setter Property="Foreground"
Value="OrangeRed" />
</Trigger>
</Style.Triggers>
</Style>
since each UI field has a specific custom class associated with it.
I would seriously rather not implement this way, as I have possibly 100+ pages of input screens, with each screen having 1-12 TextBox's each, ignoring the DataGrids thrown in the mix (with parellel arrays of data and their associated custom classes).
Any ideas? I've read about custom validation, though I yet don't see how this might help in this case.
Ignore making use of the IDataErrInfo validation all together, as it seems that the you really want to make it 1 of 4 values...and 'bad' data just happens to be one of them.
You need to keep the items on an even playing field since you are treating them the same, just differentiating colors. Use a single property with an object wrapping the value and the state of the model within the ViewModel. Ignore IDataErroInfo and then use a converter to provide the coloring and then add a delegate to the AdornerString that will be set to the validation function written in the ViewModel for it.