I will use the the application that we built in my previous post, Creating a Complete Tabbed Inteface in WPF using MVVM. If you have read it or are confident that you can understand what it does, just by looking at source code ( it's very easy if you know a little about MVVM and WPF ), you can download it here and use it for this post. But if you don't think you can get it, I strongly recommend you read it.
Let's look at how we are going to implement it. We are going to have a folder called Themes which will have a lot of binaries (.dll) each representing a theme. Our application gets all the binaries when it starts and loads the theme, the name of which matches with the one stored in the application's settings. To achieve this, we are going to move the themes into a separate project.
Create a new WPF User Control library project (in the same solution) and name it DefaultTheme. Move the MainView.xaml to the DefaultTheme project and change the corresponding namespaces. Our code is still messed up, it won't compile. For this to run, the
App
class needs a MainView. We are going to give it using MEF. Add a reference the System.ComponentModel.Composition
assembly from MEF to the TabbedLayout project. If you don't have it, download it at the CodePlex site of MEF. Now delete the App.xaml file; we are going to create our own. If you don't want to delete it, you can modify it and get our app running. But for this post, let's say you've deleted it. Create a new class App that derives from the Application class. Add the Main method that runs it.class App : Application
{
public static void Main(string[] args)
{
App app = new App();
app.Run();
}
}
Our application now runs (provided you've changed the namespaces correctly). But it does not show any window. To show a window, it needs a theme. By theme, we mean a name of the theme and a Window that can be used as the MainWindow of our application. Let's define an interface ITheme.{
public static void Main(string[] args)
{
App app = new App();
app.Run();
}
}
public interface ITheme
{
public string Name { get; }
public Window MainView { get; }
}
public class DefaultTheme : ITheme
{
private MainView _mainView;
public string Name
{
get { return "Default"; }
}
public Window MainView
{
get { return _mainView ?? (_mainView = new MainView()); }
}
}
public IEnumerable<ITheme> Themes { get; set; }
private ITheme _selectedTheme;
[STAThread]
public static void Main(string[] args)
{
App app = new App();
app.Configure();
app.Run();
}
private void Configure()
{
if (Themes.Count() == 0)
{
MessageBox.Show("No theme is present. Application is shutting down");
Shutdown();
return;
}
string themeName = TabbedLayout.Properties.Settings.Default["ThemeName"] as string; //Get the theme name from settings
_selectedTheme = Themes.SingleOrDefault(x => x.Name.Equals(themeName, StringComparison.OrdinalIgnoreCase)); //Get the theme with the above name from the collection
if (_selectedTheme == null) //If there is no such theme, use the first theme in the collection
{
MessageBox.Show("The selected theme has been modified or removed. IncEditor will start with one of your available themes. You need to restart the application");
_selectedTheme = Themes.First(x => true);
TabbedLayout.Properties.Settings.Default["ThemeName"] = _selectedTheme.Name;
}
_selectedTheme.MainView.DataContext = new MainViewModel();
MainWindow = _selectedTheme.MainView; //Set the MainWindow of the Application to the MainWindow of the selected theme
_selectedTheme.MainView.Show();
}
We need to set the ThemeName setting in our application. Right Click the TabbedLayout project in the Solution Explorer and select Properties. Add a new User setting ThemeName and set its value to Default. We are going to use the Default theme.public static void Main(string[] args)
{
App app = new App();
app.Configure();
app.Run();
}
private void Configure()
{
if (Themes.Count() == 0)
{
MessageBox.Show("No theme is present. Application is shutting down");
Shutdown();
return;
}
string themeName = TabbedLayout.Properties.Settings.Default["ThemeName"] as string; //Get the theme name from settings
_selectedTheme = Themes.SingleOrDefault(x => x.Name.Equals(themeName, StringComparison.OrdinalIgnoreCase)); //Get the theme with the above name from the collection
if (_selectedTheme == null) //If there is no such theme, use the first theme in the collection
{
MessageBox.Show("The selected theme has been modified or removed. IncEditor will start with one of your available themes. You need to restart the application");
_selectedTheme = Themes.First(x => true);
TabbedLayout.Properties.Settings.Default["ThemeName"] = _selectedTheme.Name;
}
_selectedTheme.MainView.DataContext = new MainViewModel();
MainWindow = _selectedTheme.MainView; //Set the MainWindow of the Application to the MainWindow of the selected theme
_selectedTheme.MainView.Show();
}
Now our application is all ready, except that it does not have the Themes collection. We haven't initialized it yet. Now, we need MEF to load all the assemblies from the Themes folder and add all the IThemes to the collection. To do this, we need to add an ImportManyAttribute on the Themes collection in the App class.
We need to export the DefaultTheme class from the DefaultTheme project. Add an
[Export(typeof(ITheme))]
public class DefaultTheme : ITheme
private void Compose()
{
var catalog = new DirectoryCatalog(@"..\..\Themes");
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
private void Configure()
{
Compose();
I found this on internet and it is really very nice.
ReplyDeleteAn excellent blog.
Great work!