对所有代码感到抱歉,但我觉得我需要它来解释问题。另外,我知道一些问题可以通过使用动态资源来解决,但我也需要它来处理静态资源,因为我们使用了大量
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>
设法想出一个更清洁的解决方案。
<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;
}
}