设置 mahapps 汉堡包菜单标签的自定义控件的数据上下文

Set the data context of a custom control of mahapps hamburger menu tag

我遇到过很多关于这个主题的问题,但是,我还没有找到可以解决我的问题的问题。例如,我不能使用 this solution since I'm using MVVM and I register my view models with the help of the host builder. This question sets a view model's property to a text block's text property with the help of this 一个。我一直在尝试使用 Binding Proxy,但无法正常工作。

更具体地说,我使用 MVVM,因此没有默认构造函数。我在 App.xaml 中设置视图的数据上下文,如下所示:

<Application.Resources>
     <ResourceDictionary>

           ..

          <DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
                <views:HomeView />
          </DataTemplate>

          <DataTemplate DataType="{x:Type viewmodels:LogoutViewModel}">
               <views:LogoutView />
          </DataTemplate>

     </ResourceDictionary>
</Application.Resources>

我有汉堡菜单的用户控件如下所示:

HamburgerMenuRipple.xaml

<mah:HamburgerMenu
        x:Name="HamburgerMenuControl"
        DisplayMode="CompactInline"
        HamburgerButtonClick="HamburgerMenuControl_HamburgerButtonClick"
        IsPaneOpen="True"
        ItemInvoked="HamburgerMenuControl_OnItemInvoked"
        ItemTemplate="{StaticResource MenuItemTemplate}"
        OpenPaneLength="275"
        OptionsItemTemplate="{StaticResource MenuItemTemplate}"
        SelectedIndex="0"
        Style="{StaticResource MahApps.Styles.HamburgerMenu.Ripple}"
        VerticalScrollBarOnLeftSide="False">

        <!--  Header  -->
        <mah:HamburgerMenu.HamburgerMenuHeaderTemplate>
            <DataTemplate>
                <TextBlock
                    Padding="10,0,0,0"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Center"
                    FontSize="16"
                    FontWeight="DemiBold"
                    Foreground="White"
                    TextWrapping="Wrap" />
            </DataTemplate>
        </mah:HamburgerMenu.HamburgerMenuHeaderTemplate>


        <!--  Items  -->
        <mah:HamburgerMenu.ItemsSource>

            <mah:HamburgerMenuItemCollection>
                <mah:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=Home}" Label="Home" />

                <mah:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=Bell}" Label="Notifications" />

                ..

            </mah:HamburgerMenuItemCollection>
        </mah:HamburgerMenu.ItemsSource>

        <!--  Options  -->
        <mah:HamburgerMenu.OptionsItemsSource>
            <mah:HamburgerMenuItemCollection>
                <mah:HamburgerMenuSeparatorItem x:Name="Separator_2" IsVisible="{Binding ElementName=Separator_2}" />

                <mah:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=Cog}" Label="Settings" />

                <mah:HamburgerMenuIconItem Icon="{iconPacks:Material Kind=Logout}" Label="Logout">
                    <mah:HamburgerMenuIconItem.Tag>
                        <views:LogoutView/> <!--How can I set the datacontext here?-->
                    </mah:HamburgerMenuIconItem.Tag>
                </mah:HamburgerMenuIconItem>

            </mah:HamburgerMenuItemCollection>
        </mah:HamburgerMenu.OptionsItemsSource>

        <mah:HamburgerMenu.ContentTemplate>
            <DataTemplate DataType="{x:Type mah:HamburgerMenuIconItem}">
                <Grid Margin="20,0,10,0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock
                        Grid.Row="0"
                        Margin="0,15,0,5"
                        Padding="0"
                        FontFamily="{DynamicResource MahApps.Fonts.Family.Header}"
                        FontSize="16"
                        Text="{Binding Label}" />
                    <ScrollViewer
                        Grid.Row="1"
                        Focusable="False"
                        HorizontalScrollBarVisibility="Disabled"
                        VerticalScrollBarVisibility="Auto">
                        <ContentControl Content="{Binding Tag}" Focusable="False" />
                    </ScrollViewer>
                </Grid>
            </DataTemplate>
        </mah:HamburgerMenu.ContentTemplate>
    </mah:HamburgerMenu>

我的注销视图现在很简单,但看起来像这样:

LogoutView.xaml

<StackPanel Orientation="Vertical">
        <TextBlock Text="Do you want to log out?" />
        <Button Command="{Binding LogoutCommand}" Content="Log out" />
    </StackPanel>

和视图模型:

LogoutViewModel.cs

    public class LogoutViewModel : ViewModelBase
        {
            public ICommand LogoutCommand { get; }
    
            public LogoutViewModel(IAuthenticationService authenticationService, INavigationService loginNavigationService)
            {
                LogoutCommand = new LogoutCommand(this, authenticationService, loginNavigationService);
            }
        }

