-->

Sharing Collection between ViewModels

2020-07-30 05:08发布

问题:

I've searched and nothing is helping me get to where I need to get.

Description of my problem(s):

  1. We have a single dialog window which hosts multiple views (usercontrols). Clicking next and back will move you forward and backwards in this dialog similar to a wizard.

  2. Out of the 6 dialogs, 4 of them reference the same core data. For example we will say an ObservableCollection

  3. That being the case, I have 4 viewModels which all need to reference this same ObservableCollection. I don't want to save and then reload the list everytime I move on to a new step in the "wizard" dialog.

My question is what's the best/most practical way to accomplish this.

The following things I've considered:

  1. Static class
  2. Singleton - ehhhh
  3. Passing parameters between views (although this is difficult as the nextlocation and previouslocation are very generic).
  4. Mediator pattern? My problem with the mediator pattern is that I don't want to "communicate" between views. I just want all of the views to share the same data source.
  5. Observer pattern? If I'm using an ObservableCollections and implementing INotifyPropertyChanged, then I shouldn't need to notify anyone of a change should I?

Please let me know additional info you might need to help me with this and I'll happily provide it.

I'm not really looking for code examples as I am design. Although if code examples can help explain the design, I'm all for it.

Lastly, EventAggregator is not an option as I'm not using any frameworks (unless I'm not understanding EventAggregator correctly).

Thanks in advance!!

回答1:

like Randolf said DI would work. i do this with MEF and contructor injection with CreationPolicy shared. you just have to put the stuff you wanna handle for you 4views in one export class. how easy is that :)

and btw: Mediator pattern is "communicate" between viewmodels not views.

[Export]
public class MyClassWichHasDataSharedFor4Views {}

public class Viewmodel1
{
   [ImportingContructor] 
   public Viewmodel1(MyClassWichHasDataSharedFor4Views shareddata)
   {}
}

public class Viewmodel2
{
   [ImportingContructor] 
   public Viewmodel2(MyClassWichHasDataSharedFor4Views shareddata)
   {}
}

public class Viewmodel3
{
   [ImportingContructor] 
   public Viewmodel3(MyClassWichHasDataSharedFor4Views shareddata)
   {}
}

public class Viewmodel4
{
   [ImportingContructor] 
   public Viewmodel4(MyClassWichHasDataSharedFor4Views shareddata)
   {}
}

now the 4 viewmodels has a reference to your core data



回答2:

Based on the description of your dialog and how it works, I would have one ViewModel that controlled the overall "Wizard".

This DialogWizardViewModel would contain:

  • ObservableCollection<SomeModel> Data
  • ObservableCollection<ViewModelBase> DialogWizardViews
  • ViewModelBase SelectedView
  • ICommand NextCommand
  • ICommand BackCommand

Your DialogView would contain something like a ContentControl bound to the SelectedView, and your Next/Back commands would simply switch the SelectedView to the next or previous view in DialogWizardViews

For example,

<DockPanel>
    <StackPanel DockPanel.Dock="Bottom" 
                Orientation="Horizontal" 
                HorizontalAlignment="Center">
        <Button Command="{Binding BackCommand}" Content="Back" />
        <Button Command="{Binding NextCommand}" Content="Next" />
    </StackPanel>

    <ContentControl Content="{Binding SelectedView}" />
</DockPanel>

The Data can can set in your child ViewModels when you first create the DialogWizardViews, or whenever you switch the SelectedView depending on what you prefer.

DialogWizardViews = new ObservableCollection<ViewModelBase>()
{
    new DialogViewModel1(Data),
    new DialogViewModel2(),
    new DialogViewModel3(Data),
    new DialogViewModel4(Data),
    new DialogViewModel5(Data),
    new DialogViewModel6()
};

SelectedView = DialogWizardViews.FirstOrDefault();

Remember, with MVVM your ViewModels are your application while the Views just provide a user-friendly interface for users to interact with the ViewModels. Ideally you should be able to run your entire application from something like test scripts, or a console application, without the View at all.

If you need to pass something around, it should be handled by some parent object within the application hierarchy, or use some kind of communications system such as Prism's EventAggregator or MVVM Light's Messenger. You don't need to use the entire framework to make use of these objects - you can just pick out the parts you're interested in.



回答3:

I know it has been a long time since this question was asked, but after struggling a lot with the same problem RayBurns's Response here really helped me come to the really simple and relieving realization that there is no need to feel like you have to make viewmodels for each of your usercontrols. I now use one viewmodel for a bunch of different views over the evolution of a Collection as a family of related information whose states are all relevant to eachother .

Otherwise, if you really must share data between viewmodels, I'd absolutely do this as a parameter to the constructor like @blindmeis demonstrates.

The nature of how DataContexts are set up and shared in the first place (for me!) seems to lie in the code-behind of the View. That's the only thing I put there, but it allows me to share ObjectContexts in Entity Framework, establish Master-Details relationships with one view-many viewmodels, etc.

Forming a repository "higher layer" is also great, but be cautious that you're copying over/transferring the data several times over before it reaches its destination.

Anywho, that's my two cents. Hope it helps. After a lot of discomfort that's what I ultimately found myself converging to.



回答4:

It sounds to me like your views share the same Model, which should be observable, and your ViewModels should all represent the Model in different ways.

If your Model and ViewModels implement the Observer pattern, that seems to be the cleanest, "rightest" approach.



回答5:

A simple "dependency injection" should work. Constructor of the second viewmodel depends on that Shared Data parameter injected into his constructor, and so on.