2

我正在使用 MVVM 开发一个 WPF 项目,并且我正在尝试实现一个动态更改主题的功能。主题信息位于单独的 xaml 文件中(即 Theme1.xaml、Theme2.xaml)。由于各种原因,我想在 ViewModel 类中而不是在 View.xaml 的代码隐藏文件中进行实际的主题更改。

我尝试了几个想法,但没有任何工作:

  • 我尝试将 View 的 ResourceDictionary 绑定到 ViewModel 中的变量,但被告知无法在 ResourceDictionary 类型的 Source 属性上设置绑定

  • 我的 ViewModel 类中没有任何类型的 View 对象可以调用“UpdateTheme”方法

关于如何从 ViewModel 类更改 View 类中的 MergedDictionary 引用的任何想法?

谢谢!

4

3 回答 3

4

我之前在这里处理过同样的问题,我在我的情况下所做的可能是它可以帮助你。

将所有主题文件(theme1.xaml、theme2.xaml...)复制到 exe 路径下的 Themes 文件夹中。并尝试使用下面的示例代码。使用绑定

C#:

private void ChangeTheme(FileInfo _SelectTheme)
{
    App.Current.Resources.Clear();
    App.Current.Resources.Source = new Uri(_SelectTheme.FullName, UriKind.Absolute);
}
private ObservableCollection<FileInfo> _files;
public ObservableCollection<FileInfo> Files
{
    get { return _files; }
    set { _files = value; OnChanged("Files"); }
}
public MainWindow()
{
    this.InitializeComponent();

    Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
    {
        var localthemes = new System.IO.DirectoryInfo("Themes").GetFiles();
        if (Files == null)
            Files = new ObservableCollection<FileInfo>();
        foreach (var item in localthemes)
        {
            Files.Add(item);
        }
        SelectedTheme = Files[0];
    }));

    this.DataContext = this;
}

public event PropertyChangedEventHandler PropertyChanged;
public void OnChanged(string name)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
}

XAML:

