设置 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>
..
我遇到过很多关于这个主题的问题,但是,我还没有找到可以解决我的问题的问题。例如,我不能使用 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>
..