Nuc*_*mer 7 wpf xaml binding datagrid mvvm
当最外层控件的DataContext发生更改时,我遇到了使用ItemSources的嵌套控件的问题.内部控件似乎更新以反映新的DataContext,但它有一些"Ghost"绑定仍然绑定到旧的DataContext.
我怀疑拥有DataTemplates的嵌套控件会阻止内部控件的绑定在外部控件的DataContext更改时更新.我在某处读到只有绑定才响应从PATH中明确定义的对象引发的PropertyChanged事件.
我的问题是:如何使用ItemsSources从nexted控件中完全定义绑定PATH?就我而言:
<DataGrid name="OuterGrid" ItemsSource={Binding SelectedSchool.Classes}">
<ItemsControl ItemsSource={Binding Students}">
<ComboBox SelectedItem={Binding Grade}" />
</ItemsControl>
</DataGrid>
Run Code Online (Sandbox Code Playgroud)
我想完全指定内部ComboBox的SeletedItem PATH,类似于以下内容,但是我需要将它绑定到集合中的特定项(而不仅仅是索引0处的那个).
<ComboBox SelectedItem="{Binding ElementName=OuterGrid,
Path=DataContext.SelectedSchool.Classes[0].Students[0].Grade}" />
Run Code Online (Sandbox Code Playgroud)
我在下面有一个更详细的问题示例,我无法发布实际代码或描述我正在使用的ACTUAL对象(安全原因),所以我试图用最简单的方式来理解它.
我有一个相当复杂的Biz对象,其中包含其他对象的集合.集合中的项目也包含集合.
每个类(包括我的ViewModel)都实现了INotifyPropertyChanged,每个集合都是一个ObservableCollection.
我的ViewModel具有以下特性:
这里要注意的重要一点是,不同的学校可能有不同的可能成绩(即一个可能有A +,A和A-而另一个只有A).
我有一个Datagrid绑定到我的ViewModel的AllSchools集合和ViewModel的SelectedSchool属性.当用户双击一行时,事件处理程序通过更改ViewModel的IsEditing属性(编辑面板的Visibily绑定到IsEditing属性)打开所选学校的"编辑面板".在编辑面板中,我有一个Datagrid(绑定到所选学校的Classes集合),在Datagrid内部我有一个带有ItemsControl的TemplatedColumn(绑定到当前Class的学生的集合).每个学生都有一个ComboBox,用于学生在课堂上的成绩.ComboBox的ItemsSource是ViewModel的PossibleGrades集合.
问题在于,当SelectedSchool发生变化时,之前在SelectedSchool中的任何学生,其字母成绩不适用于新选择的学校,突然将其成绩设置为null(因为ComboBox的ItemsSource不再具有成绩) .
在视觉上,一切似乎都很好.编辑面板正确显示所选学校的属性,并在SelectedSchool属性更改时更新.但是如果我重新打开第一所学校的编辑面板,那么组合框中没有一个选择了值,因为当我选择第二所学校时它们都被设置为null.
它就像旧的ComboBoxes仍然有他们的Bindings连接,即使他们不再出现在屏幕上.但是,如果只影响以前的SelectedSchool(不是之前的那个).
感谢@OmegaMan 对绑定幕后发生的事情的描述。
我基本上通过创建一个级联 PropertyChanged 事件的接口来解决这个问题。
public interface ICascadePropertyChanged: INotifyPropertyChanged
{
void CascadePropertyChanged();
}
Run Code Online (Sandbox Code Playgroud)
然后,我修改了 ModelBase 和 CollectionBase 类,通过使用 Refection 在子属性上递归调用 CascadePropertyChanged() 来实现所述接口。
public class ModelCollection<M> : ObservableCollection<M>,
ICascadePropertyChanged where M: ModelBase
{
...
public void CascadePropertyChanged()
{
foreach (M m in this)
{
if (m != null)
{
m.CascadePropertyChanged();
}
}
}
}
public abstract class ModelBase: ICascadePropertyChanged
{
...
public void CascadePropertyChanged()
{
var properties = this.GetType().GetProperties()
.Where( p => HasInterface(p.PropertyType, typeof(ICascadePropertyChanged));
// Cascade the call to each sub-property.
foreach (PropertyInfo pi in properties)
{
ICascadePropertyChanged obj = (ICascadePropertyChanged)pi.GetValue(this);
if (obj != null)
{
obj.CascadePropertyChanged();
}
}
RaisePropertyChanged();
}
}
Run Code Online (Sandbox Code Playgroud)
我不得不凭记忆重新输入,所以请原谅打字错误。