Passing data to user control in MVVM pattern - wpf

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.

You are instantiating the UserControl1 object twice:
Once within the XAML. The <uc:UserControl1> element instantiates a UserControl1 object, using the default constructor, and assigns it to the member ucControl.
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:
Remove the XAML's user control.
In main window, subscribe to the Loaded event
In the handler of the Loaded event, instantiate a new UserControl1 - with whatever constructor parameter that you want.
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

Update bindings for control created in code

I have created a UserControl, which has some values bound to its view model.
I want to create an instance of that UserControl, provide it with a view model, and save it to an image without actually showing it on screen anywhere.
The problem is that when I create an instance of the UserControl and give it its view model, the bindings don't update. So the image that I am saving is the control without any data in it.
How can I force all the bindings to update for a user control that I am creating in code, without actually displaying it on screen?
Here is a short example I wrote:
UserControl1.xaml:
<Grid>
<TextBlock Name="txt" Text="{Binding}" />
</Grid>
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
internal string GetText()
{
return txt.Text;
}
}
Test.cs
public void Test()
{
UserControl1 uc1 = new UserControl1();
uc1.DataContext = "Test";
Console.WriteLine("The text is " + uc1.GetText());
}
I am expecting it to print out "The text is Test", but it actually prints out "The text is".
I managed to get around the problem by setting the DataContext before calling InitializeComponent. In order to do this, I created a new constructor for my user control that looks like this:
public UserControl1(object viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
If I use this constructor, the bindings will work even if I never show the control on screen.

WPF Call Method On User Control With Binding

I created a Task List control. I have an AddTask method on it. I'd like to call this method from the host Window.
I found a few posts here in SO and other sites that suggest using an interface, then looping over all the controls in the window to find the control, then getting a reference to it and using that to call the method. Here's an example:
Call method on various user controls
But is it possible to call a method somehow with binding? Assume someone is using MVVM and the Window's VM wants to fire the control's AddTask method. is this possible?
Thanks!
If you really want to do it (in a possible) the right way i'd tell you to write about MVVM.
Binding and methods work very well in MVVM using Commands
Here it is my solution
Create a ViewModel class
Create a nested class MyCommandBehaviour that implements ICommand (some people create the class in a different class)
In the view model create a property MyCommandBehaviour MyCommand
In the constructor of the view model instantiate that property
In The XAML bind the button {Binding MyCommand}
Set the DataContext of the window (or user control) to the view model
Note: I usually create the Command nested class with a constructor that accepts the 'parent' view model. Because the class is nested it can directly access the view model private members
public class OkCommand : System.Windows.Input.ICommand
{
private MyViewModel _vm;
public OkCommand(MyViewModel vm)
{
this._vm = vm;
}
public bool CanExecute(object parameter)
{
return true;//I never use this and the event below
}
#pragma warning disable 0067
public event EventHandler CanExecuteChanged;
#pragma warning restore 0067
public void Execute(object parameter)
{
//do your stuff. Note you can access the MyViewModel members here via _vm
}
}

Order In Chaos Of Control Initialization Steps

I would like to know when is actually what happening inside initalization process of controls when I start a WPF application?
When are DP initalized? When Binding? When does DataContext get set? Is DataContext avaialbe in constructor of a control? Is there any kind of order?
I realized I ran into a trap that once I set a value on getter/setter of a DP inside constructor of a control the DP value gets updated but immediately also the values gets rolled back to default value which was null.
So my guess is that contructors get initalized first and then dependency properties.
Can somebody help me out with this?
Edit: Just for Rachel. The dp receives the value 234 and immedialty rolls back to null. I think its because constructor gets called first and then subsequently the initalizing of dps happens which sets dp back to null because null is default value. Am i thinking wrong about this? What is the order of initalization steps of a control or dependency object.
class MySuperDuperCoolClass : ContentControl
{
public MySuperDuperCoolClass()
{
InitalizeComponents();
this.MySuperDuperProperty = "234";
}
public string MySuperDuperProperty
{
get { return (string)GetValue(MySuperDuperPropertyProperty);}
set { SetValue(MySuperDuperPropertyProperty, value);}
}
public static DependencyProperty MySuperDuperPropertyProperty =
DependencyProperty.Register("MySuperDuperProperty", typeof(string), typeof(MySuperDuperCoolClass),
new PropertyMetadata(null));
}
I find the DispatcherPriority Enum useful for recalling the exact event order:
Send
Normal - Constructors run here
DataBind
Render
Loaded
Background
ContextIdle
ApplicationIdle
SystemIdle
Inactive
Invalid
Input
As you can see, Constructors get run first, followed by data bindings.
DependencyProperties get initialized when the object gets created, just like any other property, so that would occur prior to the constructor being run so the property exists in the constructor.
Setting the DataContext property or other DependencyProperties works just like any other property you are setting. If you set them with a binding, they'll get evaluated after the constructor. If you set them in the XAML, they'll get set in the Constructor. If you set them in the Loaded event, they'll get set after everything has been constructed, bound, and rendered.
You also might find this SO answer useful:
Sequence of events when a Window is created and shown
As requested, here is the sequence of major events in WPF when a
window is created and shown:
Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the
objects being updated and any objects that inherit from them
As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be
found applied in addition to any element-specific initialization you
may define [note: Initialized event not fired for leaves in a logical
tree if there is no PresentationSource (eg Window) at its root]
The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional
object tree construction including more constructors and
getters/setters
The window and all non-collapsed Visuals on it are Arranged
The window and its descendants (both logical and visual) receive a Loaded event
Any data bindings that failed when they were first set are retried
The window and its descendants are given an opportunity to render their content visually
Steps 1-2 are done when the Window is created, whether or not it is
shown. The other steps generally don't happen until a Window is
shown, but they can happen earlier if triggered manually.
Edit based on code added to question
Your DependencyProperty.Register method looks funny to me. The signature of the method doesn't match any of the overloads for that method, and you're using what appears to be a custom UIProperty class to set the default value instead of the normal PropertyMetadata.
I can confirm that if your code runs as expected with a normal DependencyProperty.Register signature, so the likely cause of your problem is either somewhere within your custom code, or its with how you are using/setting the property.
The code I used for a quick sample test is this:
public partial class UserControl1 : ContentControl
{
public UserControl1()
{
InitializeComponent();
this.TestDependencyProperty = "234";
}
public string TestDependencyProperty
{
get { return (string)GetValue(TestDependencyPropertyProperty); }
set { SetValue(TestDependencyPropertyProperty, value); }
}
public static DependencyProperty TestDependencyPropertyProperty =
DependencyProperty.Register("TestDependencyProperty", typeof(string), typeof(UserControl1),
new PropertyMetadata(null));
}
and the XAML is
<ContentControl x:Class="WpfApplication1.UserControl1"
x:Name="TestPanel" ...>
<Label Content="{Binding ElementName=TestPanel, Path=TestDependencyProperty}"/>
</ContentControl>
In WPF you are setting default values for DP with PropertyMetaData not via constructor.
public partial class UserControl1 : ContentControl
{
public UserControl1()
{
InitializeComponent();
}
public string TestDependencyProperty
{
get { return (string)GetValue(TestDependencyPropertyProperty); }
set { SetValue(TestDependencyPropertyProperty, value); }
}
public static DependencyProperty TestDependencyPropertyProperty =
DependencyProperty.Register("TestDependencyProperty", typeof(string), typeof(UserControl1),
new PropertyMetadata("234"));
}

Notify button clicked in popup window to main window on wpf

I have two windows ,say window1 and window2(this one should be a pop up) on WPF.
What I want to do is, when a button in popup window(window2) is clicked,
I want to run method in window1.
I can achieve this by passing window1 to windows2, but I think it's not an memory-efficient way.
I have red article about routedCommand, but it's hard to understand.
I'm working on c# and any help is appreciated
Thank you
Quite often I have a static property for Current on my MainViewModel (or which ever ViewModel), and I set that property in the constructor for the ViewModel. Then from anywhere else in the application, I can get a reference to the ViewModel in question.
On the ViewModel
public MainViewModel()
{
Current = this;
}
public static MainViewModel Current { get; set; }
Anywhere else in the application:
MainViewModel.Current.DoSomething();
Routed Command
Routed commands are typically databound, and thus the command logic depends on which Data Context it is written on. If Window1's DataContext is MainViewModel, and Window2's DataContext is SecondViewModel, in order to have a button on Window2 execute a command on MainViewModel, you will have to have a reference to that instance of MainViewModel as the DataContext for the button in question.

WPF User Control's DataContext is Null

I have a user control where the XAML of the control can bind to the appropriate properties from the parent's data context like normal (the data context propagates in xaml).
For example, I have a window whose DataContext I am setting to ObjectA for example. My user control within the window is then try to access the properties within the dataContext
So my window's xaml and code behind can both see a non-null DataContext.
My control that DataContext propagates to can see a non-null DataContext in the Xaml but not in the code behind.
What is the proper way of handling this?
failing that if you need to check whether the DataContext is being set you can use the DataContextChanged
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
DataContextChanged += new DependencyPropertyChangedEventHandler(UserControl1_DataContextChanged);
}
void UserControl1_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// You can also validate the data going into the DataContext using the event args
}
}
Note it wont enter UserControl1_DataContextChanged until DataContext is changed from null to a different value.
Not sure if this answers your question but can be quite handy to use in debugging issues.
I think you are checking the 'DataContext' in the constructor of the UserControl. It will be null at the Constructor since the user control hasnt yet created while execution is in the constructor code. But check the property at Loaded event you will see the object properly.
public partial class UserControl1
{
public UserControl1()
{
this.InitializeComponent();
//DataContext will be null here
this.Loaded += new RoutedEventHandler(UserControl1_Loaded);
}
void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
//Check DataContext Property here - Value is not null
}
}
I would check to see whether you are having a binding error at runtime. Add this namespace to your XAML:
xmlns:debug="clr-namespace:System.Diagnostics;assembly=System"
and check the debugger's Output window for relevant error messages.
Alternatively, can you show us more code?

Resources