动态解析 MergedDictionaries 中的静态资源

问题描述 投票:0回答:1

对所有代码感到抱歉,但我觉得我需要它来解释问题。另外,我知道一些问题可以通过使用动态资源来解决,但我也需要它来处理静态资源,因为我们使用了大量

BasedOn

我有一个包含两个项目的解决方案,一个 WPF 应用程序和一个 WPF 类库。

WPF 类库有两个资源字典

SharedStyles_A.xaml
SharedStyles_B.xaml
,其中包含不同主题的样式。

SharedStyles_A

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="FontWeight"
                Value="Bold" />
        <Setter Property="FontSize"
                Value="30" />
    </Style>

    <Style TargetType="{x:Type Button}"
           x:Key="BigButton">
        <Setter Property="Content"
                Value="Big Button A" />
        <Setter Property="Foreground"
                Value="Red" />
        <Setter Property="Padding"
                Value="25" />
    </Style>
    
</ResourceDictionary>

SharedStyles_B

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="FontWeight"
                Value="Thin" />
        <Setter Property="FontSize"
                Value="30" />
    </Style>

    <Style TargetType="{x:Type Button}"
           x:Key="BigButton">
        <Setter Property="Content"
                Value="Big Button B" />
        <Setter Property="Foreground"
                Value="Green" />
        <Setter Property="Padding"
                Value="25" />
    </Style>
    
</ResourceDictionary>

我在应用程序项目中使用其中一种样式,如下所示:

App.xaml

<Application x:Class="WpfApp26.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/SharedStyles;Component/SharedStyles_A.xaml" />
                <ResourceDictionary Source="Resources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Resources.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="{x:Type Button}"
           BasedOn="{StaticResource BigButton}"
           x:Key="AppButton">
        <Setter Property="Content"
                Value="Big Button (AppButton)" />
    </Style>
</ResourceDictionary>

Window.xaml

<Window x:Class="WpfApp26.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow">
    
    <StackPanel HorizontalAlignment="Center"
                VerticalAlignment="Center">
        <TextBlock Text="Hello World!"
                   HorizontalAlignment="Center" />
        <Button Style="{StaticResource BigButton}" />
        <Button Style="{StaticResource AppButton}" />
    </StackPanel>
    
</Window>

一切都好,一切顺利。

但是,我现在想动态选择在启动时使用

SharedStyles_A
还是
SharedStyles_B

所以我把

App.xaml
改为

<Application x:Class="WpfApp26.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

然后在后面的代码中:

public enum Theme { A, B }

public partial class App : Application
{
    private Theme _theme = Theme.A;

    public App()
    {
        //_theme = ... load from a config or something
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        Resources.MergedDictionaries.Add(new ResourceDictionary()
        {
            Source = new Uri($"/SharedStyles;Component/SharedStyles_{_theme}.xaml", UriKind.RelativeOrAbsolute)
        });
    }
}

但是,这不起作用,找不到

BigButton
样式,因为现在的顺序是错误的。
SharedStyles_X.xaml
需要在
Resources.xaml
之前出现,而不是在之后。

所以我把它改为:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    Resources.MergedDictionaries.Insert(0, new ResourceDictionary()
    {
        Source = new Uri($"/SharedStyles;Component/SharedStyles_{_theme}.xaml", UriKind.RelativeOrAbsolute)
    });
}

但这仍然不起作用?似乎静态资源已经被解决并且第一次失败,并且更改顺序不会更新内容。也就是说,您必须从一开始就按照正确的顺序添加它们,您不能只是在之后重新排序。

确实有效的一件事如下:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    var previous = Resources.MergedDictionaries.ToArray();
    Resources.MergedDictionaries.Clear();

    Resources.MergedDictionaries.Add(new ResourceDictionary()
    {
        Source = new Uri($"/SharedStyles;Component/SharedStyles_{_theme}.xaml", UriKind.RelativeOrAbsolute)
    });

    foreach (var item in previous)
    {
        Resources.MergedDictionaries.Add(new ResourceDictionary()
        {
            Source = item.Source
        });
    }
}

但是现在你要重新加载两次......在我真正的工作应用程序中,内容更多,这似乎很浪费。

有更好的方法吗?

我很感激您可以在代码后面完成所有这些操作,但理想情况下,我仍然希望支持将内容添加到 xaml 中的 App.xaml 资源中以提高可读性。

因为我真正的应用程序看起来像这样,我不想将所有这些移动到代码后面:

<Application x:Class="WpfApp26.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="LocalStuff.xaml" />
                <ResourceDictionary Source="ThirdPArtyStuff.xaml" />
                <ResourceDictionary Source="Other.xaml" />
                <ResourceDictionary Source="Blah.xaml" />
                <ResourceDictionary Source="BlahBlah.xaml" />
                <ResourceDictionary Source="BlahBlahBlah.xaml" />
                <!-- etc -->
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

wpf resourcedictionary staticresource
1个回答
0
投票

设法想出一个更清洁的解决方案。

<Application x:Class="WpfApp26.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:wpfapp26="clr-namespace:WpfApp26"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <wpfapp26:ThemeResourceDictionary ThemeSource="/SharedStyles;Component/Theme_#.xaml" />
                <ResourceDictionary Source="Resources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

public class ThemeResourceDictionary : ResourceDictionary
{
    private string? _themeSource;

    public string? ThemeSource
    {
        get => _themeSource;
        set
        {
            _themeSource = value;
            SetSource();
        }
    }

    private void SetSource()
    {
        if (string.IsNullOrEmpty(_themeSource))
            throw new ArgumentException(nameof(ThemeSource));

        var theme = ((App)Application.Current).Theme.ToString();
        Source = new Uri(_themeSource.Replace("#", theme), UriKind.RelativeOrAbsolute);
    }
}

public enum Theme { A, B }

public partial class App : Application
{
    public Theme Theme { get; private set; }

    public App()
    {
        Theme = Theme.A;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.