c# - 在运行时更改 ThemeResource 的值不会反射(reflect)在其他 View 中

标签 c# xaml uwp win-universal-app uwp-xaml

我在我的 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.ResourcesResourceDictionary对象和 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/

相关文章:

c# - DbComparisonExpression 需要具有可比较类型的参数

C# UWP 隐藏软导航栏

Azure 通知中心 - Windows (WNS) 错误 - WNS 凭据无效

.net - DeviceInformation.Id 是否在 .NET Framework 中持久存在?

c# - VS2010 的测试列表编辑器在 VS2012 中的功能(更新)

c# - C++ inside of C# 通信性能

c# - WPF:绑定(bind)到 DataGrid 中行的 SelectedItem

c# - Windows 通用 (UWP) 地理定位 API 权限

c# - XAML View /控件的 GUI 测试框架

c# - GL 项目无法正常工作