CommandParameter 上的 DataContext 与 Command 本身上的 DataContext 不同
DataContext on CommandParameter differs from DataContext on Command itself
这个问题与我之前在这里的问题有关:Predicate won't validate parameter correctly
首先是关于我的模型的一些信息:
基础视图模型:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (Name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
private BaseViewModel _homePage;
public BaseViewModel HomePage
{
get
{
return _homePage;
}
set
{
if (HomePage != value)
{
_homePage = value;
OnPropertyChanged("HomePage");
}
}
}
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
问题:
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding HomePage}"/>
如您在 BaseViewModel 中所见,属性 "Name" 和 "HomePage" 在相同的 class 中定义,因此如果 DataContext 是派生自的 ViewModel,则它们应该是可访问的"BaseViewModel"。
起初我失去了理智,因为似乎没有任何效果 - 输出 window 表示无法检索 "HomePage" 的值 - 除了每次都正确绑定的名称之外。
在我几乎要放弃之后,我将我的视图命名为 "Test" 并尝试沿着 ElementName 重定向 Command属性 绑定 - 它成功了:
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>
但是为什么呢?为什么可以在不分配 ElementName 的情况下绑定的 Name 属性 和需要 ElementName 的 HomePage 之间的 DataContext 不同?
更新 1:
主视图模型:基础视图模型
private RelayCommand _command;
public RelayCommand ChangePageCommand {
get {
return _command ?? (_command = new RelayCommand(p => ChangeViewModel((BaseViewModel)p), x => {
return x is BaseViewModel;
}));
}
}
public void ChangeViewModel(BaseViewModel viewModel) {
CurrentPageViewModel = viewModel;
}
更新二:
<Window.Resources>
<DataTemplate DataType="{x:Type home:HomeViewModel}">
<home:Home/>
</DataTemplate>
<DataTemplate DataType="{x:Type ua:UserAdministrationViewModel}">
<ua:UserAdministration/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPageViewModel}"/>
更新 3:
现在它变得非常奇怪 - 我尝试添加一个 TextBlock 并将其直接绑定到 HomePage.Text - 它有效。
<Button Height="50" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>
<TextBlock Text="{Binding HomePage.Name}"/>
那么,为什么在绑定到 CommandParameter 时不能直接访问主页,而是将 TextBlock 的文本 属性 直接绑定到 HomePage.Name 有效?
更新 4:
输出 Windows 说:
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=姓名;数据项=空;目标元素是 'Button' (Name='');目标 属性 是 'Content'(类型 'Object')
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=DataContext.ChangePageCommand;数据项=空;目标
元素是 'Button' (Name='');目标 属性 是 'Command'(类型 'ICommand')
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=首页;数据项=空;目标元素是 'Button' (Name='');目标 属性 是 'CommandParameter'(类型 'Object')
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=HomePage.Name;数据项=空;目标元素是 'TextBlock' (Name='');目标 属性 是 'Text'(类型 'String')
但除了 CommandParameter 之外的所有内容都已成功绑定。刷新绑定后,CommandParameter(和底层 CanExecute-Method)是否有可能不会重新验证?也许绑定一开始会失败,但除了 CommandParameter 之外的每个绑定都会得到刷新和重新验证。但是我该如何解决这个问题呢?
我很自豪地宣布我已经解决了这个愚蠢的问题:
我假设我用来将我的 CommandBinding 重定向到 Window 的 ViewModel 的 RelativeSource 破坏了所有以下属性(!不是控件)的 DataContext。
不工作:
<Button Grid.Row="0"
Grid.Column="0"
Height="50"
Content="{Binding PreviousPageViewModel.Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding PreviousPageViewModel}" />
工作:
<Button Grid.Row="0"
Grid.Column="0"
Height="50"
Content="{Binding PreviousPageViewModel.Name}"
CommandParameter="{Binding PreviousPageViewModel}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
所以在这种情况下,属性的顺序很重要。
注意:以下所有控件的 DataContext 都是正确的。
这个问题与我之前在这里的问题有关:Predicate won't validate parameter correctly
首先是关于我的模型的一些信息:
基础视图模型:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (Name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
private BaseViewModel _homePage;
public BaseViewModel HomePage
{
get
{
return _homePage;
}
set
{
if (HomePage != value)
{
_homePage = value;
OnPropertyChanged("HomePage");
}
}
}
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
问题:
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding HomePage}"/>
如您在 BaseViewModel 中所见,属性 "Name" 和 "HomePage" 在相同的 class 中定义,因此如果 DataContext 是派生自的 ViewModel,则它们应该是可访问的"BaseViewModel"。 起初我失去了理智,因为似乎没有任何效果 - 输出 window 表示无法检索 "HomePage" 的值 - 除了每次都正确绑定的名称之外。
在我几乎要放弃之后,我将我的视图命名为 "Test" 并尝试沿着 ElementName 重定向 Command属性 绑定 - 它成功了:
<Button Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>
但是为什么呢?为什么可以在不分配 ElementName 的情况下绑定的 Name 属性 和需要 ElementName 的 HomePage 之间的 DataContext 不同?
更新 1:
主视图模型:基础视图模型
private RelayCommand _command;
public RelayCommand ChangePageCommand {
get {
return _command ?? (_command = new RelayCommand(p => ChangeViewModel((BaseViewModel)p), x => {
return x is BaseViewModel;
}));
}
}
public void ChangeViewModel(BaseViewModel viewModel) {
CurrentPageViewModel = viewModel;
}
更新二:
<Window.Resources>
<DataTemplate DataType="{x:Type home:HomeViewModel}">
<home:Home/>
</DataTemplate>
<DataTemplate DataType="{x:Type ua:UserAdministrationViewModel}">
<ua:UserAdministration/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPageViewModel}"/>
更新 3:
现在它变得非常奇怪 - 我尝试添加一个 TextBlock 并将其直接绑定到 HomePage.Text - 它有效。
<Button Height="50" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding DataContext.HomePage, ElementName=Test}"/>
<TextBlock Text="{Binding HomePage.Name}"/>
那么,为什么在绑定到 CommandParameter 时不能直接访问主页,而是将 TextBlock 的文本 属性 直接绑定到 HomePage.Name 有效?
更新 4:
输出 Windows 说:
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=姓名;数据项=空;目标元素是 'Button' (Name='');目标 属性 是 'Content'(类型 'Object')
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=DataContext.ChangePageCommand;数据项=空;目标 元素是 'Button' (Name='');目标 属性 是 'Command'(类型 'ICommand')
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=首页;数据项=空;目标元素是 'Button' (Name='');目标 属性 是 'CommandParameter'(类型 'Object')
System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值;使用默认代替。 BindingExpression:Path=HomePage.Name;数据项=空;目标元素是 'TextBlock' (Name='');目标 属性 是 'Text'(类型 'String')
但除了 CommandParameter 之外的所有内容都已成功绑定。刷新绑定后,CommandParameter(和底层 CanExecute-Method)是否有可能不会重新验证?也许绑定一开始会失败,但除了 CommandParameter 之外的每个绑定都会得到刷新和重新验证。但是我该如何解决这个问题呢?
我很自豪地宣布我已经解决了这个愚蠢的问题:
我假设我用来将我的 CommandBinding 重定向到 Window 的 ViewModel 的 RelativeSource 破坏了所有以下属性(!不是控件)的 DataContext。
不工作:
<Button Grid.Row="0"
Grid.Column="0"
Height="50"
Content="{Binding PreviousPageViewModel.Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding PreviousPageViewModel}" />
工作:
<Button Grid.Row="0"
Grid.Column="0"
Height="50"
Content="{Binding PreviousPageViewModel.Name}"
CommandParameter="{Binding PreviousPageViewModel}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
所以在这种情况下,属性的顺序很重要。
注意:以下所有控件的 DataContext 都是正确的。