【发布时间】:2017-08-25 09:52:44
【问题描述】:
我在将 Caliburn Micro 的 Conductor<>.Collection.OneActive 与 MahApps.Metro HamburgerMenu 一起使用时遇到了一些问题。来自fewsamples,但它们都没有解决我的情况。
我的所有代码都可以在this Github repository 中找到。
我想在HamburgerMenu 中显示一组窗格。每个窗格都有一个标题和一个显示名称:
public interface IPane : IHaveDisplayName, IActivate, IDeactivate
{
PackIconModernKind Icon { get; }
}
在我的例子中,IPane 是使用 PaneViewModel 实现的:
public class PaneViewModel : Screen, IPane
{
public PaneViewModel(string displayName, PackIconModernKind icon)
{
this.Icon = icon;
this.DisplayName = displayName;
}
public PackIconModernKind Icon { get; }
}
这有以下观点:
<UserControl x:Class="CaliburnMetroHamburgerMenu.Views.PaneView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Padding="12"
Background="Pink">
<StackPanel Orientation="Vertical">
<TextBlock Text="Non-bound text" />
<TextBlock x:Name="DisplayName" FontWeight="Bold" />
</StackPanel>
</UserControl>
我的外壳视图模型也很简单。它继承自 Conductor<IPane>.Collection.OneActive,并接受一个窗格列表,并添加到其 Items 集合中:
public class ShellViewModel : Conductor<IPane>.Collection.OneActive
{
public ShellViewModel(IEnumerable<IPane> pages)
{
this.DisplayName = "Shell!";
this.Items.AddRange(pages);
}
}
现在,这对我来说非常模糊。这是ShellView.xaml的摘录:
<controls:HamburgerMenu
ItemsSource="{Binding Items, Converter={StaticResource PaneListToHamburgerMenuItemCollection}}"
SelectedItem="{Binding ActiveItem, Mode=TwoWay, Converter={StaticResource HamburgerMenuItemToPane}}">
<ContentControl cal:View.Model="{Binding ActiveItem}" />
<controls:HamburgerMenu.ItemTemplate>
<DataTemplate>
<Grid x:Name="RootGrid"
Height="48"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<iconPacks:PackIconModern
Grid.Column="0"
Kind="{Binding Icon}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Label}" />
</Grid>
</DataTemplate>
</controls:HamburgerMenu.ItemTemplate>
</controls:HamburgerMenu>
为了完成这项工作,我依赖于两个转换器(坦率地说,他们做的比他们应该做的更多)。一个转换器采用 ICollection<IPane> 并创建一个带有 HamburgerMenuIconItems 的 HamburgerMenuItemCollection,它们现在包含使用视图模型和菜单项的 Tag 属性的双向链接。
class PaneListToHamburgerMenuItemCollection : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var viewModels = value as ICollection<IPane>;
var collection = new HamburgerMenuItemCollection();
foreach (var vm in viewModels)
{
var item = new HamburgerMenuIconItem();
item.Label = vm.DisplayName;
item.Icon = vm.Icon;
item.Tag = vm;
vm.Tag = item;
collection.Add(item);
}
return collection;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
每当SelectedItem 更改时,第二个转换器使用此Tag 在视图模型和菜单项之间进行转换:
class HamburgerMenuItemToPane : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((IPane)value)?.Tag;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((HamburgerMenuIconItem)value)?.Tag;
}
}
当我运行此代码并单击汉堡菜单中的项目时,每次都会切换页面。问题是,当应用程序首次运行时,没有选定的窗格,并且您无法使用ShellViewModel 中可用的任何激活覆盖(例如OnViewAttached 或OnActivate,或事件构造函数)设置一个,如连接Tag 的转换器代码尚未运行。
我对可行解决方案的要求:
- Caliburn 的指挥必须负责,因为堆栈中的视图和视图模型依赖于运行的激活逻辑。
- 应该可以在激活
ShellViewModel期间的某个时间点从 Caliburn 激活第一个项目 - 应尊重关注点分离,即视图模型不应知道视图中正在使用汉堡菜单。
请参阅the GitHub repository 以获取应立即运行的解决方案。
【问题讨论】:
标签: c# wpf xaml caliburn.micro mahapps.metro