从 C# 更新 ThemeResources?

Update ThemeResources from c#?

我有一个分析图像的空白,提取两种主色,并替换 "SystemControlForegroundAccentBrush" 和 "SystemControlHighlightAccentBrush",我在 App.xaml 中覆盖了它们。它运行良好,只是需要一些时间来分析图像,所以一旦我页面中的所有控件都已加载,它就会更改颜色。 结果,它们保留了旧的强调色。我怎样才能让它们动态绑定到 ThemeRessource?

这是我的 app.xaml:

<Application.Resources>
    <ResourceDictionary x:Name="resdic">
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Dark">
                <SolidColorBrush x:Key="SystemControlForegroundAccentBrush"/>
                <SolidColorBrush x:Key="SystemControlHighlightAccentBrush"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Application.Resources>

这是 ColorExtractor.cs class 中改变颜色的(非常简化的)部分:

public async static void Analyse()
{
    Application.Current.Resources["SystemControlForegroundAccentBrush"] = new SolidColorBrush(color);
    Application.Current.Resources["SystemControlHighlightAccentBrush"] = new SolidColorBrush(color2);
}

我在 Page.xaml 中有一堆控件,它们的前景设置如下:

<TextBlock Foreground="{ThemeResource SystemControlForegroundAccentBrush]"/>

我在 Page.xaml.cs 中调用 ColorExtractor.Analyse()(在 OnNavigatedTo 事件中)。我总是可以创建一个在设置颜色后触发的事件,但我需要找到一种方法来更新我页面中所有控件的颜色。

您可以看看模板 10 是如何进行主题更改的,但在他们的例子中,他们提前在资源字典中定义了两个不同的主题。您可以在 repo on Github 中找到他们的代码,但这里有一些使用的代码:

(Window.Current.Content as FrameworkElement).RequestedTheme = value.ToElementTheme();
Views.Shell.SetRequestedTheme(value, UseBackgroundChecked);

来自here

public static void SetRequestedTheme(ApplicationTheme theme, bool UseBackgroundChecked)
    {

        WindowWrapper.Current().Dispatcher.Dispatch(() =>
        {
            (Window.Current.Content as FrameworkElement).RequestedTheme = theme.ToElementTheme();

            ParseStyleforThemes(theme);

            HamburgerMenu.NavButtonCheckedForeground = NavButtonCheckedForegroundBrush;

            HamburgerMenu.NavButtonCheckedBackground = (UseBackgroundChecked) ?
                NavButtonCheckedBackgroundBrush : NavButtonBackgroundBrush;

            HamburgerMenu.NavButtonCheckedIndicatorBrush = (UseBackgroundChecked) ?
                Colors.Transparent.ToSolidColorBrush() : NavButtonCheckedIndicatorBrush;

            HamburgerMenu.SecondarySeparator = SecondarySeparatorBrush;

            List<HamburgerButtonInfo> NavButtons = HamburgerMenu.PrimaryButtons.ToList();
            NavButtons.InsertRange(NavButtons.Count, HamburgerMenu.SecondaryButtons.ToList());

            List<HamburgerMenu.InfoElement> LoadedNavButtons = new List<HamburgerMenu.InfoElement>();

            foreach (var hbi in NavButtons)
            {
                StackPanel sp = hbi.Content as StackPanel;
                if (hbi.ButtonType == HamburgerButtonInfo.ButtonTypes.Literal) continue;
                ToggleButton tBtn = sp.Parent as ToggleButton;
                Button btn = sp.Parent as Button;

                if (tBtn != null)
                {
                    var button = new HamburgerMenu.InfoElement(tBtn);
                    LoadedNavButtons.Add(button);
                }
                else if (btn != null)
                {
                    var button = new HamburgerMenu.InfoElement(btn);
                    LoadedNavButtons.Add(button);
                    continue;
                }
                else
                {
                    continue;
                }

                Rectangle indicator = tBtn.FirstChild<Rectangle>();
                indicator.Visibility = ((!hbi.IsChecked ?? false)) ? Visibility.Collapsed : Visibility.Visible;

                if (!hbi.IsChecked ?? false) continue;

                ContentPresenter cp = tBtn.FirstAncestor<ContentPresenter>();
                cp.Background = NavButtonCheckedBackgroundBrush;
                cp.Foreground = NavButtonCheckedForegroundBrush;
            }

            LoadedNavButtons.ForEach(x => x.RefreshVisualState());

        });
    }

来自here

I've got a void that analyses an image, extracts two dominant colors, and replaces "SystemControlForegroundAccentBrush" and "SystemControlHighlightAccentBrush", that I'm overriding in App.xaml.

首先,我认为您在 app.xaml 中的代码无法覆盖 ThemeResource,并且您在页面中使用了此 Brush,如下所示:

<TextBlock Foreground="{ThemeResource SystemControlForegroundAccentBrush}" Text="Hello World!" FontSize="50" />

如果您在 SystemControlForegroundAccentBrush 上按 "F12",您实际上可以在 "generic.xaml" 文件中找到该资源。

现在假设你的 ColorExtractor.cs class 工作正常并且 ColorExtractor.Analyse() 可以覆盖这两个画笔的颜色,并且页面中有许多控件使用这两个资源,刷新此处页面可以解决您的问题。

但是我觉得这个操作还是不要放在OnNavagateTo事件或者Page.Loaded事件里面比较好,UWP里面没有刷新的方法,我们使用再次导航到这个页面刷新,所以如果将此操作放在 OnNavagateTo 事件或 Page.Loaded 事件中,每次导航到此页面时,资源将被覆盖并再次导航。所以我把这个操作放在一个 Button click 事件中,像这样:

public bool Reload()
{
    return Reload(null);
}

private bool Reload(object param)
{
    Type type = this.Frame.CurrentSourcePageType;
    if (this.Frame.BackStack.Any())
    {
        type = this.Frame.BackStack.Last().SourcePageType;
        param = this.Frame.BackStack.Last().Parameter;
    }
    try { return this.Frame.Navigate(type, param); }
    finally
    {
        this.Frame.BackStack.Remove(this.Frame.BackStack.Last());
    }
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    ColorExtractor.Analyse();
    Reload();
}

最终,我决定在ColorExtractor.cs中创建一个事件:

public static event EventHandler Analysed;
public async static void  Analyse(BitmapImage poster)
{
    //Analyse colors
    Analysed(null, null);
}

然后,在我的 MainPage.xaml.cs 上:

ColorExtractor.Analyse(bmp);
ColorExtractor.Analysed += (sender, EventArgs) =>
{
    //Set Page foreground color, as a lot of controls are dynamically binded to their parent's foreground brush.
    //If a control isn't automatically binded, all I have to do is say: Foreground="{Binding Foreground, ElementName=page}"
    page.Foreground = Application.Current.Resources["SystemControlForegroundAccentBrush"] as SolidColorBrush;
    page.BorderBrush = Application.Current.Resources["SystemControlHighlightAccentBrush"] as SolidColorBrush;
    //Reload any custom user control that sets it's children's color when it's loaded.
    backdrop.UserControl_Loaded(null, null);
};

所以我实际上并没有直接将我的控件绑定到 ForegroundAccentBrush,但这不需要重新导航到页面就可以工作。