c# - 将数据从子项聚合到父项以进行绑定(bind)

标签 c# vb.net mvvm windows-phone-8 binding

这篇文章经过完全编辑,以提供更可靠的示例和简化的问题

我希望子项更改的结果反射(reflect)在父项中,尤其是字段 NumberOfChildrenWithDegrees .如果您选中两个绑定(bind)为 HasUniversityDegree 的框和 HasHighSchoolDegree然后 HasTwoDegrees在 subview 更新中。我想要的是 child 的数量 HasTwoDegrees反射(reflect)在父 VM 中。

我认为这很简单,但事实证明它不起作用 - 选中两个框在父 View (<TextBlock Text="{Binding NumberOfChildrenWithDegrees,Mode=TwoWay,UpdateSourceTrigger=Explicit}"/> 区域)中没有任何改变。我如何获得这个数字实时更改?

我为 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>

代码隐藏:

Imports System.Collections.ObjectModel
Imports System.ComponentModel

Partial Public Class TestPage
    Inherits PhoneApplicationPage

    Public Sub New()
        InitializeComponent()
        parentLB.ItemsSource = Grandparent1.ParentGroups
    End Sub
    Public Function Grandparent1() As GrandParent
        Dim ParentGroups As New List(Of Parent)
        ParentGroups.Add(Parent1)
        ParentGroups.Add(Parent2)
        Dim gp As New GrandParent
        gp.ParentGroups = ParentGroups
        Return gp
    End Function
    Public Function Parent2() As Parent
        Dim p As New Parent With {.Name = "Tom"}
        Dim c1 As New Child With {.Name = "Tammy"}
        Dim c2 As New Child With {.Name = "Timmy"}
        Dim children As New List(Of Child)
        children.Add(c1)
        children.Add(c2)
        p.Children = children
        Return p
    End Function

    Public Function Parent1() As Parent
        Dim p As New Parent With {.Name = "Carol"}
        Dim c1 As New Child With {.Name = "Carl"}
        c1.HasHighSchoolDegree = True
        c1.HasUniversityDegree = True
        Dim c2 As New Child With {.Name = "Karla"}
        Dim children As New List(Of Child)
        children.Add(c1)
        children.Add(c2)
        p.Children = children
        Return p
    End Function
End Class

Public Class GrandParent
    Inherits BindableBase
    Public Property ParentGroups As List(Of Parent)
End Class
Public Class Parent
    Inherits BindableBase
    Public Property Name As String

    Private _children As List(Of Child)
    Public Property Children As List(Of Child)
        Get
            Return Me._children
        End Get
        Set(value As List(Of Child))
            Me.SetProperty(Me._children, value)
        End Set
    End Property

    Private _numberOfChildrenWithDegrees As Integer
    Public Property NumberOfChildrenWithDegrees As Integer
        Get
            Return Children.Where(Function(f) f.HasTwoDegrees).Count
        End Get
        Set(value As Integer)
            Me.SetProperty(Me._numberOfChildrenWithDegrees, value)
        End Set
    End Property
End Class
Public Class Child
    Inherits BindableBase
    Public Property Name As String

    Public ReadOnly Property HasTwoDegrees As Boolean
        Get
            Return HasHighSchoolDegree AndAlso HasUniversityDegree
        End Get
    End Property

    Private _hasUniversityDegree As Boolean
    Public Property HasUniversityDegree As Boolean
        Get
            Return Me._hasUniversityDegree
        End Get
        Set(value As Boolean)
            Me.SetProperty(Me._hasUniversityDegree, value)
            OnPropertyChanged("HasTwoDegrees")
        End Set
    End Property

    Private _hasHighSchoolDegree As Boolean
    Public Property HasHighSchoolDegree As Boolean
        Get
            Return Me._hasHighSchoolDegree
        End Get
        Set(value As Boolean)
            Me.SetProperty(Me._hasHighSchoolDegree, value)
            OnPropertyChanged("HasTwoDegrees")
        End Set
    End Property
End Class
Public MustInherit Class BindableBase
    Implements INotifyPropertyChanged
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    Protected Function SetProperty(Of T)(ByRef storage As T, value As T,
                                    Optional propertyName As String = Nothing) As Boolean

        If Object.Equals(storage, value) Then Return False

        storage = value
        Me.OnPropertyChanged(propertyName)
        Return True
    End Function
    Protected Sub OnPropertyChanged(Optional propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

End Class

最佳答案

由于您的子元素是引发 PropertyChangeEvent 的元素,并且计数绑定(bind)到父属性,因此您需要在父元素中订阅每个子元素的 PropertyChangedEvent。然后,您需要使用绑定(bind)到 UI 元素的 PropertyName 在父级中引发您自己的属性更改事件。

在引发PropertyChangedEvent 时执行的操作只是调用OnPropertyChanged() 方法并传入NumberOfChildrenWithDegrees 字符串。如果您有一个更复杂的对象,您可能希望根据事件参数中的 PropertyName 在 ChildOnPropertyChanged() 方法中执行 case 语句。我在代码中留下了一个注释示例。下面是完整的 C# 代码,我不需要更改 XAML。请注意,我还更改了列表的创建和添加方式,以便在将每个 child 添加到列表时订阅他们的属性更改事件。

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));
        }
    }

}
}

关于c# - 将数据从子项聚合到父项以进行绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21581936/

相关文章:

C# SqlDataReader 执行统计和信息

c# - Entity Framework 的“使用”关键字与类字段

c# - Telerik RadComboBox 未构建

wpf - 如何为 VSE 2013 桌面安装 MVVMLight 片段

wpf - MVVM应用程序无法正确还原状态

c# - 将 #if DEBUG 与 Main 参数一起使用

c# - 绑定(bind)到项目模板中的字典

c# - 将带有接口(interface)的继承从 C# 转换为 VB.NET

mysql - Foreach 循环仅更新数据库的一行

wpf - Caliburn micro 和 tabcontrol