在运行时更改ThemeResource的值不会反映在其他视图中

时间:2017-04-16 13:19:46

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

我在我的UWP应用中使用自定义themedictionary。我在运行时更改了ThemeResource的值。此更改仅反映在主视图中,而不反映在其他视图中。即使我在更改资源的值后创建新视图,新视图也只使用资源的初始值。有什么我做错了吗?

这就是我改变资源价值的方式。

(Application.Current.Resources["BackgroundBrush"] as SolidColorBrush).Color = Windows.UI.Colors.Black;

我的辅助视图的XAML:

<Grid Background="{ThemeResource BackgroundBrush}"/>

即使我的主视图也有相同的XAML。

这是完整的项目。 Download Repo as zip

2 个答案:

答案 0 :(得分:8)

我认为这是设计上的。当我们为应用程序创建多个窗口时,每个窗口都独立运行。每个窗口都在自己的线程中运行。每个窗口中使用的Application.Resources也是独立的。

Application.ResourcesResourceDictionary个对象,ResourceDictionary类继承自DependencyObject,因此Application.Resources也是DependencyObject个实例。

  

必须在与应用的当前DependencyObject相关联的UI线程上创建所有Window实例。这是由系统强制执行的,有两个这对您的代码具有重要意义:

     
      
  • 使用来自两个DependencyObject实例的API的代码将始终在同一个线程上运行,该线程始终是UI线程。在这种情况下,您通常不会遇到线程问题。
  •   
  • 未在主UI线程上运行的代码无法直接访问DependencyObject,因为DependencyObject仅与UI线程具有线程关联。只有在UI线程上运行的代码才能更改甚至读取依赖项属性的值。例如,您使用.NET 任务或显式ThreadPool线程启动的工作线程无法读取依赖项属性或调用其他API。< / LI>   

有关详细信息,请参阅RemarksDependencyObject下的 DependencyObject和线程

因此每个Window都有自己的Application.Resources。在辅助视图中,Application.Resources会从ResourceDictionary重新评估BackgroundBrush(Application.Current.Resources["BackgroundBrush"] as SolidColorBrush).Color = Windows.UI.Colors.Black; 不会受到主视图中设置的影响,如下面的代码

Application.Current.Resources

您只是更改与主视图Window相关联的BackgroundBrushColor实例。

如果您希望辅助视图使用与主视图相同的画笔,我认为您可以在主视图中存储此颜色,然后在创建辅助视图时应用它。例如,我在App类中添加了一个名为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); } 的静态字段,然后使用它,如下所示:

ThemeChanged
  

我已更改代码以修改每个视图中的资源。如果在更改值之前在SecondaryPage.xaml.cs中的ThemeChanged方法中设置断点,则可以看到资源的值已更改为更新的值。但它没有反映在视野中。

此处的问题是因为您在主视图中触发了ThemeManager_ThemeChanged事件,因此它在主视图的主题中运行,因此您使用了Application.Current.Resources方法在 SecondaryPage.xaml.cs 中使用的也将在主视图的线程中运行。这导致ThemeManager_ThemeChanged方法ResourceDictionary仍然获得与主视图关联的this.Dispatcher.RunAsync实例。这就是为什么资源的价值已经改变为更新的价值并且不会反映在视图中的原因。要清楚地看到这一点,您可以在调试时利用Threads Window

要解决此问题,您可以使用private async void ThemeManager_ThemeChanged(Utils.ThemeManager theme) { await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { ((SolidColorBrush)Application.Current.Resources["BackgroundBrush"]).Color = theme.HighAccentColorBrush.Color; }); } 方法在右侧线程中运行代码,如下所示:

HighAccentColorBrush

然而,通过这种方式,您仍会收到如下错误:&#34; 该应用程序称为为不同线程编组的接口。 &#34;这是因为SolidColorBrush类也继承自DependencyObject。要解决此问题,我建议将private async void ThemeManager_ThemeChanged(Utils.ThemeManager theme) { await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { ((SolidColorBrush)Application.Current.Resources["BackgroundBrush"]).Color = theme.HighAccentColorBrush; }); } 类型从SolidColorBrush更改为Color,然后将其用作:

<a href="geo:38.897096,-77.036545">test</a>

答案 1 :(得分:0)

以这种方式使用主题。

在按钮上的主题页面中,单击添加此逻辑

    void OnClicked(object sender, System.EventArgs e)
    {
        var btn = sender as Button;

        MessagingCenter.Send(this, "ThemeButtonClicked", btn.BackgroundColor);

        Application.Current.Properties["Theme"] = btn.BackgroundColor;
        Application.Current.SavePropertiesAsync();

        Navigation.PopAsync(true);

    }

在所有其他页面中订阅以上通知,如下所示。

 MessagingCenter.Subscribe<Theme, Color>(this, "ThemeButtonClicked", OnThemeChanged);

添加以下方法,以便在订阅者通知时执行。

  private void OnThemeChanged(Theme source, Color cl)
    {
        this.BackgroundColor = cl;
        layout.BackgroundColor = cl;

    }

这样堆栈上的所有打开实例也会更新主题。希望这能回答你的问题。