Twoway-bind view's DependencyProperty to viewmodel's property?
I use Caliburn.Micro for separating the ViewModel from the View. Still, it might work the same way in MVVM. I guess MVVM sets the view's DataContext
property to the instance of the ViewModel, either.
VIEW
// in the class of the view: MyView
public string ViewModelString // the property which stays in sync with VM's property
{
get { return (string)GetValue(ViewModelStringProperty); }
set
{
var oldValue = (string) GetValue(ViewModelStringProperty);
if (oldValue != value) SetValue(ViewModelStringProperty, value);
}
}
public static readonly DependencyProperty ViewModelStringProperty =
DependencyProperty.Register(
"ViewModelString",
typeof(string),
typeof(MyView),
new PropertyMetadata(OnStringValueChanged)
);
private static void OnStringValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
// do some custom stuff, if needed
// if not, just pass null instead of a delegate
}
public MyView()
{
InitializeComponent();
// This is the binding, which binds the property of the VM
// to your dep. property.
// My convention is give my property wrapper in the view the same
// name as the property in the VM has.
var nameOfPropertyInVm = "ViewModelString"
var binding = new Binding(nameOfPropertyInVm) { Mode = BindingMode.TwoWay };
this.SetBinding(SearchStringProperty, binding);
}
VM
// in the class of the ViewModel: MyViewModel
public string ViewModelStringProperty { get; set; }
Note, that this kind of implementation lacks completely of implementation of the INotifyPropertyChanged
interface. You'd need to update this code properly.
Writing back a dependency property value to the view model in two-way binding
Unless I'm overlooking something, you just need to do:
private JSValue MapZoom_OnMapZoomChanged(JSValue[] arguments)
{
string newZoom= arguments[0];
this.Zoom = newZoom; // here
return null;
}
The dependency property pattern is setup so that from the outside you can just treat Zoom as a normal c# property (setter and getter). If you look at the setter for that property it calls SetValue(), which is on the DependencyObject base class and does the magic to notify the binding engine of the change (also see: Register())
Update
A simple way to prevent the change from sending the update to the map when the map was the one providing the change in the first place:
private bool IsMapUpdateSuppressed = false;
private JSValue MapZoom_OnMapZoomChanged(JSValue[] arguments)
{
string newZoom= arguments[0];
this.IsMapUpdateSuppressed = true;
this.Zoom = newZoom;
this.IsMapUpdateSuppressed = false;
return null;
}
private static void OnZoomPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
GoogleMap control = source as GoogleMap;
if (!control.IsMapUpdateSuppressed)
{
control.SetZoom(e.NewValue.ToString());
}
}
This allows the DP value to change, process, and notify the binding engine as usual, but just prevents the call to SetZoom
Two Way binding to a Dependency Property in a User Control and call a method
Implement INotifyPropertyChanged
in your ViewModel
public class Sample : INotifyPropertyChanged
{
private string input = string.Empty;
public string Input
{
get
{
return input;
}
set
{
input = value;
NotifyPropertyChanged("Input");
InputChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In your case, you can do it in the code behind of your usercontrol
WPF - How to keep Dependency Property and View Model property in sync?
I can see that your code is passing the IntValue from MainWindowViewModel's property to IncrementIntView's dependency property to IncrementIntViewModel's property.
The increment button is updating the IncrementIntViewModel's IntValue property. Unfortunately, whatever happens in the IncrementIntViewModel is not being reflected back to the IncrementIntView's IntValue dependency property. The TwoWay Mode is not between IncrementIntView's dependency property and IncrementIntViewModel's property, but it is between MainWindowViewModel's property to IncrementIntView's dependency property.
The easy solution: Bind the MainWindow's Label to IncrementIntViewModel's IntValue property without bothering the View's property.
<local:IncrementIntView x:Name="iiv" IntValue="{Binding IntValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ElementName=ViewModel}" />
<Label Content="{Binding DataContext.IntValue, ElementName=iiv}" />
<!--You need to specify DataContext.IntValue, because you have same name for both view's dependency property and viewmodel's property-->
Here you can see that MainWindowViewModel's IntValue is not that important, because it just passes the value to IncrementIntViewModel once and never have the value updated ever.
The other solution: You need to trigger value change back to MainViewModel's property.
First thing first, there is no connection between MainViewModel and IncrementIntViewModel. One solution is to make the MainViewModel to be singleton, so that when increment is done inside the IncrementIntViewModel, you want to update the MainViewModel's property as well.
In MainViewModel.cs
public static MainWindowViewModel SingletonInstance { get; set; }
public MainWindowViewModel()
{
if (SingletonInstance == null)
{
SingletonInstance = this;
}
}
In IncrementIntViewModel.cs
public void IncrementInt()
{
IntValue++;
MainWindowViewModel.SingletonInstance.IntValue = IntValue;
}
The other other solution: Similar to the above solution, but we don't need to make Singleton instance of MainWindowViewModel, because MainWindow is singleton to begin with.
In IncrementIntViewModel.cs
public void IncrementInt()
{
IntValue++;
((MainWindowViewModel)App.Current.MainWindow.DataContext).IntValue = IntValue;
}
If your intention is to update the IntValue from IncrementViewModel's property to IncrementView's dependency property, then you might ask why you need to do this, because MVVM is supposed to separate between V and VM. V should be looking to VM, but not the other way around.
WPF CustomControl unable to bind dependency property two way to view model
The reason for why you code does not work as expected is that you're assigning new value to your StringProperty
while still handling the previous change to that property. I don't really know the mechanics behind that (possibly it's some kind of mechanism meant to prevent potentially infinite recursive calls?), but I am 100% that that is the culprit.
To solve your problem it is sufficient to defer new value assignment until the control is returned from your handler, which can be easily achieved by using the Dispatcher
associated with your control:
private static void PropertyChangedCallback(DependencyObject dO,
DependencyPropertyChangedEventArgs e)
{
var textBox = (CustomTextBox) dO;
if (textBox.StringProperty == null)
return;
DoSomething(textBox.StringProperty)
//following lambda will be queued for execution rather than executed immediately
//and is guaranteed to be executed after this handler has finished
textBox.Dispatcher.InvokeAsync(() => textBox.StringProperty = null);
}
If you're using .NET version prior to 4.5 you will need to use Dispatcher.BeginInvoke
method instead.
WPF usercontrol Twoway binding Dependency Property
Bind the TextBox.Text
property in the UserControl to its SampleProperty
like this:
<TextBox Text="{Binding SampleProperty,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Now you could simply remove your OnSamplePropertyChanged
callback.
You might also register SampleProperty
to bind two-way by default like this:
public static readonly DependencyProperty
SamplePropertyProperty = DependencyProperty.Register(
"SampleProperty", typeof(string), typeof(UserControl1),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
Bind ViewModel's property to UserControl's child control and itself DependencyProperty
The view model property setter is not implemented correctly.
It should look like shown below, i.e. use the correct property name nameof(VoltInVm)
and probably not check m_V != value
before calling Set
, because that is already done by the Set
method.
private int voltInVm;
public int VoltInVm
{
get { return voltInVm; }
set { Set<int>(ref voltInVm, value, nameof(VoltInVm)); }
}
When you now bind the UserControl's property like
<local:Unit Volt="{Binding VoltInVm}"/>
the PropertyChangedCallback of the Volt
dependency property will be called each time the VoltInVm
property changes its value.
It is however unclear what (o, e) => ((Unit)o).OnVoltChanged(o, e)
in the dependency property registration is supposed to be. It should certainly look like this:
(o, e) => ((Unit)o).OnVoltChanged((int)e.NewValue)
and the method should be declared with an int
argument instead of double
:
private void OnVoltChanged(int volt) ...
Or - certainly better - change all voltage property types to double
, in the control and in the view model.
Related Topics
Are P/Invoke [In, Out] Attributes Optional for Marshaling Arrays
Regex:How to Get Words from a String (C#)
Irregular Shaped Windows Form (C#)
Xamarin Android Alarm Manager Issue
Piecewise Linear Integer Curve Interpolation in C#/Unity3D
How to Get All Input Elements in a Form with HTMLagilitypack Without Getting a Null Reference Error
How to Remove Specific Style Tag in HTML Using C#
Is There a Linux Equivalent of Dllimport in .Net Core
C# Async/Await Progress Event on Task<> Object
Load Different CSS File Based on Browser
Reading Data from CSV to Screen Output
Mvc-Web API: 405 Method Not Allowed
How to Convert Style Text to C# Object Such as Class/Hashtable/Collection