1

Background:

I have WPF application with a main window containing a user control. I want to pass a value from the main window to the user control.

In the Main window's constructor I have code:

    public MainWindow()
    {
        InitializeComponent();
        _vm = new MainWindowViewModel();
        this.DataContext = _vm;
        ucControl = new UserControl1("NameSet");
    }

(ucControl is my user control)

User control has two constructors:

    public UserControl1()
    {
        InitializeComponent();
        this.ID = ID.GetNewID;
    }

    public UserControl1(string name)
    {
        InitializeComponent();
        _vm = new UCViewModel(name);
        this.DataContext = _vm;
        this.ID = ID.GetNewID;
    }

The problem is: although the second constructor (with parameter) is called, it is not loaded in the main window. I checked the ID (this.ID) in the user control's loaded event and I see the ID set in the default constructor and its DataContext is null. Because of this reason, I do not get the "name" string in my user control.

Any help please? Since I am using MVVM pattern I do not want to expose properties in user control (view) to be set from main window just for this.

H.B.
  • 166,899
  • 29
  • 327
  • 400
thewpfguy
  • 794
  • 6
  • 15

1 Answers1

1

You are instantiating the UserControl1 object twice:

  1. Once within the XAML. The <uc:UserControl1> element instantiates a UserControl1 object, using the default constructor, and assigns it to the member ucControl.

  2. You instantiate it again within the constructor of the MainWindow object

If you put a break point in the constructor of UserControl, you'll notice it is called twice. I assume WPF instantiate and initialize the XAML's UserControl (#1 from above) after you assign the dynamic UserControl (#2 from above), and this is why you see the former in the logical tree of MainWindow.

You should have only one instance. If you want to parameterized a user control, the canonical paradigm is what you mention that you don't want to do (why??). If you had such a property, you could set it in the XAML: <uc:UserControl1 x:Name="..." YourProperty="NameSet>

exposing such a property is a single line in the UserControl:

public YourProperty { get; set; }

If you insist of not having this line, you should do the following:

  1. Remove the XAML's user control.
  2. In main window, subscribe to the Loaded event
  3. In the handler of the Loaded event, instantiate a new UserControl1 - with whatever constructor parameter that you want.
  4. Manually add it to the Children array of the parent Grid element

Clearly this isn't my recommendation. In addition to the complexity, with the former method you'll also work very well with the Visual Studio designer.

Chris Schiffhauer
  • 17,102
  • 15
  • 79
  • 88
Uri London
  • 10,631
  • 5
  • 51
  • 81
  • 1
    Thanks for your reply. I have the "name" property in user control's view model. How do I set that? Of course this wouldn't work - – thewpfguy Mar 22 '12 at 07:55
  • Should we duplicate all properties in view model in the user control class, just for this purpose? – thewpfguy Mar 22 '12 at 07:59
  • That would fail DataContext.Name, just access ucControl.DataContext in code. – Silvermind Mar 22 '12 at 08:18
  • @thewpfguy - the DataContext of UserControl is different than the UserControl. You need to instantiate it as well, either by code (typical), or by XAML. If by code, you'll do it by the MainWindow view model, and typically it would be a property of the MainWindow View Model (e.g. MainVm.ucVm). Then, you bind the DataContext property of UserControl1 to it: . – Uri London Mar 22 '12 at 08:19