C#-当某个子属性发生更改时通知父属性



我有一个类的属性为"DesignParameters",更改后会影响另一个名为"AxialMomentDataLists"的属性。但是,"DesignParameters"由一组其他"子"属性组成,这些属性可以通过UI上的数据网格访问,还可以实现属性更改。如果其中一个子属性发生更改,我还希望"DesignParameters"自动更新,这将反过来要求设置新的"AxialMomentDataLists"。有人对实现这一目标的最佳方法有什么建议吗?

public class Column : ObservableObject
{
private double length;
private DesignParameters desParameters;
public DesignParameters DesParameters
{
get { return desParameters; }
set
{
desParameters = value;
RaisePropertyChanged(nameof(DesParameters));
RaisePropertyChanged(nameof(AxialMomentDataLists));
}
}
public List<AxialMomentDataSet> AxialMomentDataLists
{
get { return CalculateAxialMomentLists(ColumnForce, DesParameters); }
set { }
}
}

节选自儿童课堂:

public class DesignParameters : ObservableObject
{
#region Private variables
private double phiC;
private double phiS;
private int cover;
private int reinforcementYieldStrength;
private int concreteStrength;
private double b;
private double h;
private LiveLoadReductionType liveLoadReduction;
private StirrupType stirrupBar;
private int numberOfXBars;
private int numberOfYBars;
private BarDiameterType longitudinalBarDiameter;
private double longitudinalReinforcementPercentage;
List<Bar> rebar;
#endregion

public int NumberOfXBars
{
get { return numberOfXBars; }
set
{
numberOfXBars = PropertyMethods.SetNumberOfBars("x", value, B, H, Cover, LongitudinalBarDiameter);
RaisePropertyChanged(nameof(NumberOfXBars));
RaisePropertyChanged(nameof(Rebar));
RaisePropertyChanged(nameof(LongitudinalReinforcementPercentage));
RaisePropertyChanged(nameof(AxialResistance));

}
}
}

编辑

