How to pass parameters to usercontrol's viewmodel through window's xaml? I am using MVVM pattern.
I have tried creating a dependency property as below. But passing it in constructoe of viewmodel throws "nonstatic properties cannot be field initializers" exception.
xaml.cs of usercontrol
public partial class SomeView : UserControl
{
SomeViewModel vm = new SomeViewModel(ForeColor);
public SomeView()
{
InitializeComponent();
this.DataContext = vm;
}
public Color ForeColor
{
get { return (Color)this.GetValue(ForeColorProperty); }
set { this.SetValue(ForeColorProperty, value); }
}
public static readonly DependencyProperty ForeColorProperty = DependencyProperty.Register("ForeColor", typeof(Color), typeof(SomeView ));
}
and this is how I am calling the user control
<local:SomeView ForeColor="{Binding Foreground}"/>
Foreground is a property of type System.Drawing.Color
As the error stated you cannot refer instance fields from field initializers.
Move the initialization logic to constructor instead:
SomeViewModel vm;
public SomeView()
{
InitializeComponent();
vm = new SomeViewModel(ForeColor);
this.DataContext = vm;
}
I would suggest you to always keep the View's code behind as simple as possible, making this way a cleaner app and keeping in mind the MVVM pattern concept.
Based on my understanding, you would want to update the property which has changed on the View to its ViewModel. Otherwise, you could just define the property in the ViewModel without passing it from the View.
Therefore, defining the Property on the ViewModel anyway, you could update its value by specifying the Binding Mode as "OneWayToSource" or "TwoWay". No Property instantiation would be needed on the View's code behind for passing through parameter. The Binding with its DataContext would do the work. In addition, to get these binding modes to work, you may set the UpdateSourceTrigger property on the Binding block.
You can find more information on the following MSDN site:
Binding.Mode Property
Binding.UpdateSourceTrigger Property
I hope this helped you, Regards.
Related
I am working on an application and trying to follow MVVM as much as possible and thus have many Views with corresponding ViewModes. I am deserializing a ViewModel which is instantiated in the View using XAML. For example, if the View is called "ExampleView" and the ViewModel is called "ExampleViewModel". The ViewModel is instantiated in the ExampleView by doing this...
<UserControl.Resources>
<local:ExampleViewModel x:Key="ViewModel" />
</UserControl.Resources>
With the following code behind to get/set the ViewModel from the View (normally this is only a get, but I tried the set to set the ViewModel after deserialization).
public ExampleViewModel ViewModel
{
get { return (ExampleViewModel)this.Resources["ViewModel"]; }
set
{
if (this.Resources["ViewModel"]!=value)
{
this.Resources["ViewModel"] = value;
}
}
}
This didn't work, but I figured the reason is that PropertyChanged wasn't being fired. So in ExampleViewModel I put in a method to refresh each of the Properties. For example ...
public void RefreshAllProperties()
{
NotifyPropertyChanged("Property1");
NotifyPropertyChanged("Property2");
...
}
where NotifyPropertyChanged is ...
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
While this doesn't pass the code smell test, I was trying to understand on my way to find something more elegant. However, I was surprised to find it didn't work.
I would prefer to keep the ViewModel instantiated in the XAML. Is there a best practice to re-instantiate the ViewModel after deserialization?
Modified follow on question
Any comments on moving the ViewModel creating into the constructor of the View class? Is this a better design pattern?
ExampleViewModel exampleViewModel;
public ExampleView()
{
InitializeComponent();
ExampleViewModel = new ExampleViewModel();
this.DataContext = ExampleViewModel;
}
public ExampleViewModel ViewModel
{
get { return exampleViewModel; }
set
{
if (exampleViewModel!=value)
{
exampleViewModel = value;
NotifyPropertyChanged();
}
}
}
I haven't seen a ViewModel defined in a ResourceDictionary before. I tend to initialize my ViewModels in the code-behind (I know you mentioned you wanted to keep it in XAML) since I can more directly control the page DataContext and when it ultimately updates (like after deserialization in your case). Modifying the ResourceDictionary at runtime seems like a dangerous approach and the ResourceDictionary in WPF does not implement INotifyPropertyChanged or any other change interface like INotifyCollectionChanged, meaning that it will not notify anything that one of its key-value pairs has changed in some way.
In summary: My answer is to not define your VM in a ResourceDictionary, but to manage it in your page code-behind where you can ensure the DataContext is appropriately updated when the VM or its state changes.
I created a custom renderer for a Label. Now I wanted to manipulate with its focus state, so I created bindable property and event. It works fine when I change the bindable property from custom renderer like so: Element.IsFocused = true;
But when I change it from the view model, it affects XAML view, but for some reasons doesn't call the setter for this property. Here is the code:
In custom class:
public new static readonly BindableProperty IsFocusedProperty =
BindableProperty.Create(nameof(IsFocused), typeof(bool), typeof(FloatingEntry), false);
public new bool IsFocused
{
get { return (bool)GetValue(IsFocusedProperty); }
set
{
SetValue(IsFocusedProperty, value);
if (value) Focus();
}
}
In XAML:
IsFocused="{Binding PasswordEntryIsFocused}"
In View Model:
private bool _passwordEntryIsFocused;
public bool PasswordEntryIsFocused
{
get { return _passwordEntryIsFocused; }
set
{
SetProperty(ref _passwordEntryIsFocused, value);
}
}
In View Model in some method: PasswordEntryIsFocused = true;
It's not about new keyword, I tried without it.
And binding works, because I tried to bind it with a visual property, like IsVisible and it was working like it should, but setter is always called only from a custom renderer.
I think I may miss something in a context of bindable property work.
But when I change it from the view model, it affects XAML view, but for some reasons doesn't call the setter for this property.
Yes, that is a common mistake with WPF. The XAML generated code does not call the setter, instead it changes the bound dependency property immediately. You can't break on this event, unless you attach the PropertyChangedCallback event.
I have a usercontrol with a single Textbox inside a grid like this:
<TextBox Text="{Binding Username}"></TextBox>
The code-behind of the usercontrol implements INotifyDataErrorInfo and INotifyPropertyChanged. This is how my code-behind looks like (Apart from the above mentioned interface implementations)
public TestControl()
{
InitializeComponent();
this.DataContext = this;
}
private string _username;
public string Username
{
get { return _username; }
set
{
_username = value;
if (_username.Length < 3)
SetErrors("Username", new List<string> { "Usernames should be at least 3 characters long" });
OnPropertyChanged("Username");
}
}
Where SetErrors is just a function which adds an error to the IEnumerable which the INotifyDataErrorInfo.GetErrors will return. This works pretty well. When I write text less than 3 characters, the textbox turns red. That is exactly what I expect.
Now I want the MainWindow's viewmodel to set this textbox. To do that, the Username field should be a dependency property so I can bind to it. But the problem is that I can't validate it now. I made a dependency property and tried validating it at the ValidateValueCallback but INotifyDataErrorInfo members are not static. So I can't reach them. What should I do?
Place Username within MainViewModel and inside of UserControl bind to it using RelativeSource binding like
"{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Username}"
You can replace explicit name indication with CallerMemberName attribute.
EDIT
When you define dependency property you can determine event which will be raised whenever value's change occurs. As a parameter it has reference to class, in which it is defined, as far as your project is concerned it would be your UserControl. You can then invoke any method on this object, no need to be static at all. Following code depicts the concept, not precise solution, adjust it to your requirement:
public static readonly DependencyProperty PropertyTypeProperty = DependencyProperty.Register(
"PropertyType", typeof (propertyType), typeof (PasswordBoxDP), new PropertyMetadata((x, y) =>
{
var userControlClass = x as UserControlClass;
userControlClass.Validate();
}));
private void Validate()
{
}
By the way, binding in your case will not be working. You defined DataContext refering to itself so when you set a binding on your dependency property it will kick off seeking within UserControl.
I'm fairly new to WPF and I have some problems getting databinding to work as I want. I've written a user control which contains a TextBox whose Text-Property I want to bind to a property of my UserControl, which I want to bind again to something else.
What am I missing?
XAML
<!-- User Control -->
<TextBox Text="{Binding Path=TheText}" />
<!-- Window -->
<WpfApplication1:SomeControl TheText="{Binding Path=MyStringProp}" />
C#
// User Control ----
public partial class SomeControl : UserControl
{
public DependencyProperty TheTextProperty = DependencyProperty
.Register("TheText", typeof (string), typeof (SomeControl));
public string TheText
{
get
{
return (string)GetValue(TheTextProperty);
}
set
{
SetValue(TheTextProperty, value);
}
}
public SomeControl()
{
InitializeComponent();
DataContext = this;
}
}
// Window ----
public partial class Window1 : Window
{
private readonly MyClass _myClass;
public Window1()
{
InitializeComponent();
_myClass = new MyClass();
_myClass.MyStringProp = "Hallo Welt";
DataContext = _myClass;
}
}
public class MyClass// : DependencyObject
{
// public static DependencyProperty MyStringPropProperty = DependencyProperty
// .Register("MyStringProp", typeof (string), typeof (MyClass));
public string MyStringProp { get; set; }
// {
// get { return (string)GetValue(MyStringPropProperty); }
// set { SetValue(MyStringPropProperty, value); }
// }
}
Best RegardsOliver Hanappi
PS: I've tried to implement the INotifyPropertyChanged interface on my user control, but it did not help.
You want to bind the Text property of your TextBox back to the TheText property of the UserControl it lives in, right? So you need to tell the binding where the property lives. There's a couple of ways to do this (you can do it with a RelativeSource using FindAncestor) but the easiest way is to give the UserControl a "name" in the XAML and bind using element binding:
<UserControl ...
x:Name="me" />
<TextBox Text="{Binding TheText,ElementName=me}" />
</UserControl>
Now your TextBox will reflect the value you've assigned (or bound) to your "SomeControl.TheText" property - you needn't change any of your other code, although you'll probably want to implement INotifyPropertyChanged on your underlying MyClass object so that the binding knows when the property has changed.
Matt has provided a solution to your problem. Here is a little more explanation and a hint to stop this problem in future.
As SomeControl.DataContext is set in the SomeControl constructor, the window's binding TheText="{Binding Path=MyStringProp}" has a Source of type SomeControl, not MyClass as you intended.
Any bindings that fail at runtime cause debug messages to be logged to the output panel of Visual Studio. In this case, you would have seen that no such property 'MyStringProp' exists on object of type 'SomeControl', which should have raised your suspicions.
I think everyone finds WPF data binding takes some time to learn and especially to debug, but persevere. Data binding in WPF is really fantastic, and I still get a kick out of knowing how easily it makes the data on my UIs stay up to date.
Can an object created in the code (ie C#) be used for binding in the XAML?
For example:
public class MyForm
{
private MyComplexObject complexObject;
public MyForm()
{
InitializeComponent();
}
public OnButtonClick(object sender, RoutedEventArgs e)
{
complexObject = new MyComplexObject();
}
}
complexObject is not created till a button is clicked. But once that button is clicked I would like to have a text box that is bound to complexObject.ID start showing the Id.
I would like to do that in the XAML if that is possible.
Can this be done? If so, how?
Yes, this can be done, binding to a property that you update with the desired value. I'd suggest you look into the MVVM pattern (Model-View-ViewModel), which is really useful for structuring this nicely working with WPF. Check out this video for a nice overview:
MVVM video
Using MMVM you would create a class which would be the ViewModel class. This one would typically be set to the DataContext of the View. Having done so you could add dynamic references to the other properties of the class, e.g. binding your text field to some property holding the Id og the ComplexObject. If your ViewModel class had a property ComplexObject, which again had a property ID you'd simply bind to the object like this:
<TextBlock Text="{Binding ComplexObject.ID}" />
Having this you could trigger creation of your ComplexObject from mouse click, which you should ideally set up as a command binding. Also note that the ViewModel class (or whoever is holding the ComplexObject needs to notify the View when the object has been set. This can either be done by making the ComplexObject a DependencyProperty or by making the class holding the property implement the INotifyPropertyChanged interface - giving it the PropertyChanged function to trigger the changed event. I prefer the latter.
One possibility would be to have your XAML bind to a property on your code behind. The getter for that property would return complexObject.ID, if complexObject != null. Otherwise, it returns something "default", whether that's null or 0 or default(ID's type). Similarly, the setter for that property would assign value to complexObject.ID if complexObject is, again, not null.
public int ID
{
get
{
if (complexObject != null)
return complexObject.ID;
return 0; // or null or some appropriate default
}
set
{
if (complexObject != null)
complexObject.ID = value;
}
}