使用 RelativeSource 绑定到 ViewModel 不起作用
Binding to ViewModel not working using RelativeSource
我有一个应用程序有一个导航菜单,它使用命令从一页转到另一页。
导航菜单已在 Xaml 中创建,我将其存储在 Navigation.xaml 中,见下文
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Cirdan.Wpf.Navigation"
xmlns:infrastructure="clr-namespace:Cirdan.Wpf.Infrastructure">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Cirdan.Wpf;component/Resources/Styles/Stylesheet.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="Navigation" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0" Columns="1">
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding ViewerPageCmd}" >Viewer Screen</Button>
<Button DataContext="{Binding NavigationViewModel, Source={x:Static infrastructure:MainWindow.LocatorX}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding}" >Acquisition Screen</Button>
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
</UniformGrid>
<ToggleButton Grid.Row="1" Style="{StaticResource ToggleBtnToolStyle}" x:Name="Menu" IsChecked="true" Background="Transparent" BorderThickness="0" >
<StackPanel Orientation="Horizontal">
<ContentPresenter Margin="5" Height="50" Content="{StaticResource MenuIcon}"></ContentPresenter>
<Viewbox>
<TextBlock Margin="5" Style="{StaticResource TxtToolStyle}">Menu</TextBlock>
</Viewbox>
</StackPanel>
</ToggleButton>
</Grid>
</DataTemplate>
我尝试将这些按钮命令绑定到的 ViewModel 称为 NavigationViewModelBase.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
namespace Cirdan.Wpf.Navigation
{
public abstract class NavigationViewModelBase : ViewModelBase
{
private List<DicomMetadataModel> _dicomMetadata;
//Navigation Cmd
public ICommand AcquisitionPageCmd { get; private set; }
public ICommand ManualEntryWindowCmd { get; private set; }
public ICommand SessionWindowCmd { get; private set; }
public ICommand SettingsWindowCmd { get; private set; }
public ICommand StudyInfoPageCommandCmd { get; private set; }
public ICommand ViewerPageCmd { get; private set; }
public ICommand WorklistPageCmd { get; private set; }
protected NavigationViewModelBase()
{
AcquisitionPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.AcquisitionScreen)));
ManualEntryWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.ManualEntry, DicomMetadata)));
SessionWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Session)));
SettingsWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Settings)));
ViewerPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Viewer)));
WorklistPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Worklist)));
}
}
}
然后我在每个页面上使用以下代码添加导航
<ContentControl Grid.Column="2" Grid.Row="2" ContentTemplate="{StaticResource Navigation }" />
目前我没有收到任何错误,当我在上面的一个按钮中设置 DataContext 时,当我去绑定我的命令时,我可以看到那个 Viewmodel 的所有属性,所以那个位是工作正常,但是当我 运行 程序并单击这些按钮时没有任何反应。
感谢您的帮助
RelativeSource FindAncestor 在 VisualTree 中寻找祖先。
VisualTree 可以看起来像:
Window
Grid
TextBlock
TextBox
Button
ViewModels不在可视三
例如,Button 的祖先是 Grid,在这种情况下 Window。
那怎么办呢?
好吧,在 ResourceDictionary 中使用 ContentTemplate 创建自定义 UserControl
而不是 ContentControl。
在用户控件中,将 DataContext 设置为继承自 NavigationViewModelBase 的 class。
那么绑定就很简单了:
<UniformGrid Grid.Row="0" Columns="1">
<Button Command="{Binding ViewerPageCmd}">Viewer Screen</Button>
<Button Command="{Binding AcquisitionPageCmd}">Acquisition Screen</Button>
<Button Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
</UniformGrid>
Ancestor Binding 仅适用于 visualtree/view 中的元素或您页面中的简单元素。 viewModel 不在页面的任何位置作为控件。因此,代替 viewModel 给出具有 NavigationViewModelBase 的视图类型作为 datacontext.write 绑定如下(如果您的 view/control 是 NavigationView 绑定将是)。并在路径中写入 vm:NavigationViewModelBase 的 属性
class :
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:NavigationView}}, path= PropertyYouWantToBind}"
只需确保如果您没有为层次结构中的任何其他控件定义 DataContext,Button 将获得与视图相同的 DataContext,那么您只需绑定命令。你已经做对了。
如果您没有将 DataContext 提供给视图中的任何控件,那么只需将其提供给您 contentControl 或 Grid。就像
<Grid>
<Grid.DataContext>
<vm:NavigationViewModelBase />
</Grid.DataContext>
<Grid.RowDef.......
然后对命令使用简单的绑定。
我有一个应用程序有一个导航菜单,它使用命令从一页转到另一页。
导航菜单已在 Xaml 中创建,我将其存储在 Navigation.xaml 中,见下文
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Cirdan.Wpf.Navigation"
xmlns:infrastructure="clr-namespace:Cirdan.Wpf.Infrastructure">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Cirdan.Wpf;component/Resources/Styles/Stylesheet.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="Navigation" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0" Columns="1">
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding ViewerPageCmd}" >Viewer Screen</Button>
<Button DataContext="{Binding NavigationViewModel, Source={x:Static infrastructure:MainWindow.LocatorX}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding}" >Acquisition Screen</Button>
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
</UniformGrid>
<ToggleButton Grid.Row="1" Style="{StaticResource ToggleBtnToolStyle}" x:Name="Menu" IsChecked="true" Background="Transparent" BorderThickness="0" >
<StackPanel Orientation="Horizontal">
<ContentPresenter Margin="5" Height="50" Content="{StaticResource MenuIcon}"></ContentPresenter>
<Viewbox>
<TextBlock Margin="5" Style="{StaticResource TxtToolStyle}">Menu</TextBlock>
</Viewbox>
</StackPanel>
</ToggleButton>
</Grid>
</DataTemplate>
我尝试将这些按钮命令绑定到的 ViewModel 称为 NavigationViewModelBase.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
namespace Cirdan.Wpf.Navigation
{
public abstract class NavigationViewModelBase : ViewModelBase
{
private List<DicomMetadataModel> _dicomMetadata;
//Navigation Cmd
public ICommand AcquisitionPageCmd { get; private set; }
public ICommand ManualEntryWindowCmd { get; private set; }
public ICommand SessionWindowCmd { get; private set; }
public ICommand SettingsWindowCmd { get; private set; }
public ICommand StudyInfoPageCommandCmd { get; private set; }
public ICommand ViewerPageCmd { get; private set; }
public ICommand WorklistPageCmd { get; private set; }
protected NavigationViewModelBase()
{
AcquisitionPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.AcquisitionScreen)));
ManualEntryWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.ManualEntry, DicomMetadata)));
SessionWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Session)));
SettingsWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Settings)));
ViewerPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Viewer)));
WorklistPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Worklist)));
}
}
}
然后我在每个页面上使用以下代码添加导航
<ContentControl Grid.Column="2" Grid.Row="2" ContentTemplate="{StaticResource Navigation }" />
目前我没有收到任何错误,当我在上面的一个按钮中设置 DataContext 时,当我去绑定我的命令时,我可以看到那个 Viewmodel 的所有属性,所以那个位是工作正常,但是当我 运行 程序并单击这些按钮时没有任何反应。
感谢您的帮助
RelativeSource FindAncestor 在 VisualTree 中寻找祖先。
VisualTree 可以看起来像:
Window
Grid
TextBlock
TextBox
Button
ViewModels不在可视三
例如,Button 的祖先是 Grid,在这种情况下 Window。
那怎么办呢?
好吧,在 ResourceDictionary 中使用 ContentTemplate 创建自定义 UserControl
而不是 ContentControl。
在用户控件中,将 DataContext 设置为继承自 NavigationViewModelBase 的 class。
那么绑定就很简单了:
<UniformGrid Grid.Row="0" Columns="1">
<Button Command="{Binding ViewerPageCmd}">Viewer Screen</Button>
<Button Command="{Binding AcquisitionPageCmd}">Acquisition Screen</Button>
<Button Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
</UniformGrid>
Ancestor Binding 仅适用于 visualtree/view 中的元素或您页面中的简单元素。 viewModel 不在页面的任何位置作为控件。因此,代替 viewModel 给出具有 NavigationViewModelBase 的视图类型作为 datacontext.write 绑定如下(如果您的 view/control 是 NavigationView 绑定将是)。并在路径中写入 vm:NavigationViewModelBase 的 属性 class :
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:NavigationView}}, path= PropertyYouWantToBind}"
只需确保如果您没有为层次结构中的任何其他控件定义 DataContext,Button 将获得与视图相同的 DataContext,那么您只需绑定命令。你已经做对了。
如果您没有将 DataContext 提供给视图中的任何控件,那么只需将其提供给您 contentControl 或 Grid。就像
<Grid>
<Grid.DataContext>
<vm:NavigationViewModelBase />
</Grid.DataContext>
<Grid.RowDef.......
然后对命令使用简单的绑定。