我创建了一段更简单的代码,试图完成与我在这里大致相同的事情(https://github.com/dtnaughton/SampleApp)

基本上,我有一个绑定到FamilyMember上属性的UI,属性AgeName可以更改。在Family(父(类上,我有一个属性CumulativeFamilyAge,它返回一个方法,该方法基本上汇总了家庭成员的年龄(在本例中,只有一个家庭成员,因此它的读数应该与FamilyMember.Age相同

当用户在UI上更改FamilyMember.Age时,我希望Family检测到它的一个子属性已经更改,从而相应地更新CumulativeFamilyAge

例如,如果'NumberOfXBars'属性发生更改(它在UI上公开(,那么我需要Column类上的父属性'DesignParameters'来识别它的一个子属性已经更改,因此必须更新。

我会按照我的理解来写。

在Column类中,您有一个包含DesignParameters实例的DesParameters属性
如果此实例的其中一个属性(NumberOfXBars(的值已更改,则需要更新与此实例关联的所有视图。

在这里,您遇到了一个明显的问题,即破坏INotifyPropertyChanged接口的约定。根据此约定,PropertyChanged事件在为属性分配值时不会触发,但仅当该值与上一个值不同时才会触发
因此,如果您只是为相同的值引发此事件,那么绑定将不会对此做出反应。

正如我所认为的,你根本没有正确思考
您不需要更新到实例的绑定
您需要更新其任何属性的任何绑定
为此,接口约定提供创建一个具有空参数的事件。

如果以上是对你的问题的正确理解,那么尝试这个实现:

public int NumberOfXBars
{
get { return numberOfXBars; }
set
{
numberOfXBars = PropertyMethods.SetNumberOfBars("x", value, B, H, Cover, LongitudinalBarDiameter);
RaisePropertyChanged(string.Empty);
}
}

附加问题后的示例实现:

我创建了一段更简单的代码,试图完成与我在这里大致相同的事情(https://github.com/dtnaughton/SampleApp)

使用通用MVVMLight方法改进了Person实体的实现。

using GalaSoft.MvvmLight;
namespace SampleApp.Models
{
public class Person : ObservableObject
{
private string _name;
private int _age;
public string Name { get => _name; set => Set(ref _name, value); }
public int Age { get => _age; set => Set(ref _age, value); }
public Person()
{
Name = "TestName";
Age = 30;
}
}
}

族是从ViewModelBase派生的,以便能够监视事件属性的更改
这样做是为了演示如何观察属性中实体的更改
还添加了第二个Person实体来改进演示示例。

using GalaSoft.MvvmLight;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SampleApp.Models
{
public class Family : ViewModelBase
{
private Person _familyMember;
private int _cumulativeFamilyAge;
private Person _childMember;
public Person FamilyMember { get => _familyMember; set => Set(ref _familyMember, value); }
public Person ChildMember { get => _childMember; set => Set(ref _childMember, value); }
public int CumulativeFamilyAge { get => _cumulativeFamilyAge; private set => Set(ref _cumulativeFamilyAge, value); }
public Family()
{
FamilyMember = new Person() { Name = "Father" };
ChildMember = new Person() { Name = "Son", Age = 7 };
}

public override void RaisePropertyChanged<T>([CallerMemberName] string propertyName = null, T oldValue = default, T newValue = default, bool broadcast = false)
{
base.RaisePropertyChanged(propertyName, oldValue, newValue, broadcast);
if (propertyName == nameof(FamilyMember) || propertyName == nameof(ChildMember))
{
if (oldValue is Person oldPerson)
oldPerson.PropertyChanged -= OnPersonPropertyChanged;
if (newValue is Person newPerson)
newPerson.PropertyChanged += OnPersonPropertyChanged;
CalculateCumulativeFamilyAge();
}
}
private void OnPersonPropertyChanged(object sender, PropertyChangedEventArgs e)
{
CalculateCumulativeFamilyAge();
}
public void CalculateCumulativeFamilyAge()
{
CumulativeFamilyAge = (FamilyMember?.Age ?? 0)
+ (ChildMember?.Age ?? 0);
return;
}
}
}

窗口的数据上下文最好在XAML中定义,这使得XAML设计更加容易
如果您需要在"代码隐藏"中有一个带有ViewModel链接的字段,则应该从XAML中获取该字段。

using SampleApp.ViewModels;
using System.Windows;
namespace SampleApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly MainViewModel viewModel /*= new MainViewModel()*/;
public MainWindow()
{
InitializeComponent();
//DataContext = viewModel;
viewModel = (MainViewModel)DataContext;
}
}
}

在窗口中为Person实体添加了一个DataTemplate,并设置了两个实体的输出。

<Window x:Class="SampleApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="clr-namespace:SampleApp.ViewModels"
xmlns:local="clr-namespace:SampleApp.Models"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewmodels:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:Person}">
<Border Margin="5" Background="LightSkyBlue" Padding="5">
<StackPanel>
<TextBox Text="{Binding Name}" Height="30"/>
<TextBox Text="{Binding Age}" Height="30"/>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<ContentPresenter Content="{Binding FamilyModel.FamilyMember}"/>
<ContentPresenter Content="{Binding FamilyModel.ChildMember}"/>
<TextBlock Text="{Binding FamilyModel.CumulativeFamilyAge}" Height="30"/>
</StackPanel>
</Grid>
</Window>

我不确定我是否正确地接受了你。DesignParameters实现INotifyPropertyChanged,并且每当它的某个属性发生更改时,您想在Column类中为AxialMomentDataLists属性调用PropertyChanged吗?

如果是这样的话,这很容易。只要在为DesParameters属性设置新值时订阅此事件即可。不要忘记取消订阅旧值中的活动。一个null检查可能是必要的(或者你使用带有null引用类型的C#8吗?(

public class Column : ObservableObject
{
private double length;
private DesignParameters desParameters;
public DesignParameters DesParameters
{
get { return desParameters; }
set
{
if(desParameters != null)
{
desParameters.PropertyChanged -= DesParametersChildPropertyChanged;
}
desParameters = value;
desParameters.PropertyChanged += DesParametersChildPropertyChanged;
RaisePropertyChanged(nameof(DesParameters));
RaisePropertyChanged(nameof(AxialMomentDataLists));
}
}
private void DesParametersChildPropertyChanged(object sender, PropertyChangeEventArgs args)
{
RaisePropertyChanged(nameof(DesParameters));
RaisePropertyChanged(nameof(AxialMomentDataLists));
}
public List<AxialMomentDataSet> AxialMomentDataLists
{
get { return CalculateAxialMomentLists(ColumnForce, DesParameters); }
set { }
}
}

最新更新