更新 Universal Windows App XAML 中的资源
Update resource in Universal Windows App XAML
我需要在运行时更改通用 Windows 应用程序中应用程序文本块的颜色。
通用 Windows 应用程序不支持动态资源,我一直在探索几种不同的方法来更改 TextBlock 的颜色,但没有成功
<TextBlock Text="Test" Style="{StaticResource MyText}"/>
使用样式
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
</Style>
我的问题是:如何在运行时更改 TextBlock 的颜色?
以下均为改色尝试:
最初,我关注了这篇文章+视频Dynamically Skinning Your Windows 8 App,并将TextColor
存储在一个单独的字典文件中,我可以将其换入和换出MergedDictionaries
Day.xaml
包含 <SolidColorBrush x:Key="TextColor" Color="#FFDDEEFF" />
Night.xaml
包含 <SolidColorBrush x:Key="TextColor" Color="#FFFFDD99" />
在代码中:
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);
}
当这不起作用时,我想我需要清除字典:
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);
}
我也试过用改字典的方法刷新frame,没用
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.Content.GetType());
在另一次尝试中,我尝试在运行时创建字典并更新它
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));
最后,我意识到也许 StaticResource
会使颜色不可变,所以我决定尝试 ThemeResource
。我修改了我的主题:
<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>
我像之前的尝试一样交换了 Application.Current.Resources.MergedDictionaries
中的方法。
同样,颜色不会改变,即使我 fake-refresh Frame
几个月前我遇到了同样的问题,直到遇到以下 blog post 提出了一个很好的通用解决方案,我才解决了这个问题。
基本上你需要做的是:
第一
添加以下助手 Frame
class,它将替换您的默认 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));
}
}
}
ThemeAwareFrame
class 博客 post 作者解释的想法是:
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.
第二
在 App.xaml 中创建 Light 和 Dark 主题资源:
<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>
第三
在 App.Xaml.cs 中将 rootFrame 更改为 ThemeAwareFrame 而不是简单的 Frame:
rootFrame = new ThemeAwareFrame(ElementTheme.Dark);
在OnLaunched
方法中:
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
}
//..
四
在使用主题相关资源时使用 ThemeResource
而不是 staticResource
:
<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>
终于
要更改您的应用主题,只需像这样更改 rootFrame 的 AppTheme
属性:
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;
}
我需要在运行时更改通用 Windows 应用程序中应用程序文本块的颜色。
通用 Windows 应用程序不支持动态资源,我一直在探索几种不同的方法来更改 TextBlock 的颜色,但没有成功
<TextBlock Text="Test" Style="{StaticResource MyText}"/>
使用样式
<Style x:Key="MyText" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
</Style>
我的问题是:如何在运行时更改 TextBlock 的颜色?
以下均为改色尝试:
最初,我关注了这篇文章+视频Dynamically Skinning Your Windows 8 App,并将TextColor
存储在一个单独的字典文件中,我可以将其换入和换出MergedDictionaries
Day.xaml
包含<SolidColorBrush x:Key="TextColor" Color="#FFDDEEFF" />
Night.xaml
包含<SolidColorBrush x:Key="TextColor" Color="#FFFFDD99" />
在代码中:
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);
}
当这不起作用时,我想我需要清除字典:
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);
}
我也试过用改字典的方法刷新frame,没用
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.Content.GetType());
在另一次尝试中,我尝试在运行时创建字典并更新它
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));
最后,我意识到也许 StaticResource
会使颜色不可变,所以我决定尝试 ThemeResource
。我修改了我的主题:
<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>
我像之前的尝试一样交换了 Application.Current.Resources.MergedDictionaries
中的方法。
同样,颜色不会改变,即使我 fake-refresh Frame
几个月前我遇到了同样的问题,直到遇到以下 blog post 提出了一个很好的通用解决方案,我才解决了这个问题。
基本上你需要做的是:
第一
添加以下助手 Frame
class,它将替换您的默认 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));
}
}
}
ThemeAwareFrame
class 博客 post 作者解释的想法是:
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.
第二
在 App.xaml 中创建 Light 和 Dark 主题资源:
<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>
第三
在 App.Xaml.cs 中将 rootFrame 更改为 ThemeAwareFrame 而不是简单的 Frame:
rootFrame = new ThemeAwareFrame(ElementTheme.Dark);
在OnLaunched
方法中:
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
}
//..
四
在使用主题相关资源时使用 ThemeResource
而不是 staticResource
:
<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>
终于
要更改您的应用主题,只需像这样更改 rootFrame 的 AppTheme
属性:
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;
}