So, I'd like to have a nice, dynamic menu or toolbar (yup, either or both) that Prism modules can add items to. It'd be nice if they could place those items where they need them, such that the menus are still ordered in a useful manner for the user. It'd also be nice if a module could add an item that was always displayed, versus an item that is only displayed when a view in the module is active.
Whew.
Spec the code. Design the code. Implement the code. (Okay, so I didn't do that completely, and I paid for it. But let's pretend that I practice what I preach...)
- The menu / toolbar service should not be tied to its view. That is, items added should be able to be displayed in a menu, toolbar, "ribbon", or some hybrid thereof.
- Modules should be able to differentiate between the various types however - I should know if I'm adding an item to a menu or a toolbar.
- Modules should be able to specify whether an item has a parent (which can be any previously added item), and place the item relative to some other child of that parent.
- Modules should be able to add a seperator.
- Items that are added can be either permanent (always shown), or tied to the activity of the module.
You'll note that I already defined that the dynamic menu / toolbar thingy should be a service - based on the shellacking of my previous attempt, its become pretty obvious that this thing should be a service interface in the Prism project's Infrastructure DLL. I didn't list that in the requirements, but it probably should be.
The first requirement should be pretty easy to meet - the service can expose an ObservableCollection of items, and the various UI elements can use a HierarchicalDataTemplate to format the collection. This does mean that I'll probably need multiple implementations of the service however (requirement #2) - that shouldn't be a problem however using Unity's Resolve
Requirement three means that each item will have to have children items, and the service should:
- expose a way to add an item that optionally includes a parent and a predecessor
- expose an IEnumerable<> of items, so that modules can query for parents / predecessors
- (maybe?) expose a way to query for items, since the hierarchical nature of the data means queries will have to be recursive (and writing recursive LINQ operators everywhere would kinda suck)
Requirement five is tricky. There's a couple of ways Prism handles what is viewed / active:
- IActiveAware. Views can implement this interface, which gives you a boolean field indicating whether or not the view is active, and an event that can be subscribed to when that field changes. This is tied to a Region's concept of what is active. If we allow modules to provide an IActiveAware object, a menu item could be tied to that, and we could show / hide a menu item based on that object.
- The service could implement INavigationAware. This would give us notification when a navigation request is made to some object. I'm not sure what happens if we register an object that is INavigationAware that is not designed to handle a navigation URI request - in essence, we want our service to snoop navigation, not actually perform in it.
- A PresentationEvent, that modules can fire when they want their items shown or hidden.
Option #2 feels a little fishy. While it seems ideal, I don't like our service being a navigation object when it isn't; that, and at least two of the methods (OnNavigatedTo and OnNavigatedFrom) would never be called. Implmenting half of interfaces always feels a little bad, so I'm inclined not to pick that route.
Option #3 gives the most granularity, but also requires more out of the users of the service. I want this to be as "fire and forget" as possible - I put in my menu items, and I move on.
So, I'm probably going to go with Option #1.
Lots of design decisions made, with a lot of tradeoffs. Next time: more design decisions, and some initial implementation.