【问题标题】:Conducting MahApps.Metro HamburgerMenu with Caliburn Micro使用 Caliburn Micro 进行 MahApps.Metro HamburgerMenu
【发布时间】: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&lt;IPane&gt;.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&lt;IPane&gt; 并创建一个带有 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 中可用的任何激活覆盖(例如OnViewAttachedOnActivate,或事件构造函数)设置一个,如连接Tag 的转换器代码尚未运行。

我对可行解决方案的要求:

  1. Caliburn 的指挥必须负责,因为堆栈中的视图和视图模型依赖于运行的激活逻辑。
  2. 应该可以在激活ShellViewModel 期间的某个时间点从 Caliburn 激活第一个项目
  3. 应尊重关注点分离,即视图模型不应知道视图中正在使用汉堡菜单。

请参阅the GitHub repository 以获取应立即运行的解决方案。

【问题讨论】:

    标签: c# wpf xaml caliburn.micro mahapps.metro


    【解决方案1】:

    我认为问题是由控件内部的HamburgerMenu_Loaded 方法引起的。如果在控件加载之前有选中的项,则替换汉堡菜单的内容:

    private void HamburgerMenu_Loaded(object sender, RoutedEventArgs e)
    {
        var selectedItem = this._buttonsListView?.SelectedItem ?? this._optionsListView?.SelectedItem;
        if (selectedItem != null)
        {
            this.SetCurrentValue(ContentProperty, selectedItem);
        }
    }
    

    在您的情况下,ContentControl 被删除,您的 Conductor 无法完成其工作。

    我正在尝试通过将代码更改为以下内容来查看是否可以直接在 MahApps 中更改此行为:

    if (this.Content != null)
    {
        var selectedItem = this._buttonsListView?.SelectedItem ?? this._optionsListView?.SelectedItem;
        if (selectedItem != null)
        {
            this.SetCurrentValue(ContentProperty, selectedItem);
        }
    }
    

    【讨论】:

    • 您是否设法找到解决方案或解决方法? punker76 或 MahApps 的其他人是否发现了这个问题?
    • 这将在 v1.6.0 中最终修复
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-29
    • 2012-10-24
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-20
    相关资源
    最近更新 更多