- The menu (or pseudo-toolbar) was implemented in its own Prism Module. Other modules obtained the service by referencing the module, and could add menu items by accessing the service.
- Menu items were added by specifying a command, menu name, a reference to an image to display, and optionally, a parent menu item.
///
/// Add a menu item to the toolbar
///
public void AddMenu(Action<object>callbackFunction, string menuName, string iconLocation, string parentMenu)
{
if (String.IsNullOrEmpty(menuName))
throw new ArgumentNullException("menuName");
if (ExistingMenuNames.Contains<String>(menuName))
throw new InvalidOperationException("Cannot add a menu item with the same name as an existing menu item");
ToolbarObject newObject = new ToolbarObject(
menuName,
iconLocation,
new CompositeCommand());
// Add a command, if available
if (callbackFunction != null)
newObject.Command.RegisterCommand(new DelegateCommand<Object>(callbackFunction));
// Set up the parent menu, if available
if (!String.IsNullOrEmpty(parentMenu))
{
if (!ExistingMenuNames.Contains<String>(parentMenu))
throw new InvalidOperationException("Cannot add a menu to a parent menu item that doesn't exist");
if (_model == null)
_model = _container.Resolve<IToolBarPresentationModel>();
var parent = (from item in _model.ToolbarItems
where String.Equals(item.Name, parentMenu, StringComparison.InvariantCulture)
select item).Single<ToolbarObject>();
parent.Children.Add(newObject);
}
else
{
// Insert after Main, keeping Help last
_model.ToolbarItems.Insert(1, newObject);
}
}The big issue I had run into before was actually displaying the items in the View appropriately. Enter the wonderful HierarchicalDataTemplate, and its little (very necessary) cousin, ItemContainerStyle. The view code using these two is below.
<UserControl.Resources>
<model:ToolBarPresentationModel x:Key="modelData" />
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Path=Command}"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<HierarchicalDataTemplate DataType="{x:Type model:ToolbarObject}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Path=Command}"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel VerticalAlignment="Top" Margin="5,0,5,0">
<Image Height="24" VerticalAlignment="Center" Source="{Binding Path=ImageLocation}"/>
<ContentPresenter Content="{Binding Path=Name}" HorizontalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource modelData}"/>
</UserControl.DataContext>
<Grid>
<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent"
ItemsSource="{Binding}">
</Menu>
</Grid>This code creates a menu where each menu item in the parent menu can have children, or be a clickable menu item itself. Each menu item provides its own path to a resource, such that each menu item shows an image and a text description. Due to the limited nature of the application, the first menu is always kept as Main (or File), while the last is always kept as Help.
While this approach worked great for the application at hand, its not very useful for a general application. To whit, the following are some of the obvious drawbacks:
- First come, first served. If Module A adds an item to the menu without a parent, that will get placed after Main. If Module B does the same, then it will get added after Main, pushing Module A's menu further down the line. This may not be desireable.
- Nesting of menus only goes down one level. If you specify a parent that is not in the first row of the menu, the parent won't be found.
- Specifying an Action as the parameter only gives us a callback, and doesn't allow multiple modules to tie a callback to a single menu option. This works fine if a menu option should only be handled by a single module, but works poorly if a number of modules need to respond to a menu click, e.g., Save or Exit.
- Menu options that are added under a parent are added on a first come, first served basis. This can lead to some odd and confusing orderings, depending on the Module loading.
- No separators. Ugh.
- The toolbar service module should not have to be referenced directly by other modules.
- Does everything really need an image? This approach was supposed to emulate (to a very, very limited extent) a Ribbon-style control, combining both menu / toolbar approaches. While kind of fun and quirky, its not appropriate for a typical line of business application.
Edit
Also: Merry Christmas! The fact that I'm interested in writing about code again may be an "end of the year" thing, but I'll see where it takes me for now.
0 comments:
Post a Comment