<Window x:Class="WPFTheme.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Name="Window"
        Title="MainWindow"
        Width="640"
        Height="480">

    <Grid x:Name="LayoutRoot" Background="{DynamicResource DisabledForegroundBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.285*" />
            <ColumnDefinition Width="0.365*" />
            <ColumnDefinition Width="0.35*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="0.132*" />
            <RowDefinition Height="0.162*" />
            <RowDefinition Height="0.403*" />
            <RowDefinition Height="0.168*" />
            <RowDefinition Height="0.135*" />
        </Grid.RowDefinitions>
        <Button Width="57"
                Margin="15,13,0,10.872"
                HorizontalAlignment="Left"
                Content="Enabled" />
        <Button Width="72"
                Margin="0,14,17.12,10.872"
                HorizontalAlignment="Right"
                Content="Disabled"
                IsEnabled="False" />
        <TextBlock Grid.Column="1"
                   Width="69"
                   Margin="11.88,15,0,27.872"
                   HorizontalAlignment="Left"
                   Text="TextBlock"
                   TextWrapping="Wrap" />
        <TextBox Grid.Column="1"
                 Width="64"
                 Height="21"
                 Margin="9.88,0,0,4.872"
                 HorizontalAlignment="Left"
                 VerticalAlignment="Bottom"
                 Text="TextBox"
                 TextWrapping="Wrap" />
        <TextBox Grid.Column="1"
                 Height="21"
                 Margin="88.88,0,35.8,3.872"
                 VerticalAlignment="Bottom"
                 IsEnabled="False"
                 Text="TextBox Disabled"
                 TextWrapping="Wrap" />
        <CheckBox Grid.Row="1"
                  Width="71"
                  Height="14"
                  Margin="11,7.128,0,0"
                  HorizontalAlignment="Left"
                  VerticalAlignment="Top"
                  Content="CheckBox" />
        <CheckBox Grid.Row="1"
                  Width="71"
                  Height="14"
                  Margin="0,8.128,15.12,0"
                  HorizontalAlignment="Right"
                  VerticalAlignment="Top"
                  Content="Disabled"
                  IsEnabled="False" />
        <ComboBox Grid.Column="2"
                  Width="94"
                  Margin="8.2,18,0,11.872"
                  HorizontalAlignment="Left"
                  ItemsSource="{Binding Files}"
                  SelectedItem="{Binding SelectedTheme,
                                         Mode=TwoWay,
                                         UpdateSourceTrigger=PropertyChanged}" />
        <ComboBox Grid.Column="2"
                  Width="94"
                  Margin="0,17,14,12.872"
                  HorizontalAlignment="Right"
                  IsEnabled="False"
                  ItemsSource="{Binding Files}" />
        <DataGrid Grid.Row="2"
                  Grid.Column="1"
                  Margin="8.88,6.876,7.8,62.862"
                  AutoGenerateColumns="True"
                  ItemsSource="{Binding Files}" />
        <DatePicker Grid.Row="2"
                    Height="23"
                    Margin="10,0,15,147"
                    VerticalAlignment="Bottom" />
        <GroupBox Grid.Row="2"
                  Grid.Column="2"
                  Margin="6.2,2.876,6,5.862"
                  Header="GroupBox">
            <ScrollViewer Margin="6,0.723,1,1" ScrollViewer.HorizontalScrollBarVisibility="Visible">
                <ListBox Width="161"
                         Height="108"
                         ItemsSource="{Binding Files}" />
            </ScrollViewer>
        </GroupBox>
        <ListView Grid.Row="2"
                  Grid.Column="1"
                  Height="59"
                  Margin="12.88,0,5.8,-4.138"
                  VerticalAlignment="Bottom"
                  ItemsSource="{Binding Files}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="File Name">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Name}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
        <ProgressBar x:Name="progressBar"
                     Grid.Row="1"
                     Grid.Column="1"
                     Height="20"
                     Margin="5.88,6.128,61.8,0"
                     VerticalAlignment="Top"
                     Value="50" />
        <RadioButton Grid.Row="1"
                     Width="64"
                     Margin="11,25.128,0,29.124"
                     HorizontalAlignment="Left"
                     Content="RadioButton" />
        <RadioButton Grid.Row="1"
                     Width="51"
                     Margin="0,25.128,33.12,29.124"
                     HorizontalAlignment="Right"
                     Content="RadioButton"
                     IsEnabled="False" />
        <Slider Grid.Row="1"
                Grid.Column="1"
                Margin="11.88,34.128,38.8,15.124"
                AutoToolTipPlacement="BottomRight"
                Maximum="{Binding Maximum,
                                  ElementName=progressBar}"
                Minimum="{Binding Minimum,
                                  ElementName=progressBar}"
                Value="{Binding Value,
                                ElementName=progressBar}" />
        <TabControl Grid.Row="1"
                    Grid.Column="2"
                    Margin="7.2,9.128,9,0.124">
            <TabItem Header="TabItem">
                <Grid Background="#FFE5E5E5" />
            </TabItem>
            <TabItem Header="TabItem">
                <Grid Background="#FFE5E5E5" />
            </TabItem>
        </TabControl>
        <TreeView Grid.Row="3"
                  Margin="8,5.138,12.12,1.79"
                  ItemsSource="{Binding Files}" />
        <ToolBar Grid.Row="4"
                 Grid.ColumnSpan="2"
                 Margin="10,9.21,104.8,17">
            <Button />
            <CheckBox />
            <ComboBoxItem />
            <MenuItem />
            <Separator />
            <TabItem />
        </ToolBar>
    </Grid>
</Window>
于 2012-06-21T13:45:48.357 回答
2

我像这样在我的应用程序启动时处理主题切换。

Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(Themes.Where(p => p.Value.ThemeName == "MyTheme").SingleOrDefault().Value.Theme);

我首先清除Dictionaries以删除任何预设Theme。我这样做是因为我在编辑器中使用默认主题,然后run-time根据用户配置在切换期间。

我重新启动应用程序以加载新主题,但是当您保存状态等时,您ViewModel应该能够重新加载,UI而无需完全重新启动应用程序。然而,这不是我的项目的要求,所以我从来没有走那么远。

您可能只是从 . 中传递您的主题名称View,然后使用您的ViewModel.

于 2012-06-21T13:25:50.477 回答
1

您的问题是您试图直接从您的 ViewModel 更改视图,这是不允许的。您需要提出一个基于属性绑定的更被动的解决方案。

我的方法是在主视图的代码隐藏中使用一小段代码来切换合并字典中的资源文件,并且它执行此操作的方式可以通过绑定到的 ViewModel 中的属性值来确定。MVVM 中允许使用少量代码隐藏来支持以视图为中心的行为。

于 2012-06-21T13:25:29.503 回答