-1

I just can't figure this out. So I will try describe my problem best I can.

I am building application using MVVM pattern. I have user control AAAView with viewmodel AAAViewModel that is used to fill data class CDataClass. I also have main window MainView and its viewmodel MainViewModel. Next I have window DialogView with DialogViewModel.

So now MainViewModel (that has its own user control) creates DialogViewModel (with another instance of user control). How can I transfer data in CDataClass between these two user controls? I tried to create property in AAAViewModel that would hold instance of MainViewModel or DialogViewModel so I can pass data to it but I get stuck because I couldn't make it as dependency property.

Image representation of problem

My goal is to make user control that can be used in different views which can have different data in underlaying CDataClass.

Just to clarify... I am using user control as <views:GeneralInfoView Grid.Row="0" /> and don't know how to share data between two different instances of the same user control in different views. Any point to some pattern or method would be much appreciate.

Thank you for help.

benderto
  • 896
  • 11
  • 39

1 Answers1

1

I don't think it's ideal that you've got your application architecture diagrammed as relationships among views; I think a better way to think about it is as a set of relationships among viewmodels, with the views hanging off that tree as needed. When you think about it that way, "how does data get passed" gets a lot simpler. A view is just a conduit between a viewmodel and the user. You don't design a house as a set of windows and telephones and then try to figure out the floor plan from that. You start with what the house does and how people will live in it.

So this is easy:

Some viewmodels have an AAViewModel property. There may be all kinds of simple or complicated views on those viewmodels; if a view wants to let the user edit the viewmodel's AAViewModel stuff, then it includes an AAView bound appropriately to the viewmodel's AAViewModel. Your MainViewModel and DialogViewModel are both big complicated interactive views that want to let somebody edit their vm's AAViewModel stuff.

If MainViewModel is DialogViewModel's parent, or created a temporary instance of DialogViewModel just to put in a modal dialog, then MainViewModel would show the dialog, and have a look at dialogVM.AAVM.CData.IsDirty to decide what to do with it. Or maybe it gives dialogVM.AAVM a new CDataClass instance before showing the dialog (maybe a clone of its own instance), and if ShowModel() returns true, then it does something with dialogVM.AAVM.CData.

The point is that once your viewmodels are driving everything, it becomes relatively simple for them to communicate with each other. Parent-child is easy: Parent gives stuff to the child and looks at what the child brings back. A viewmodel can subscribe to another viewmodel's PropertyChanged event; a parent viewmodel can monitor its children; when something happens on a child, the parent can decide whether to update a sibling. In general, children should not know anything at all about their parents; this makes it much easier to reuse child viewmodels in disparate contexts. It's up to parents to decide what to do with that information.

All AAViewModel knows is that somebody handed him a copy of CDataClass; he updates his public properties accordingly. Then somebody else (probably AAView, but he doesn't know) hands him some changes by setting his properties; he updates his CDataClass instance accordingly. After a while, unknown to him, one viewmodel or another comes and looks at that CDataClass.

And communication between views and viewmodels happens via bindings.

UPDATE

It turns out that you're creating viewmodels in your views, and as a result you have no idea how the parent can get to them. And now you know why it's not a good idea to create child view viewmodels that way.

Here's how you do child view/viewmodels in the viewmodel-centric design I described above:

First, get rid of whatever you're doing to create the child viewmodels inside the view.

Second, create a DataTemplate for the child viewmodel type. This should go in a resource dictionary which is merged into the resources in App.xaml, but it's so simple that it won't kill you if you get lazy and just paste it into the Resources of the two different views where it's used.

I don't know what your namespace declarations look like. I'm going to assume that views are in something called xmlns:view="...", and viewmodels are in something called xmlns:vm="...".

<DataTemplate DataType="{x:Type vm:AAAViewModel}">
    <view:AAAView />
</DataTemplate>

Now, you can assign an AAAViewModel to the ContentProperty of any control that inherits from ContentControl (and that's most of them), and the template will be instantiated. That means that XAML will create an AAAView for you and assign that instance of AAAViewModel to the DataContext property of the AAAView that it just created.

So let's create a child AAAViewModel next, and then we'll show it in the UI.

public class DialogViewModel 
{

    //  You can create this in DialogViewModel's constructor if you need to 
    //  give it parameters that won't be known until then. 
    private AAAViewModel _aaavm = new AAAViewModel();
    public AAAViewModel AAAVM
    {
        get { return _aaavm; }
        protected set {
            _aaavm = value;
            OnPropertyChanged(nameof(AAAVM));
        }
    }

And now we can display AAAVM in DialogView:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <ContentControl 
        Content="{Binding AAAVM}" 
        Grid.Row="0"
        />

    <StackPanel Orientation="Vertical" Grid.Row="1">
        <!-- Other stuff -->
    </StackPanel>
</Grid>

Now how does MainViewModel get in touch with a DialogViewModel? In the case of dialogs, since they have a finite lifespan, it's not actually a big deal to let them create their own viewmodels. You can do it either way. I generally lean towards having it create its own as in the second example below.

Not quite the same, but close. First, once again, get rid of whatever you're doing where the dialog creates its own viewmodel.

MainViewModel.cs

public CDataClass CDC { /* you know the drill */ }

public void ShowDialog()
{
    var dvm = new DialogViewModel();

    //  Maybe this isn't what you want; I don't know what CDataClass does. 
    //  But I'm assuming it has a copy constructor. 
    dvm.AAAVM.CDC = new CDataClass(this.CDC);

    if (DialogView.ShowDialog(dvm).GetValueOrDefault())
    {
        CDC = dvm.CDC;
    }
}

Note that this next one is view codebehind, not viewmodel.

DialogView.xaml.cs

public static bool? ShowDialog(DialogViewModel dvm)
{
    var vw = new DialogView() { DataContext = dvm };

    return vw.ShowDialog();
}

Now, you could let the dialog continue creating its own viewmodel; in that case you would give it a public property like this:

public DialogViewModel ViewModel => (DialogViewModel)DataContext;

And a ShowDialog method like this:

DialogView.xaml.cs

public static bool? ShowDialog(CDataClass cdc)
{
    var dlg = new DialogView();

    dlg.ViewModel.AAAVVM.CDC = cdc;

    return dlg.ShowDialog();
}

And then the parent could interact with it like this instead:

MainViewModel.cs

public void ShowDialog()
{
    var cdcClone = new CDataClass(this.CDC);

    if (DialogView.ShowDialog(cdcClone).GetValueOrDefault())
    {
        CDC = cdcClone;
    }
}

Nice and tidy.

If that dialog isn't modal, make the dialog viewmodel a private member of MainViewModel, and have MainViewModel subscribe to events on the dialog viewmodel to keep abreast of what the dialog is doing. Whenever the user updates the dialog's copy of CDataClass, the dialog would raise DataClassUpdated, and MainViewModel would have a handler for that event that sniffs at _dialogViewModel.AAAVM.CDC, and decides what to do with it. We can get into example code for that if necessary.

So now you can see what I mean by building everything in terms of parent/child viewmodels, and stuffing them into views when and as appropriate.

Community
  • 1
  • 1