MVVM中子属性的PropertyChanged

PropertyChanged for child properties in MVVM
2021-05-11
  •  译文(汉语)
  •  原文(英语)

我仍在为项目的结构做些挣扎,遇到一个问题,我担心这表明我仍然对正确的做事方式缺乏很好的了解.

我目前所拥有的是:

该程序显示要运输的信息,这是基于一个包含项目表的整体项目,该项目表包含了包含容器的货运(此示例对此进行了简化)

因此,为了跟踪当前选择的对象(Project,Proforma等),我有一个名为ProjectTree的类,其外观类似于此:

Public class ProjectTree
{
  Public Project SelectedProject { get; set; }
  Public Proforma SelectedProforma { get; set; }
  public Shipment SelectedShipment { get; set; }
}

再次简化一下,因为Project本身包含Proformas列表,所以我在Project上有一个函数,该函数LoadProformas()创建了一个新线程,并将proformasObservableCollection投影到项目本身内(该加载由一组IManagers处理所有数据加载,保存等)

现在我面临的问题是,我的视图模型ProformaListViewModel可以访问,ProjectTree因此它可以LoadProformas()在Project上调用.我遇到了数据绑定的问题,_ProjectTree.SelectedProject.ProformaList因为(afaik)NotifyOfPropertyChange不会知道何时实际加载数据在这种情况下流到viewmodel中.现在,我总是可以使用EventAggregator并在加载数据时触发一个事件(这是我过去所做的事情),但是每当我看到它时,似乎有点...偶然地从中触发未绑定的事件父对象的子对象.

我在这里错过了一些对我的理解很重要的事情吗?还是使用该事件是完成我需要的合理方式?

编辑:

只是为了澄清这一点,我试图这样做的原因是因为每个项目都有一个形式清单,每个形式都有装运清单等.最初,管理人员处理对象清单以及装载和保存,但是我发现让他们完全脱离耦合,尝试根据项目的出货量来处理项目时导致问题的原因(例如)

解决过程1

您的每个viewModel类(在这种情况下,您的Project,Proforma和shipping类都可能同时充当模型和viewModel)都需要实现该INotifyPropertyChanged接口.然后,您的视图应直接绑定到属性SelectedProject.ProformaList.由于列表ObservableCollection已经存在,因此在添加或删除项目时将通知您的视图.然后,您只需要确保在更改ProformaList属性本身时引发PropertyChanged通知.

由于视图中绑定的每个属性都应通知属性更改,因此数据"树"的每个级别(项目,形式,运输)都可以独立更改,并且仍会更新视图.

解决过程2

我认为,您不应Selected...在数据类型/模型类中使用任何属性……这些属性属于您的视图模型类.此外,除非您在工作中实际使用了一个叫做a的对象,否则ProjectTree我不会为您的对象起个名字,而只是坚持使用真实世界的名称.

另外,我建议不要将功能放在您的数据类型类中……我认为(大部分情况下)它们应该只是数据的容器.我将这样构造视图模型(INotifyPropertyChanged在此示例中,我忽略了该接口,但是您需要正确地实现它):

public class ProjectViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Project> Projects { get; set; }
    public Project SelectedProject { get; set; }
    public ObservableCollection<Proforma> Proformas { get; set; }
    public Proforma SelectedProforma { get; set; }
    public ObservableCollection<Shipment> Shipments { get; set; }
    public Shipment SelectedShipment { get; set; }

    public void LoadProjects() {}
    public void LoadProformas() {}             
    public void LoadShipments() {}             
}

接下来,您提到您正在使用EventAggregator而不是INotifyPropertyChanged接口...在我看来,您的生活变得比原来需要的更加困难,因为INotifyPropertyChanged可以将接口用于此目的.在您的数据类型类视图模型类中都实现它,然后它将对每个属性都适用.请记住,您也可以PropertyChanged在视图模型中添加处理程序.

速聊1:
这里有一些很好的建议.
速聊2:
这里有几点评论,我不使用它,而是根据情况使用.同样,我的模型(例如项目)包含ObservableCollection的原因是因为项目OWNS Proformas,每个项目都有一个单独的形式清单,并且每个形式都有一个单独的装运清单.有时,我需要根据项目的形式来从事某个项目的工作.因此,为什么项目要保留该列表.
速聊3:
你说我不使用它,而不是... ...如果"它"EventAggregator您正在使用以及INotifyPropertyChanged接口,那么它仍然是不必要的...INotifyPropertyChanged界面是所有你需要为.另外,我知道在上面的视图模型中Project有一个Proformas...的Proformas集合,该集合将是用户可以从中选择要添加到Project实例的完整列表.同样,Shipments上面的集合将包含所有可能的Shipment对象供用户选择.
速聊4:
我认为您误会了,在适用于未绑定的视图模型时,我会使用EventAggregator,在这种情况下,我知道它们已绑定.用户也不会"选择"属于项目的形式,项目具有一组形式,因此,当他们选择项目A时,经理会从Web服务加载项目A的所有形式,因此不会出现我需要"全部"的情况.可能"形式.这就是为什么将形式清单作为项目模型的属性对我来说更有意义的原因.
速聊5:
那么用户Proformas在创建新商品时如何挑选Project呢?
解决过程3

