Hey,
I am trying to set up a menu in my ribbon that allows my users to dynamically choose a theme. I didn't have any luck finding anything built in to do this (does something like this exist?) so I tried something on my own. Attached is a sample project
Basically I used the ThemeManager class to get a list of the themes, created model objects from the themes, put them on a view model, and bound the ItemsSource of the application menu to ViewModel.Themes (I put the button tools directly in the application menu in my sample code so that it's quicker to get to -- once its working I would want it in a menu item in the application menu). I set up a DataTemplate to create button tools for each of my theme model objects. The button tools are bound to a ChangeThemeCommand, passing the theme model as the argument to the command.
My questions are:
1.) Is the way that I set up the DataTemplate/ItemsSource binding to fill the menu and bind to my command correct or is there some sort of bug with command binding?
The command binding sort of works, but is extremely flakey. If I click the Fall theme one time, it does nothing. If I keep clicking it, it eventually works. It's not hitting my command's CanExecute methods so it feels like the command isn't bound for the first few clicks?
Being able to set up dynamic buttons bound to commands is particularly important to me because I know of a few other places where I'll need to do this.
2.) Is the way that I change themes valid (in the ChangeThemeCommand class)?
I was guessing when I set up that chunk of code. Basically, I want as many of the controls as possible to take on the theme that the user chooses and the rest to have some sort of default theme (ie if the new theme doesn't include them).
Thanks
Cool, I'll try out the list of commands approach.
Thank you for all of your help.
jlotridge said:1.) Just to make sure I understand what I need to do: if I expose a List<ChangeThemeCommand> on my view model and bind it to the ItemsSource of a menu tool -- the menu tool will automatically create a button tool for each command with the button tool's command being that command?
jlotridge said:Is the ThemeCommandConverter class that was added an important part of this or does it do something else?
2) There is no single way to accomplish things with WPF. Adding/removing from the ApplicationResources is a valid way to change the templates/styles.
3) You would have to retemplate the ToolMenuItem and even then since its just the content of the menu item there might be other ramifications - e.g. the menuitem's enabled state wouldn't be tied to the tool, the menu's click may trigger and not the button tool because the button tool is just the content. You can look at the default styles that ship with the product if you want to go this route. I would recommend putting in commands.
Thank you for your response.
I have a few questions:
1.) Just to make sure I understand what I need to do: if I expose a List<ChangeThemeCommand> on my view model and bind it to the ItemsSource of a menu tool -- the menu tool will automatically create a button tool for each command with the button tool's command being that command? Is the ThemeCommandConverter class that was added an important part of this or does it do something else?
2.) Is my implementation for changing themes the way the it is supposed to be done?
3.) Is it at all possible to resize the button tools created in my original approach so that the created buttons behave like regular buttons (fill up the entire space in this case I guess?), or is the approach in general (normalizing the data out of the command) frowned upon?
No, you won't be able to use this specific approach. The binding isn't flakey. What is happening is that the ButtonTool from your datatemplate is becoming the content in the content presenter within the menu item so it appears to work as long as you click on the text of the menu item (which is where the contentpresenter is within the menu item). If you want to take this kind of approach then you will need to expose a collection of commands and bind that to the itemssource. The menu item uses that command as the command for itself. So you will need to create a separate command instance for each theme - probably just add a ThemeModel property to it - and bind to the collection of those. Since you will have different instances of the command, you'll probably need to statically share the theme resource dictionary you added.
BTW, be careful about your ICommand implementation. Just because you're not using the CanExecuteChanged doesn't mean something won't be hooked into it so if you keep that command around you could end up rooting elements that were using that command. If you don't need it then you could explicitly implement empty add{} remove{} for the event or follow what routed command does which is to add the listener to the CommandManager.RequerySuggested. The former is probably ok since the state of the command is constant.
Here's a modified version of the command class.