implementar - WPF y MVVM: cambiando los temas dinĂ¡micamente
wpf c# mvvm (3)
He trabajado con el mismo problema de tiempo anteriormente, lo que hice en mi caso puede ser de ayuda.
Copie todos sus archivos de temas (theme1.xaml, theme2.xaml ...) en la carpeta Temas en su ruta exe. y prueba con el siguiente código de muestra. usando ligaduras
public partial class MainWindow : Window, INotifyPropertyChanged
{
private FileInfo _SelectTheme;
public FileInfo SelectedTheme
{
get { return _SelectTheme; }
set
{
_SelectTheme = value;
OnChanged("SelectedTheme");
ChangeTheme(_SelectTheme);
}
}
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));
}
}
<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>
Estoy trabajando en un proyecto de WPF usando MVVM y estoy tratando de implementar una característica que cambie el tema de forma dinámica. La información de temas se encuentra en archivos xaml separados (es decir, Theme1.xaml, Theme2.xaml). Quiero hacer el cambio de tema real en la clase ViewModel en lugar de en el código detrás del archivo de View.xaml por varias razones.
He intentado un par de ideas pero no puedo hacer que nada funcione:
Intenté vincular el ResourceDictionary of View a una variable en ViewModel pero me dijeron que no se puede establecer un enlace en la propiedad Source de tipo ResourceDictionary
No tengo ningún tipo de objeto View en mi clase ViewModel al que llamar un método "UpdateTheme"
¿Alguna idea sobre cómo puedo cambiar la referencia de MergedDictionary en mi clase View desde la clase ViewModel?
¡Gracias!
Manejo el cambio de tema en la puesta en marcha en mi aplicación de esta manera.
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(Themes.Where(p => p.Value.ThemeName == "MyTheme").SingleOrDefault().Value.Theme);
Primero borro los Dictionaries
para eliminar cualquier Theme
preestablecido. Lo hago porque utilizo un tema predeterminado en el editor, y luego durante el cambio en run-time
dependiendo de la configuración de los usuarios.
Reinicio la aplicación para cargar el nuevo tema, pero a medida que guarda los estados, etc. en su ViewModel
, debe poder volver a cargar la UI
sin tener que reiniciar completamente la aplicación. Sin embargo, este no era un requisito para mi proyecto, así que nunca llegué tan lejos.
Probablemente podría simplemente pasar el nombre de su tema desde la View
, y luego analizarlo usando la lógica de su ViewModel
.
Su problema es que está intentando cambiar la Vista directamente desde su ViewModel, lo cual no está permitido. Necesitas encontrar una solución más pasiva basada en enlaces de propiedad.
Mi enfoque sería tener un pequeño fragmento de código en el código subyacente de su vista principal que cambie los archivos de recursos en sus diccionarios fusionados, y la forma en que lo hace puede ser reflejada por el valor de una propiedad en su ViewModel al que está vinculado. Se permite una pequeña cantidad de código subyacente para admitir el comportamiento centrado en la vista en MVVM.