C# 将数据从子级聚合到父级以进行绑定
这篇文章经过完整的编辑,提供了一个更坚实的例子和简化的提问 我希望将子项更改的结果反映在父项中,特别是字段C# 将数据从子级聚合到父级以进行绑定,c#,vb.net,mvvm,windows-phone-8,binding,C#,Vb.net,Mvvm,Windows Phone 8,Binding,这篇文章经过完整的编辑,提供了一个更坚实的例子和简化的提问 我希望将子项更改的结果反映在父项中,特别是字段NumberOfChildrenWithDegrees。如果选中绑定为HasUniversityGreee和HashighSchoolGreee的两个框,则子视图中的HasTwoDegrees将更新。我想在父VM中反映具有HasTwoDegrees的孩子的数量 我原以为这很简单,但事实证明它不起作用-选中两个框不会改变父视图(在区域)中的任何内容。如何实时更改此数字? 我为Windows P
NumberOfChildrenWithDegrees
。如果选中绑定为HasUniversityGreee
和HashighSchoolGreee
的两个框,则子视图中的HasTwoDegrees
将更新。我想在父VM中反映具有HasTwoDegrees
的孩子的数量
我原以为这很简单,但事实证明它不起作用-选中两个框不会改变父视图(在
区域)中的任何内容。如何实时更改此数字?
我为Windows Phone创建了一个小示例,它可以直接插入一个名为TestPage的页面。只需将XAML放入内容网格,将代码放入代码隐藏(VB)中即可
XAML:
<Grid x:Name="ContentPanel" Background="{StaticResource PhoneChromeBrush}" Grid.Row="1">
<ListBox x:Name="parentLB" Margin="12,12,12,12" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="0,12,0,0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Parent Name: "/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Number of Children with Degrees: "/>
<TextBlock Text="{Binding NumberOfChildrenWithDegrees,Mode=TwoWay,UpdateSourceTrigger=Explicit}"/>
</StackPanel>
<ListBox Margin="12,0,0,0" x:Name="group2" ItemsSource="{Binding Children,Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Child Name: "/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
<CheckBox Content="Has High School Degree:" IsChecked="{Binding HasHighSchoolDegree,Mode=TwoWay}"/>
<CheckBox Content="Has University Degree: " IsChecked="{Binding HasUniversityDegree,Mode=TwoWay}"/>
<CheckBox Content="Has Two Degrees? " IsEnabled="False" IsChecked="{Binding HasTwoDegrees}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
由于您的子元素是引发
PropertyChangeEvent
的元素,并且计数绑定到父属性,因此您需要订阅父元素中每个子元素的PropertyChangedEvent
。然后您需要在父元素中引发自己的属性更改事件,并将PropertyName绑定到UI元素
当引发PropertyChangedEvent
时,您执行的操作只是调用OnPropertyChanged()
方法,并传入NumberOfChildrenWithDegrees
字符串。如果您有更复杂的对象,您可能希望在ChildOnPropertyChanged()中执行case语句
方法基于事件参数中的PropertyName。我在代码中留下了一个注释示例。下面是完整的C代码,我不需要更改XAML。请注意,我还更改了列表的创建和添加方式,以便在将每个子级的属性更改事件添加到列表中时订阅它们
using System.Linq;
using Microsoft.Phone.Controls;
using System.Collections.Generic;
using System.ComponentModel;
namespace PhoneApp1
{
public partial class TestPage : PhoneApplicationPage
{
public TestPage()
{
InitializeComponent();
this.parentLB.ItemsSource = Grandparent1().ParentGroups;
}
public GrandParent Grandparent1()
{
List<Parent> ParentGroups = new List<Parent>();
ParentGroups.Add(Parent1());
ParentGroups.Add(Parent2());
GrandParent gp = new GrandParent();
gp.ParentGroups = ParentGroups;
return gp;
}
public Parent Parent2()
{
Parent p = new Parent { Name = "Tom" };
Child c1 = new Child { Name = "Tammy" };
Child c2 = new Child { Name = "Timmy" };
p.AddChild(c1);
p.AddChild(c2);
return p;
}
public Parent Parent1()
{
Parent p = new Parent { Name = "Carol" };
Child c1 = new Child { Name = "Carl" };
c1.HasHighSchoolDegree = true;
c1.HasUniversityDegree = true;
Child c2 = new Child { Name = "Karla" };
p.AddChild(c1);
p.AddChild(c2);
return p;
}
}
public class GrandParent : BindableBase
{
public List<Parent> ParentGroups { get; set; }
}
public class Parent : BindableBase
{
public string Name { get; set; }
private List<Child> _children;
public List<Child> Children
{
get { return this._children; }
set { _children = value; }
}
public Parent()
{
_children = new List<Child>();
}
public void AddChild(Child child)
{
child.PropertyChanged += ChildOnPropertyChanged;
_children.Add(child);
}
private void ChildOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
//if(propertyChangedEventArgs.PropertyName == "HasUniversityDegree");
OnPropertyChanged("NumberOfChildrenWithDegrees");
}
private int _numberOfChildrenWithDegrees;
public int NumberOfChildrenWithDegrees
{
get { return Children.Where(f => f.HasTwoDegrees).Count(); }
set { _numberOfChildrenWithDegrees = value; }
}
}
public class Child : BindableBase
{
public string Name { get; set; }
public bool HasTwoDegrees
{
get { return HasHighSchoolDegree && HasUniversityDegree; }
}
private bool _hasUniversityDegree;
public bool HasUniversityDegree
{
get { return this._hasUniversityDegree; }
set
{
_hasUniversityDegree = value;
OnPropertyChanged("HasTwoDegrees");
}
}
private bool _hasHighSchoolDegree;
public bool HasHighSchoolDegree
{
get { return this._hasHighSchoolDegree; }
set
{
_hasHighSchoolDegree = value;
OnPropertyChanged("HasTwoDegrees");
}
}
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (object.Equals(storage, value))
return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
使用System.Linq;
使用Microsoft.Phone.Controls;
使用System.Collections.Generic;
使用系统组件模型;
命名空间PhoneApp1
{
公共部分类测试页:PhoneApplicationPage
{
公共测试页()
{
初始化组件();
this.parentLB.ItemsSource=grandrent1().ParentGroups;
}
公共祖父母祖父母1()
{
List ParentGroups=new List();
ParentGroups.Add(Parent1());
ParentGroups.Add(Parent2());
祖父母gp=新祖父母();
gp.ParentGroups=父组;
返回总成;
}
公共家长2()
{
父p=新父{Name=“Tom”};
子c1=新子{Name=“Tammy”};
子c2=新子{Name=“Timmy”};
p、 AddChild(c1);
p、 AddChild(c2);
返回p;
}
公共家长1()
{
父p=新父{Name=“Carol”};
子c1=新子{Name=“Carl”};
c1.HasHighSchoolDegree=true;
c1.HasUniversityGreee=true;
子c2=新子{Name=“Karla”};
p、 AddChild(c1);
p、 AddChild(c2);
返回p;
}
}
公共类祖父母:BindableBase
{
公共列表父组{get;set;}
}
公共类父级:BindableBase
{
公共字符串名称{get;set;}
私人名单——儿童;
公开儿童名单
{
获取{返回此。_children;}
设置{u children=value;}
}
公共家长()
{
_children=新列表();
}
公共无效添加子对象(子对象)
{
child.PropertyChanged+=ChildOnPropertyChanged;
_添加(child);
}
私有void ChildOnPropertyChanged(对象发送方,PropertyChangedEventArgs PropertyChangedEventArgs)
{
//如果(propertyChangedEventArgs.PropertyName==“HasUniversityGreee”);
关于财产变更(“拥有学位的儿童人数”);
}
拥有学位的儿童的私人国际人数;
拥有学位的儿童的公共int数量
{
获取{return Children.Where(f=>f.HasTwoDegrees.Count();}
设置{u numberOfChildrenWithDegrees=value;}
}
}
公共类子级:BindableBase
{
公共字符串名称{get;set;}
公立学校有两个学位
{
获取{return hashighschooldgree&&hasuniversitygree;}
}
私立学校拥有大学学位;
公立学校有大学学位
{
获取{返回此项。_hasuniversitygree;}
设置
{
_HasUniversityGreee=价值;
不动产变更(“有两个学位”);
}
}
私立学校;
公立学校
{
获取{返回此。\u hasHighSchoolDegree;}
设置
{
_hasHighSchoolDegree=值;
不动产变更(“有两个学位”);
}
}
}
公共抽象类BindableBase:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
受保护的bool SetProperty(ref T storage,T value,string propertyName=null)
{
if(object.Equals(存储,值))
返回false;
储存=价值;
此.OnPropertyChanged(propertyName);
返回true;
}
受保护的OnPropertyChanged无效(字符串propertyName=null)
{
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
}
}
我希望这听起来不太像广告。有了BCL的工具,你只有机会完全按照@jmshapland所说的去做。对于这个简单的聚合,这可能不会太糟糕,但是如果聚合变得更复杂,代码会很快变得更复杂。然而,有一些工具可以支持你,特别是我的own工具:(开源)
这个工具基本上允许您创建一个可观察的表达式并监听其更新
public class Parent
{
private INotifyValue<bool> _allTwoDegrees;
public Parent()
{
_allTwoDegrees = Observable.Expression(() => Children.WithUpdates().All(child => child.HasTwoDegrees));
_allTwoDegrees.ValueChanged += (o,e) => OnPropertyChanged("AllTwoDegrees");
}
public bool AllTwoDegrees
{
get
{
return _allTwoDegrees.Value;
}
}
...
}
公共类父类
{
私有INotifyValue
public class Parent
{
private INotifyValue<bool> _allTwoDegrees;
public Parent()
{
_allTwoDegrees = Observable.Expression(() => Children.WithUpdates().All(child => child.HasTwoDegrees));
_allTwoDegrees.ValueChanged += (o,e) => OnPropertyChanged("AllTwoDegrees");
}
public bool AllTwoDegrees
{
get
{
return _allTwoDegrees.Value;
}
}
...
}
public abstract class Bindable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String property = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(property);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string property = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public class Child : Bindable
{
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
public bool HasTwoDegrees { get { return HasHighSchoolDegree && HasUniversityDegree; } }
private bool hasUniversityDegree;
public bool HasUniversityDegree
{
get { return this.hasUniversityDegree; }
set
{
SetProperty(ref hasUniversityDegree, value);
OnPropertyChanged("HasTwoDegrees");
}
}
private bool hasHighSchoolDegree;
public bool HasHighSchoolDegree
{
get { return this.hasHighSchoolDegree; }
set
{
SetProperty(ref hasHighSchoolDegree, value);
OnPropertyChanged("HasTwoDegrees");
}
}
}
public class Parent : Bindable
{
private string[] bindingNames;
private string[] allowedProperties;
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
private ObservableCollection<Child> children = new ObservableCollection<Child>();
public ObservableCollection<Child> Children
{
get { return this.children; }
set { children = value; }
}
public Parent()
{
this.children.CollectionChanged += children_CollectionChanged;
bindingNames = new string[] { "NumberOfChildrenWithDegrees" };
allowedProperties = new string[] { "HasUniversityDegree", "HasHighSchoolDegree" };
}
private void children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (Object item in e.NewItems)
if (item is INotifyPropertyChanged)
(item as INotifyPropertyChanged).PropertyChanged += item_PropertyChanged;
if (e.OldItems != null)
foreach (Object item in e.OldItems)
(item as INotifyPropertyChanged).PropertyChanged -= item_PropertyChanged;
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (bindingNames != null)
foreach (string item in bindingNames)
if (allowedProperties.Contains(e.PropertyName))
OnPropertyChanged(item);
}
public int NumberOfChildrenWithDegrees
{
get { return Children.Where(f => f.HasTwoDegrees).Count(); }
}
}
public partial class MainPage : PhoneApplicationPage
{
ObservableCollection<Parent> parentGroups = new ObservableCollection<Parent>();
public ObservableCollection<Parent> ParentGroups
{
get { return parentGroups; }
set { parentGroups = value; }
}
public MainPage()
{
InitializeComponent();
this.parentLB.DataContext = this;
FillParents();
}
public void FillParents()
{
Parent p = new Parent { Name = "Tom" };
Child c1 = new Child { Name = "Tammy" };
Child c2 = new Child { Name = "Timmy" };
p.Children.Add(c1);
p.Children.Add(c2);
ParentGroups.Add(p);
p = new Parent { Name = "Carol" };
c1 = new Child { Name = "Carl" };
c1.HasHighSchoolDegree = true;
c1.HasUniversityDegree = true;
c2 = new Child { Name = "Karla" };
p.Children.Add(c1);
p.Children.Add(c2);
ParentGroups.Add(p);
}
}