How to Pass Data from Mainwindow to a User Control That's Inside the Mainwindow

How to pass data from MainWindow to a User Control that's inside the MainWindow?

A UserControl should never have a "private" view model, as you assign it to the DataContext in the UserControl's XAML. It should instead expose dependency properties that could be bound to properties of an externally provided view model object.

Declare an ItemsSource property like this:

public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
nameof(ItemsSource), typeof(IEnumerable), typeof(UserControl1));

public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}

public UserControl1()
{
InitializeComponent();
}
}

And bind the ListView like this:

<UserControl ...>
...
<ListView ItemsSource="{Binding ItemsSource,
RelativeSource={RelativeSource AncestorType=UserControl}}">
...
</ListView>
...
</UserControl>

When you use the UserControl, bind the property to a view model property:

<TabItem Header="UC1">
<local:UserControl1 ItemsSource="{Binding OrderList}"/>
</TabItem>

The last XAML snippet assumes that the object in the UserControl's DataContext has a OrderList property. This would automatically happen when the TabControl is bound to a collection of view model objects with that property.


Alternatively, let the elements in the UserControl's XAML directly bind to the properties of the object in the inherited DataContext.

<UserControl ...>
...
<ListView ItemsSource="{Binding OrderList}">
...
</ListView>
...
</UserControl>

Your control would not have to expose additional bindable properties, but it would only work with DataContext objects that actually provide the expected source properties.

WPF Passing data object from Main application UI to user control

Since the UserControl are defined in XAML and are initialized by the code InitializeComponent of your MainWindow your are not able to use a constructor to pass a reference of your SensorDevice to the UserControls.

Add a property SensorDevice to your UserControls AlsTabUC and rangingTabUC to pass a reference of your SensorDevice to your UserControls after InitializeComponent is called in your MainWindow.

public SensorDevice Sensor {
get;
set;
}

Change the constructor of your MainWindow to the following

public MainWindow()
{
m_mySensorDevice = new SensorDevice();
InitializeComponent();

// Pass reference of SensorDevice to UserControls
rangingTabUC1.Sensor = m_mySensorDevice;
alsTabUC1.Sensor = m_mySensorDevice;
}

In your UserControls you can use the property to call the methods on your SensorDevice

SensorDevice.readAmbientLight();

or

SensorDevice.readRange();

How to pass shortcuts from MainWindow to the UserControl in ContentControl?

If you want the UserControl to be able to handle all key presses in the window, you could get a reference to the parent window using the Window.GetWindow once the UserControl has been loaded and then hook up an event handler to it:

public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
Loaded += (s, e) =>
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
};
Unloaded += (s, e) =>
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown -= ParentWindow_PreviewKeyDown;

};
}

private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
//do something...
}
}

Accesing properties in a UserControl from the MainWindow (WPF/MVVM)

MainWindowVMInstance.UserControlVMInstance.Property

The UserControl is inside your MainWindow.

Therefore your MainWindow has a property (/instance) of your UserControlVM.

Note: If you also need a reference of MainWindowVM inside your UserControlVM, pass it to the constructor and store it as property.

In xaml it would look like this (inside MainWindow.xaml):

<ContentControl Content="{Binding UserControlVMInstance}"/>

Don't forget the DataTemplate:

<DataTemplate DataType="{x:Type vm:UserControlVM}">
<view:UserControlView/>
</DataTemplate>

Edit after question update:

This is an example with a part of your code to demonstrate WPF and MVVM in action. You simply define a DataTemplate in your UserControl.Resources and then give the ContentControl via Binding an instance of your UserControlVM. WPF knows there's a DataTemplate for this type and will add an instance of the UserControlView where the ContentControl is.

<MainWindow>
<MainWindow.Resources>
<DataTemplate DataType="{x:Type vm:UserControlVM}">
<view:UserControlView/>
</DataTemplate>
</MainWindow.Resources>

<!-- Your TabControl -->
<TabControl>
<!--Result View-->
<TabItem Header="{Binding TabImportHeader}">
<ContentControl Content="{Binding TabImportCONTENT}"/>
</TabItem>
</TabControl>
</MainWindow>

How to passing value from window to usercontrol at runtime?

The UserControl is created before the GetMyValue property is set. You cannot set a property of an instance before you have created it...

Wait until the UserControl has been loaded and you will get the value as expected:

public UserControl1()
{
InitializeComponent();
Loaded += (s, e) =>
{
string finalValue = GetMyValue;
};
}

Passing data to user control in MVVM pattern

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.



Related Topics



Leave a reply



Submit