您是否尝试手动致电到

PropertyChanged(this,new PropertyChangedEventArgs("ProformaList"));;

加载项目形式后?

I'm struggling a little with the structure of my project still, and ran into an issue which im worried demonstrates that i'm still missing a good understanding of the right way to do things.

What i have currently is:

The program displays information for shipping, this is based on an overall project which contains proformas which contains shipments which contains containers (this is simplified for this example)

So in order to keep track of the currently selected objects (Project, proforma etc.) i have a class called ProjectTree which looks similar to this:

Public class ProjectTree
{
  Public Project SelectedProject { get; set; }
  Public Proforma SelectedProforma { get; set; }
  public Shipment SelectedShipment { get; set; }
}

again this is simplified, now since Project itself contains the list of Proformas, i have a function on Project called LoadProformas() which creates a new thread and loads that projects proformas into an ObservableCollection within project itself (the loading is done by a set of IManagers that handle all the data loading, saving etc. )

Now the issue i'm faced with is that say my viewmodel ProformaListViewModel has access to ProjectTree so it can call the LoadProformas() on the Project, i run into the issue of databinding the _ProjectTree.SelectedProject.ProformaList as i wont know when the data has actually been loaded since (afaik) NotifyOfPropertyChange wont flow through into the viewmodel in this scenario. Now i could always use the EventAggregator and fire an event when the data is loaded (and this is what I've done in the past) however whenever i look at this it seem a bit... haphazard, to be firing unbound events from an child object to the parent.

Have i missed something critical in my understanding here? Or would using the event be a reasonable way to accomplish what i need?

EDIT:

Just to clear this up, the reason im trying to do this is because each project has a list of proformas and each proforma a list of shipments etc. Originally the managers handled the lists of objects as well as loading and saving, but i found that having them totally uncoupled what causing issue when trying to do work with a project based on its shipments (for example)

Solutions1

Each of your viewModel classes (your Project, Proforma, and shipment classes may in this case be acting as both the model and viewModel) need to implement the INotifyPropertyChanged interface. Then your view should databind directly to the property SelectedProject.ProformaList. Since the list is an ObservableCollection already, your view will be notified when items are added or removed. You then just need to ensure that the PropertyChanged notification is raised when the ProformaList property itself is changed.

Since every property that is bound in the View should Notify of Property Changes, each level of your data 'tree' (Project, Proforma, Shipment) can be changed independently and still update the view.

Solutions2

In my opinion, you shouldn't be using any Selected... properties in your data type/model classes... those properties belong in your view model classes. Furthermore, unless you actually use an object in your work that is called a ProjectTree, I wouldn't name your object that... just stick to real world names.

Also, I'd advise against putting functionality in your data type classes... in my opinion (for the most part) they should just be containers for data. I would structure the view model like this (I'm ignoring the INotifyPropertyChanged interface in this example, but you'll need to correctly implement it):

public class ProjectViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Project> Projects { get; set; }
    public Project SelectedProject { get; set; }
    public ObservableCollection<Proforma> Proformas { get; set; }
    public Proforma SelectedProforma { get; set; }
    public ObservableCollection<Shipment> Shipments { get; set; }
    public Shipment SelectedShipment { get; set; }

    public void LoadProjects() {}
    public void LoadProformas() {}             
    public void LoadShipments() {}             
}

Next, you mentioned that you're using the EventAggregator instead of the INotifyPropertyChanged interface... it seems to me that you are making your life more difficult than it needs to be as the INotifyPropertyChanged interface can be used for this just fine. Implement it in both your data type classes and your view model classes and then it will work for every property. Remember, you can add a PropertyChanged handler in your view model(s) too.

Talk1:
Some great advice here.
Talk2:
A couple of comments here, I'm not using it instead of, im using it as well as depending on the situation. Also the reason my models such as project contains the ObservableCollection is because project OWNS Proformas, each project has a seperate list of proformas, and each proforma has a seperate list of shipments. Sometimes i need to do work on a project depending on it's proformas for example. Hence why project holds the list.
Talk3:
You said I'm not using it instead of...... if 'it' is the EventAggregator that you are using as well as the INotifyPropertyChanged interface, then it is still unnecessary... the INotifyPropertyChanged interface is all you need for that. Also, I understand that Project has a collection of Proformas... the Proformas collection in my view model above would be the complete list from which the user could choose the ones to add to the Project instance. Likewise, the Shipments collection above would contain all possible Shipment objects for the user to choose from.
Talk4:
I think you are misunderstanding, i use EventAggregator when appropriate for viewmodels that are unbound, i understand in this case they are bound. Also the user does not "choose" which proformas belong to a project, a project has a set of proformas so when they select project A the manager loads all of project A's proformas from a webservice, there is no instance where i would need "all possible" proformas. Which is why it made more sense for me to have proformas list as a property of the project model.
Talk5:
So how does a user pick the collection of Proformas when creating a new Project?
Solutions3

did you try to call manually to

PropertyChanged(this, new PropertyChangedEventArgs("ProformaList"));

after loading the projects proformas ?

转载于:https://stackoverflow.com/questions/20217706/propertychanged-for-child-properties-in-mvvm

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入