I would like to have the equivalent of the XAML, but in C# code:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="SystemControlHighlightBaseMediumLowBrush" Color="White" />
<SolidColorBrush x:Key="SystemControlHighlightBaseHighBrush" Color="White" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
Add the following to your Application OnLaunched method:
ResourceDictionary lightTheme = new ResourceDictionary();
lightTheme["SystemControlHighlightBaseMediumLowBrush"] = new SolidColorBrush(Windows.UI.Colors.White);
lightTheme["SystemControlHighlightBaseHighBrush"] = new SolidColorBrush(Windows.UI.Colors.White);
App.Current.Resources.ThemeDictionaries.Add("Light", lightTheme);
You can use
Brush SystemControlHighlightBaseHighBrush = new SolidColorBrush(Colors.White);
Brush SystemControlHighlightBaseHighBrush = new SolidColorBrush(Colors.White);
Then you could use it like this to set the colour of a button
myButton.Background = SystemControlHighlightBaseHighBrush;
If, as your comment suggests you want to change the colour on mouse hover you need to capture the mousehover event and then you could change the colour to one of your named styles. For example to change the button colour on mousehover you could do this
private void myButton_MouseHover(object sender, System.EventArgs e)
{
myButton.Background = SystemControlHighlightBaseHighBrush;
}
Related
I have a wpf app and I am messing with loading themes (light and dark), I made two simple resource dictionary files which are created in a shared assembly:
Dark Theme (same structure for the light theme, but with different color values):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="#FF1E1E1E" x:Key="Background"/>
<SolidColorBrush x:Key="TextColorBrush" Color="White"/>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColorBrush}"/>
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="{StaticResource Background}"/>
</Style>
</ResourceDictionary>
In my main application, App.xaml I am referencing my 2 theme dictionaries as such
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_DarkTheme.xaml" x:Name="DarkTheme"/>
<ResourceDictionary Source="pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_LightTheme.xaml" x:Name="LightTheme"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The way I am setting up the resources based on which theme I am choosing is done in the App.xaml.cs
public enum Skin { Light, Dark }
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public static Skin Skin { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ChangeSkin(Skin.Light);
}
public void ChangeSkin(Skin newSkin)
{
Skin = newSkin;
if (Skin == Skin.Dark)
ApplyResources(Resources.MergedDictionaries[0].Source.ToString());
else if (Skin == Skin.Light)
ApplyResources(Resources.MergedDictionaries[1].Source.ToString());
}
private void ApplyResources(string src)
{
var dict = new ResourceDictionary() { Source = new Uri(src, UriKind.RelativeOrAbsolute) };
foreach (var mergeDict in dict.MergedDictionaries)
{
Resources.MergedDictionaries.Add(mergeDict);
}
foreach (var key in dict.Keys)
{
Resources[key] = dict[key];
}
}
}
And finally, my main window. Since I want these particular styles to be global I am not using any keys to identify them.
<Window x:Class="Foo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Label Content="hello"/>
</Grid>
</Window>
But my main issue is that the Label control doesn't show up in my application. I can see my background change color appropriately but my label control is just gone! What am I doing wrong? Many thanks in advance!
Do not add all theme ResourceDictionaries from the start to Application.Resources.MergedDictionaries, i.e. start with empty Application.Resources:
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Then change the theme by replacing Application.Current.Resources.MergedDictionaries with the current theme:
private void ChangeSkin(Skin skin)
{
ResourceDictionary theme = null;
switch (skin)
{
case Skin.Light:
theme = new ResourceDictionary { Source = new Uri("pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_LightTheme.xaml") };
break;
case Skin.Dark:
theme = new ResourceDictionary { Source = new Uri("pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_DarkTheme.xaml") };
break;
}
if (theme != null)
{
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(theme);
}
}
When changing themes only means to replace Colors and Brushes, you may also move your Styles to Application.Resources and use DynamicResource in the Style Setters.
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextColorBrush}"/>
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="{DynamicResource Background}"/>
</Style>
</Application.Resources>
</Application>
Then your theme ResourceDictionaries would only contain Color and Brush resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="#FF1E1E1E" x:Key="Background"/>
<SolidColorBrush x:Key="TextColorBrush" Color="White"/>
</ResourceDictionary>
I need to change a color of application's TextBlocks at runtime in an Universal Windows App.
Universal Windows Apps don't support Dynamic Resources and I've been unsuccessfully exploring a few different ways to change color of TextBlock
<TextBlock Text="Test" Style="{StaticResource MyText}"/>
using the style
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
</Style>
My question is: How can I change the color of the TextBlock at runtime?
What follows are all attempts to change the color:
Initially, I followed this article+video Dynamically Skinning Your Windows 8 App and I stored TextColor in a separate dictionary file that I can swap in and out of MergedDictionaries
Day.xaml contains <SolidColorBrush x:Key="TextColor" Color="#FFDDEEFF" />
Night.xaml contains <SolidColorBrush x:Key="TextColor" Color="#FFFFDD99" />
In code:
ResourceDictionary _nightTheme = new ResourceDictionary() { Source = new Uri("ms-appx:///Themes/Night.xaml") };
ResourceDictionary _baseTheme = new ResourceDictionary() { Source = new Uri("ms-appx:///Themes/MyApp.xaml") };
// OnLaunched - I set a default theme to prevent exceptions
Application.Current.Resources.MergedDictionaries.Add(_dayTheme);
// Method that changes theme:
if (NightFall)
{
Application.Current.Resources.MergedDictionaries.Remove(_dayTheme);
Application.Current.Resources.MergedDictionaries.Add(_nightTheme);
}
else
{
Application.Current.Resources.MergedDictionaries.Remove(_nightTheme);
Application.Current.Resources.MergedDictionaries.Add(_dayTheme);
}
When this didn't work, I thought I need to clear the dictionaries:
ResourceDictionary _baseTheme = new ResourceDictionary() { Source = new Uri("ms-appx:///Themes/MyApp.xaml") };
// Method that changes theme:
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(_baseTheme);
if (NightFall)
{
Application.Current.Resources.MergedDictionaries.Add(_nightTheme);
}
else
{
Application.Current.Resources.MergedDictionaries.Add(_dayTheme);
}
I also tried to refresh the frame in the method that changes dictionaries, to no avail
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.Content.GetType());
In another attempt I tried to create a dictionary at runtime and update it
ResourceDictionary _dynamicTheme = new ResourceDictionary();
// OnLaunched
_dynamicTheme.Add("TextColor", new SolidColorBrush(Windows.UI.Colors.Chocolate));
Application.Current.Resources.MergedDictionaries.Add(_dynamicTheme);
// Method that changes theme
_dynamicTheme.Remove("TextColor");
_dynamicTheme.Add("TextColor", new SolidColorBrush(NightFall ? Windows.UI.Colors.Chocolate : Windows.UI.Colors.Cornsilk));
Finally, I realized that perhaps StaticResource makes the color immutable, so I decided to give ThemeResource a try. I've modified my themes:
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource MyTextColor}" />
</Style>
Day.xaml
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="MyTextColor" Color="#FFDDEEFF" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Night.xaml
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="MyTextColor" Color="#FFFFDD99" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
I swapped the methods in and out of the Application.Current.Resources.MergedDictionaries just like in previous attempts.
Again, the color doesn't change, even if I fake-refresh the Frame
I was facing the same issue several month ago, i couldn't fix the problem until i came across the following blog post which propose a pretty good generic solution.
Basically what you need to do is :
First
add the following helper Frame class, which will replace your default Frame
public class ThemeAwareFrame : Frame
{
private static readonly ThemeProxyClass _themeProxyClass = new ThemeProxyClass();
public static readonly DependencyProperty AppThemeProperty = DependencyProperty.Register(
"AppTheme", typeof(ElementTheme), typeof(ThemeAwareFrame), new PropertyMetadata(default(ElementTheme), (d, e) => _themeProxyClass.Theme = (ElementTheme)e.NewValue));
public ElementTheme AppTheme
{
get { return (ElementTheme)GetValue(AppThemeProperty); }
set { SetValue(AppThemeProperty, value); }
}
public ThemeAwareFrame(ElementTheme appTheme)
{
var themeBinding = new Binding { Source = _themeProxyClass, Path = new PropertyPath("Theme"), Mode = BindingMode.OneWay };
SetBinding(RequestedThemeProperty, themeBinding);
AppTheme = appTheme;
}
sealed class ThemeProxyClass : INotifyPropertyChanged
{
private ElementTheme _theme;
public ElementTheme Theme
{
get { return _theme; }
set
{
_theme = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The idea behind the ThemeAwareFrame class as explained in by the blog post writer is:
I create a proxy class that will just be used to store the current theme, and,
if the theme is changed, to propagate it. It is a static field, so is
shared with all ThemeAwareFrame.
I add an AppTheme dependency property. When it will be changed, it
will changed in the proxy class.
In the ThemeAwareFrame constructor, I bind the ThemeRequested property
to the proxy class Theme property.
Second
Create your Light and Dark theme resources in the App.xaml :
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="MyTextColor" Color="DarkGray" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="MyTextColor" Color="White" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Third
in the App.Xaml.cs change the rootFrame to a ThemeAwareFrame instead of a simple Frame:
rootFrame = new ThemeAwareFrame(ElementTheme.Dark);
in the OnLaunched method :
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new ThemeAwareFrame(ElementTheme.Dark);
// TODO: change this value to a cache size that is appropriate for your application
rootFrame.CacheSize = 1;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
// TODO: Load state from previously suspended application
}
//..
Forth
Use ThemeResource instead of staticResource when using a Theme related resource :
<Page.Resources>
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource MyTextColor}" />
</Style>
</Page.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Test" Style="{StaticResource MyText}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Button Content="Dark Theme" Click="ChangeThemeToDarkClick" Grid.Row="1"></Button>
<Button Content="Light Theme" Click="ChangeThemeToLightClick" Grid.Row="2"></Button>
</Grid>
Finally
To change your app theme simply change the AppTheme property of your rootFrame like this:
private void ChangeThemeToLightClick(object sender, RoutedEventArgs e)
{
(Window.Current.Content as ThemeAwareFrame).AppTheme = ElementTheme.Light;
}
private void ChangeThemeToDarkClick(object sender, RoutedEventArgs e)
{
(Window.Current.Content as ThemeAwareFrame).AppTheme = ElementTheme.Dark;
}
I have this style in a Resource-File:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colors.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBox" x:Key="StandardTextBox"/>
<Setter Property="Foreground" Value="{StaticResource Color1}"/>
</Style>
</ResourceDictionary>
(Colors.xaml contains my brushes)
My Code to use the style:
ResourceDictionary TetxboxStyles = new ResourceDictionary();
TetxboxStyles.Source = (new Uri("TextboxStyles.xaml", UriKind.RelativeOrAbsolute));
Resources.MergedDictionaries.Add(TetxboxStyles);
tb_input.Style = (Style)Find("StandardTextBox");
This works without a problem but it doesn't work when I dynamically add the Colors-Resource via code instead of in the TextboxStyles-File:
ResourceDictionary TetxboxStyles = new ResourceDictionary();
TetxboxStyles.Source = (new Uri("TextboxStyles.xaml", UriKind.RelativeOrAbsolute));
//Adding the Colors.xaml Resource
ResourceDictionary Colors = new ResourceDictionary();
brushes.Source = (new Uri("Colors.xaml", UriKind.RelativeOrAbsolute));
TetxboxStyles.MergedDictionaries.Add(Colors);
Resources.MergedDictionaries.Add(TetxboxStyles);
tb_input.Style = (Style)Find("StandardTextBox");
Output-Error:
System.Windows.Markup.XamlParseException
"{DependencyProperty.UnsetValue}"
I replaced StaticResource with DynamicResource and it works
I have this custom control with my own logic
public class BackButton : Button
{
public BackButton()
{
this.DefaultStyleKey = typeof(Button);
this.Click += (s, e) => {
Services.NavigationService.GoBack();
};
}
}
I want to apply to it the default style BackButtonStyle. I do not want to edit StandardStyles.xaml so it's in another ResourceDictionary
<Style TargetType="controls:BackButton" BasedOn="{StaticResource BackButtonStyle}"/>
Referenced in App.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Common/StandardStyles.xaml"/>
<ResourceDictionary Source="Common/FrameworkStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
It is building but this exception is thrown :
Message = "Cannot find a Resource with the Name/Key BackButtonStyle [Line: 15 Position: 44]"
What am i doing wrong ?
BackButtonStyle has target type as Button, so you can't use BackButtonStyle as base style for target type of controls:BackButton.
<Style x:Key="BackButtonStyle" TargetType="Button">
I am trying to add control to ContentPresenter on then run, but control I've added does not apply theme.
Theres is code with reference to theme in xaml file:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/PocGraphDataTemplates.xaml" />
</ResourceDictionary.MergedDictionaries>
Also I've tried to set style in code behind, does not work:
this.graphLayout.Content = analyzerViewModel.AnalyzedLayout = new PocGraphLayout()
{
LayoutAlgorithmType = "FR"
};
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri("Resources/PocGraphDataTemplates.xaml", UriKind.Relative);
analyzerViewModel.AnalyzedLayout.Style = new Style(typeof(PocGraphLayout));
analyzerViewModel.AnalyzedLayout.Style.Resources.MergedDictionaries.Add(rd);
When control was static everything worked fine:
<ViewModel:PocGraphLayout x:Name="graphLayout"
Graph="{Binding Path=Graph}"
LayoutAlgorithmType="{Binding Path=LayoutAlgorithmType}"
Sample:LayoutManager.ManagedLayout="True"
OverlapRemovalAlgorithmType="FSA"
HighlightAlgorithmType="Simple" />
Any ideas?
PS. I am newbie in wpf.
just see the style applied are using DynamicResource instead of StaticResource