0

In my WPF application I have a MainWindow.xaml which has many User Controls embedded into it. I have separate viewmodel for both the main window as well as the user controls. In the Main Window ViewModel I create an instance of the child ViewModel and set it as the child's datacontext.

Parent VM ChildUserControlVM ChildVM = new ChildUserControlVM (ParentModel.ChildModel);

MainWindow.xaml

But this approach is not working as I am not getting the values set in the parent viewmodel in the child one and vice versa.

On the contrary, if I set the Child model object as the datacontext from the parent that is working both way.

I need some solution so that I can use MVVM in the user controls also ensuring data gets passed from parent to child and vice versa. In the usercontrol I am going to have some buttons whose action I want to handle in the child Viewmodel through ICommand. Adding the code snippets for reference MainWindow.xaml

<Grid Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0">
    <local:ProfileIdentitySettings Visibility="{Binding ProfileIdentitySettingsVisibility,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" DataContext="{Binding ChildProfileIdentityVM}"/>      
</Grid>

MainWindowVM.cs

 ProfileIdentitySettingsVM ChildProfileIdentityVM = new ProfileIdentitySettingsVM(DeviceEditorModel.ProfileIdentitySettings);

ProfileIdentitySettings.xaml

DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.MainWindowVM}">

Daniele Sartori
  • 1,674
  • 22
  • 38
Sudipta Saha
  • 31
  • 1
  • 8

3 Answers3

1

The parent view model should contain the child view models as properties, like

public class ParentViewModel
{
    public ChildViewModel1 ChildViewModel1 { get; set; }
    public ChildViewModel2 ChildViewModel2 { get; set; }
}

Your UserControls should not have any view models of their own. Instead they should expose dependency properties like

public partial class MyView1 : UserControl
{
    public string MyProperty { get; set; } // this must be a dependency property
}

which can be bound when the UserControl is used, e.g. in your MainWindow's XAML, like

<MyView1 MyProperty="{Binding ChildViewModel1.SomeProperty}" />

If there are many of these bindable properties, you may set the UserControl's DataContext once to the appropriate child view model:

<MyView1 DataContext="{Binding ChildViewModel1}"
         MyProperty="{Binding SomeProperty}" />
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • I've read your comment about not using viewModels with UC. Sounds good, but how will you reuse such UC, when you set DataContext = "{Binding ChildViewModel1}"? – Sasha Dec 07 '17 at 11:23
  • 1
    @Sasha he is doing that in the MainWindow so that MyView1 (the UC) can be reused with other viewmodels – Daniele Sartori Dec 07 '17 at 11:29
  • 2
    The line `` is in the MainWindow's XAML. And of course it assumes that the MainWindow's DataContext has been set to an instance of ParentViewModel. So you could easily reuse the UserControl, e.g. in the same MainWindow, but bind to another child view model like `` – Clemens Dec 07 '17 at 11:33
0

Set the datacontext of each of your usercontrol in the xaml

<UserControl.DataContext>
    <VMPath:ChildVM/>
</UserControl.DataContext>

then add a loaded event for your user controls defined in your MainWindow

<ViewPath:MyUserControl x:Name="uc1" Loaded="UserControlLoaded"/>

in your code behind you'll have

    private void UserControlLoaded(object sender, RoutedEventArgs e)
    {
        ParentVM vm = this.DataContext as VM;
        ChildVM cvm = uc1.DataContext as ChildVM;
        // do your stuff
    }

if the operation you need to do are always the same you can use 1 method to all of your UserControl.

Basically what you are going to do are the following operation.

  • set statically the dantacontext for your user controls to an instance of an "empty" ChildVM

  • load your controls

  • after the loading do the operation you need to do on your ChildVM (like set some properties)

All of this before you can actually use your app

Daniele Sartori
  • 1,674
  • 22
  • 38
  • 3
    Don't do this. Never! Setting a UserControl's DataContext is an anti-pattern. It effectively prevents that the control's properties can be bound to an inherited DataContext, which is what you usually want to happen. Although many "WPF experts" on the internet tell you so, setting a UserControl's DataContext will inevitably get you into trouble. – Clemens Dec 07 '17 at 10:46
  • @Clemens OP didn't gave us many details, but as far as i understand ChildVM is a different viewmodel. So actually OP's usercontrols only need to have some Properties setted by the main windows datacontext, and don't need to inherit it's datancontext. I just proposed a simple way to do that. – Daniele Sartori Dec 07 '17 at 10:54
  • Setting a UserControl's DataContext is always wrong. See e.g. https://stackoverflow.com/a/28815689 or https://stackoverflow.com/a/44729258. You are giving a very bad advice here. – Clemens Dec 07 '17 at 10:55
  • @Clemens it would be a very bad advice if OP would need to use it's user controls with different viewmodels. Here on SO i think like 99% of the question i see (especially from the newst users) project their app with several coupled strongly view/viewmodels, and i think this case is no exception. If this wasn't the case however also you answer is bad (unless ChildVM inherit really from ParentVM). If they are separate Viewmodels like i think you should do [this](https://stackoverflow.com/questions/14361687/interaction-of-view-models-in-mvvm) to be fully MVVM friendly – Daniele Sartori Dec 07 '17 at 11:27
  • I'm not talking about applications and their main windows. You will of course set a view model there. However, quite a lot of people here (including me) constantly answer questions like "why is my UserControl Binding not working". Usually because askers made the above mentioned mistake. Regardless how long you talk about it, it's still wrong. And we've discussed this a thoused times here... – Clemens Dec 07 '17 at 11:31
  • @Clemens it was just an example. The point is that you shouldn't also have viewmodels that know each other. You want to go full MVVM ? Then you need to implement a service like the one in the link (i think it's called Mediator Pattern) – Daniele Sartori Dec 07 '17 at 11:37
0

My Understanding is like you need to set the usercontrol Viewmodel from MainViewModel.

MainViewModel:

 public class MainWindowViewModel : INotifyPropertyChanged, IMainViewModel
{

    public MainWindowViewModel()
    {
        this.Collection = new List<string>();
        this.Child = new ChildUserControlViewModel();
        this.Child.mainViewModel = this;
        this.Child.TextValue = "Justin";
    }
    private List<string> _Collection;

    public List<string> Collection
    {
        get { return _Collection; }
        set { _Collection = value; this.OnPropertyChanged("Collection"); }
    }
    private string _MainValue;
    public string MainValue
    {
        get { return _MainValue; }
        set { _MainValue = value; this.OnPropertyChanged("MainValue"); }
    }
    private ChildUserControlViewModel child;
    public ChildUserControlViewModel Child
    {
        get { return child; }
        set { child = value; this.OnPropertyChanged("Child"); }
    }

}

Child Usercontrol View model:

public class ChildUserControlViewModel : INotifyPropertyChanged
{

    public IMainViewModel mainViewModel = null;

    public List<string> Collection
    {
        get { return this.mainViewModel.Collection; }
        set { this.mainViewModel.Collection = value; this.OnPropertyChanged("Collection"); }
    }

    private string _TextValue;
    public string TextValue
    {
        get { return _TextValue; }
        set
        {
            _TextValue = value;
            this.mainViewModel.MainValue = value;
            this.mainViewModel.Collection.Add(value);
            this.OnPropertyChanged("TextValue");
        }
    }
}





 public interface IMainViewModel
    {
        string MainValue { get; set; }

         List<string> Collection { get; set; }
    }

View:

<Grid>
   <usr:ChildUserControl DataContext="{Binding Child}"/>
</Grid>
Justin CI
  • 2,693
  • 1
  • 16
  • 34