我在我的 UWP 应用程序中使用自定义 themedictionary。我在运行时更改了 ThemeResource 的值。此更改仅反射(reflect)在主视图中,而不会反射(reflect)在其他 View 中。即使我在更改资源值后创建新 View ,新 View 也仅使用资源的初始值。我做错了什么吗?
这就是我更改资源值的方式。
(Application.Current.Resources["BackgroundBrush"] as SolidColorBrush).Color = Windows.UI.Colors.Black;
我的辅助 View 的 XAML:
<Grid Background="{ThemeResource BackgroundBrush}"/>
甚至我的主视图也有相同的 XAML。
这是完整的项目。 Download Repo as zip
最佳答案
我认为这是设计使然。当我们为一个应用程序创建多个窗口时,每个窗口的行为都是独立的。每个窗口都在自己的线程中运行。每个窗口中使用的Application.Resources
也是独立的。
Application.Resources是 ResourceDictionary对象和 ResourceDictionary类继承自 DependencyObject , 所以 Application.Resources
也是一个 DependencyObject
实例。
All DependencyObject instances must be created on the UI thread that is associated with the current Window for an app. This is enforced by the system, and there are two important implications of this for your code:
- Code that uses API from two DependencyObject instances will always be run on the same thread, which is always the UI thread. You don't typically run into threading issues in this scenario.
- Code that is not running on the main UI thread cannot access a DependencyObject directly because a DependencyObject has thread affinity to the UI thread only. Only code that runs on the UI thread can change or even read the value of a dependency property. For example a worker thread that you've initiated with a .NET Task or an explicit ThreadPool thread won't be able to read dependency properties or call other APIs.
有关详细信息,请参阅 Remarks 下的 DependencyObject 和线程的 DependencyObject .
所以每个Window有自己的 Application.Resources
。在辅助 View 中,Application.Resources
从您的 ResourceDictionary
重新计算。 BackgroundBrush
不会像下面的代码那样受到主视图中设置的影响
(Application.Current.Resources["BackgroundBrush"] as SolidColorBrush).Color = Windows.UI.Colors.Black;
您只是在更改与主视图的 Window 关联的 Application.Current.Resources
实例.
如果你想让次 View 使用与主视图相同的画笔,我觉得你可以将这个颜色存储在主视图中,然后在创建次 View 时应用它。例如,我在 App
类中添加了一个名为 BackgroundBrushColor
的静态字段,然后像下面这样使用它:
private void ThemeChanger_Click(object sender, RoutedEventArgs e)
{
App.BackgroundBrushColor = Windows.UI.Color.FromArgb(Convert.ToByte(random.Next(255)), Convert.ToByte(random.Next(255)), Convert.ToByte(random.Next(255)), Convert.ToByte(random.Next(255)));
(Application.Current.Resources["BackgroundBrush"] as SolidColorBrush).Color = App.BackgroundBrushColor;
}
private async Task CreateNewViewAsync()
{
CoreApplicationView newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
(Application.Current.Resources["BackgroundBrush"] as SolidColorBrush).Color = App.BackgroundBrushColor;
Frame frame = new Frame();
frame.Navigate(typeof(SecondaryPage), null);
Window.Current.Content = frame;
// You have to activate the window in order to show it later.
Window.Current.Activate();
newViewId = ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
}
I have changed the code to modify the resource in each view. if you set a breakpoint in ThemeChanged method in SecondaryPage.xaml.cs before changing the value you can see that the resource's value has already changed to the updated one. But its not reflecting in the view.
这里的问题是因为你的 ThemeChanged
事件是在主视图中触发的,所以它在主视图的线程中运行,因此你在 ThemeManager_ThemeChanged 方法>SecondaryPage.xaml.cs 也将在主视图的线程中运行。这导致 ThemeManager_ThemeChanged
方法中的 Application.Current.Resources
仍然获取与主视图关联的 ResourceDictionary
实例。这就是为什么资源的值已经更改为更新后的值并且不会反射(reflect)在 View 中的原因。要清楚地看到这一点,您可以利用 Threads Window在调试时。
要解决此问题,您可以使用 this.Dispatcher.RunAsync
方法在正确的线程中运行您的代码,如下所示:
private async void ThemeManager_ThemeChanged(Utils.ThemeManager theme)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
((SolidColorBrush)Application.Current.Resources["BackgroundBrush"]).Color = theme.HighAccentColorBrush.Color;
});
}
但是,这样您仍然会收到类似这样的错误:“应用程序调用了一个为不同线程编码的接口(interface)。”这是因为 SolidColorBrush类也继承自 DependencyObject .要解决此问题,我建议将 HighAccentColorBrush
类型从 SolidColorBrush 更改为至 Color然后像这样使用它:
private async void ThemeManager_ThemeChanged(Utils.ThemeManager theme)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
((SolidColorBrush)Application.Current.Resources["BackgroundBrush"]).Color = theme.HighAccentColorBrush;
});
}
关于c# - 在运行时更改 ThemeResource 的值不会反射(reflect)在其他 View 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43437703/