AddViewModelsHostBuilderExtensions.cs

 public static IHostBuilder AddViewModels(this IHostBuilder host)
        {
            host.ConfigureServices(services =>
            {
                services.AddTransient<MainViewModel>();

                services.AddSingleton<CreateViewModel<LoginViewModel>>(services => () => CreateLoginViewModel(services));
                services.AddSingleton<CreateViewModel<HomeViewModel>>(services => () => CreateHomeViewModel(services));
                services.AddSingleton<CreateViewModel<LogoutViewModel>>(services => () => CreateLogoutViewModel(services));
            });

            return host;
        }

        private static LoginViewModel CreateLoginViewModel(IServiceProvider services)
        {
            return new LoginViewModel(
                services.GetRequiredService<NavigationService<HomeViewModel>>());
        }

        private static HomeViewModel CreateHomeViewModel(IServiceProvider services)
        {
            return new HomeViewModel();
        }

        private static LogoutViewModel CreateLogoutViewModel(IServiceProvider services)
        {
            return new LogoutViewModel(
                services.GetRequiredService<NavigationService<LoginViewModel>>());
        }

当我单击 'Log out' 项时,我的视图正确显示,但是,每当我单击 'Log out' 按钮时,命令都不会触发。是因为视图没有视图模型的数据上下文(根据其他问题)吗?还是我在其他地方犯了错误?在这种情况下如何设置视图的数据上下文?

幸运的是,@mm8 的评论帮助我开始朝着正确的方向努力,然后我可以自己解决剩下的问题,所以我 post 我的解决方案在这里,希望它可以帮助其他人未来。

基本上,我所做的是在我的 HomeViewModel 中创建了一个视图模型 属性。我猜你可以在这个 属性 中创建它,但我在我的主机构建器中注册了视图模型,所以我从我的构造函数中获取它。

HomeViewModel.cs

public class HomeViewModel : ViewModelBase
    {
        public LogoutViewModel LogoutViewModel { get; }

        public HomeViewModel(LogoutViewModel logoutViewModel)
        {
            LogoutViewModel = logoutViewModel;
        }
    }

在此之后,我在我的汉堡菜单用户控件中创建了一个依赖项 属性,以便我可以将视图模型从 HomeViewModel 传递到汉堡菜单用户控件。

HomeView.xaml

..
<controls:HamburgerMenuRipple LogoutViewDataContext="{Binding LogoutViewModel}" />
..

HamburgerMenuRipple.xaml.cs

public static readonly DependencyProperty LogoutViewDataContextProperty =
            DependencyProperty.Register(nameof(LogoutViewDataContext), typeof(LogoutViewModel), typeof(HamburgerMenuRipple), new PropertyMetadata(null));

        public LogoutViewModel LogoutViewDataContext
        {
            get { return (LogoutViewModel)GetValue(LogoutViewDataContextProperty); }
            set { SetValue(LogoutViewDataContextProperty, value); }
        }

然后我在 OnItemInvoked 命令的帮助下设置标记的用户控件的数据上下文。

private void HamburgerMenuControl_OnItemInvoked(object sender, HamburgerMenuItemInvokedEventArgs e)
        {
            HamburgerMenuControl.Content = e.InvokedItem;
            var iconItemTag = (e.InvokedItem as HamburgerMenuIconItem).Tag;

            if (iconItemTag != null)
            {
                switch(iconItemTag)
                {
                    case LogoutView:
                        ErrorMessage = string.Empty;
                        (iconItemTag as UserControl).DataContext = LogoutViewDataContext;
                        break;

                    default:
                        ErrorMessage = "No data context has been bound to this view.";
                        break;
                }
            }
        }

HamburgerMenuRipple.xaml

<mah:HamburgerMenu
            x:Name="HamburgerMenuControl"
            DisplayMode="CompactInline"
            IsPaneOpen="True"
            ItemInvoked="HamburgerMenuControl_OnItemInvoked"

..

<mah:HamburgerMenu.OptionsItemsSource>
                <mah:HamburgerMenuItemCollection>
                    <mah:HamburgerMenuSeparatorItem />

                    <mah:HamburgerMenuIconItem
                        x:Name="LogoutIconItem"
                        Icon="{iconPacks:Material Kind=Logout}"
                        Label="Logout">
                        <mah:HamburgerMenuIconItem.Tag>
                            <views:LogoutView HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </mah:HamburgerMenuIconItem.Tag>
                    </mah:HamburgerMenuIconItem>

                </mah:HamburgerMenuItemCollection>
            </mah:HamburgerMenu.OptionsItemsSource>

..