Background:
I have a numericupdown and its 'Value' property is bound to an object's property (a Decimal, of course). The object's class implements INotifyPropertyChanged and a propertychanged event is raised when the property changes its value, the binding is set to updatemode.OnPropertyChanged.
private System.Windows.Forms.BindingSource myClassbindingSource;
In my Form's OnLoad, I have the following:
myClassbindingSource.Add( MyObject1 ); //(MyObject1 is an instance of MyClass)
base.OnLoad( e );
the auto-generated binding (after I changed its DataBinding's advanced property 'Data Source Update Mode' to 'OnPropertyChanged'):
this.NumericUpDown1.DataBindings.Add(new System.Windows.Forms.Binding(
"Value", this.myClassBindingSource, "MyProperty", true,
System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
My Class:
public class MyClass : INotifyPropertyChanged {
private Decimal myValue;
public Decimal MyProperty{
get { return myvalue; }
set {
myvalue = value;
NotifyPropertyChanged( "MyProperty" );
}
...
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged( String info ) {
var pceHandler = PropertyChanged;
var pceArgs = new PropertyChangedEventArgs( info );
if( pceHandler != null )
pceHandler( this, pceArgs );
}
}
Problem:
Now, when the object's property changes from other parts of the code, the NumericUpdown is not displaying its new value.
When I debug my code I can see that, when the property changes, the PropertyChanged event fires and the 'Value' property of my NUD gets the proper value, but the 'Text' property has its default "0.000" value.
There are other controls whos bindings work fine, e.g. checkboxes vs Booleans, labels vs strings. But not my NUD bound to a Decimal.
Question:
How can I get the 'Text' property to update when the 'Value' property does, so the proper value is displayed?
Or rather, why does the 'Text' property NOT update?
Are there properties that can control how this is handled?
Related
I use MVVM Light.
My Model inherits from MVVM Light's ObservableObject.
The Model has a property X
public float X
{
get
{
return x_;
}
set
{
Set<float>(() => this.X, ref x_, value);
}
}
In my ViewModel I have a property X
public float X
{
get
{
myModel.X;
}
}
My View has a label where the content is binding to the ViewModel property X.
(DataContext is set to ViewModel)
When the value of property X in Model is updated from code, the label in the view never gets updated.
Question what is the recommended way to make sure that the View correctly reflects the updated value?
(If I make a property in my ViewModel that returns the Model, I could bind direclty to Model.X in my View. But I want my View to bind to the ViewModel not directly down to the Model)
When myModel.X fires the PropertyChanged event, that does not automatically fire the ViewModel's PropertyChanged.
Moreover, if your Model class already implements INotifyPropertyChanged, there is no need to duplicate the X property in the ViewModel class.
Just turn the myModel member into a public property
public class ViewModel
{
public Model MyModel { get; set; }
}
and bind to it by {Binding MyModel.X}.
I want to set the DataContext of a UserControl to a new value, but I am not being successful at it.
I have an UserControl whose DataContextChanged event would write something to Console.
I have an instance of that UserControl in XAML, with DataContext bound to the correct property;
I have a RaisePropertyChanged (from MVVM-Light) with a breakpoint configured to write to Console too.
The fact is: Even if the RaisePropertyChanged fires (as shown by breakpoint), the DataContextChanged handler is not called (as shown either by breakpoint or Console.WriteLine)
IMPORTANT: When the UserControl is first loaded, it displays the value correctly (that is, it "gets" the value from ViewModel) and the messagem from DataContextChanged is printed to Console. The problem is that I can't get the DataContextChanged handler to fire when calling PontoCanalZoom setter, even if RaisePropertyChanged fires appropriately.
ViewModel:
public class AnáliseViewModel : ObservableObject
{
public PontoAnáliseViewModel PontoCanalZoom
{
get
{
if (_pontoCanalZoom == null)
_pontoCanalZoom = PontosAtivos?.FirstOrDefault();
return _pontoCanalZoom;
}
set
{
_pontoCanalZoom = value;
RaisePropertyChanged(() => PontoCanalZoom);
}
}
private PontoAnáliseViewModel _pontoCanalZoom = null;
}
public ICommand ComandoSelecionarCanalNavegação => new RelayCommand<PontoAnáliseViewModel>(SelecionarCanalNavegação);
private void SelecionarCanalNavegação(PontoAnáliseViewModel ponto)
{
// This calls the setter _and_ raises PropertyChanged event.
PontoCanalZoom = ponto;
}
Instance of Control (AreaPlotagemAnáliseEMG):
<plotagem:AreaPlotagemAnáliseEMG
DataContext="{Binding PontoCanalZoom, Mode=OneWay}"/>
Control definition:
public partial class AreaPlotagemAnáliseEMG : AreaPlotagemBase
{
public AreaPlotagemAnáliseEMG()
{
InitializeComponent();
DataContextChanged += (a, b) => System.Console.WriteLine("Trocou Data Context!!!!!!!!!!!!");
}
}
I've seen enough examples with binding to property A with a datacontext where A exists in the viewModel class. Now what if in the viewModel I do not have any property A, instead I create some calss B that contains property A, then how to set up a binding here?
Let's say in xaml<TextBlock Text="{Binding Bid}"..> and In the viewModel's constructor I set up
Quote b = new Quote();
HttpClient.QuoteMap.Add(1,b);
HttpClient.Socket.reqMktdata(1,contract,..)
So b keeps updating its Bid and Ask... The thing is I don't see how to set a binding to b's Bid. For listview or DataGrid I can see how to do it as there's a property called itemsource to specify the data binding source and for each column different property is bind to any property if needed.
The class itself needs to be a property.
<TextBlock Text="{Binding Path=MyClassToBind.PublicProperty}"
private MyClass myClass = new MyClass();
public MyClass MyClassToBind
{ get { return myClass; } }
In your viewmodel create a property A which has the notify event on its change, but get the data from the B instance. If B has INotifyPropertyChanged, then subscribe to those changes and when B event fires that a change has occurred, post the notified change for your property A such as
OnPropertyChanged("A");
That way you can have related data which exists elsewhere but still updates the view accordingly.
This will update when the B property Data changes, to the property A on the MVVM.
class MyMVVM : INotifyPropertyChanged
{
private TheBClass B { get; set { OnPropertyChanged("A"); } }
public string A
{
get { return B.Data; }
set { OnPropertyChanged("A"); }
}
public MVVM(TheBClass bInstance;)
{
B = bInstance;
B.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "Data")
OnPropertyChanged("A");
};
}
}
Short Version
If I update the Model object that my ViewModel wraps, what's a good way to fire property-change notifications for all the model's properties that my ViewModel exposes?
Detailed Version
I'm developing a WPF client following the MVVM pattern, and am attempting to handle incoming updates, from a service, to data being displayed in my Views. When the client receives an update, the update appears in the form of a DTO which I use as a Model.
If this model is an update to an existing model being shown in the View, I want the associated ViewModel to update its databound properties so that the View reflects the changes.
Let me illustrate with an example. Consider my Model:
class FooModel
{
public int FooModelProperty { get; set; }
}
Wrapped in a ViewModel:
class FooViewModel
{
private FooModel _model;
public FooModel Model
{
get { return _model; }
set
{
_model = value;
OnPropertyChanged("Model");
}
}
public int FooViewModelProperty
{
get { return Model.FooModelProperty; }
set
{
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
}
}
The Problem:
When an updated model arrives, I set the ViewModel's Model property, like so:
instanceOfFooVM.Model = newModel;
This causes OnPropertyChanged("Model") to fire, but not OnPropertyChanged("FooViewModelProperty"), unless I call the latter explicitly from Model's setter. So a View bound to FooViewModelProperty won't update to display that property's new value when I change the Model.
Explicitly calling OnPropertyChanged for every exposed Model property is obviously not a desirable solution, and neither is taking the newModel and iterating through its properties to update the ViewModel's properties one-by-one.
What's a better approach to this problem of updating a whole model and needing to fire change notifications for all its exposed properties?
According to the docs:
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
One option is to listen to your own events, and make a helper routine to raise the other notifications as required.
This can be as simple as adding, in your constructor:
public FooViewModel()
{
this.PropertyChanged += (o,e) =>
{
if (e.PropertyName == "Model")
{
OnPropertyChanged("FooViewModelProperty");
// Add other properties "dependent" on Model here...
}
};
}
Whenever your Model property is set, subscribe to its own PropertyChanged event. When your handler gets called, fire off your own PropertyChanged event. When the Model is set to something else, remove your handler from the old Model.
Example:
class FooViewModel
{
private FooModel _model;
public FooModel Model
{
get { return _model; }
set
{
if (_model != null)
{
_model.PropertyChanged -= ModelPropertyChanged;
}
if (value != null)
{
value.PropertyChanged += ModelPropertyChanged;
}
_model = value;
OnPropertyChanged("Model");
}
}
public int FooViewModelProperty
{
get { return Model.FooModelProperty; }
set
{
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
}
}
private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Here you will need to translate the property names from those
// present on your Model to those present on your ViewModel.
// For example:
OnPropertyChanged(e.PropertyName.Replace("FooModel", "FooViewModel"));
}
}
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(String.Empty))
For VB.net if anybody else needs it. If you have already implemented "INotifyPropertyChanged" then the last line is all you need.
I'm trying to bind my window title to a property in my view model, like so:
Title="{Binding WindowTitle}"
The property looks like this:
/// <summary>
/// The window title (based on profile name)
/// </summary>
public string WindowTitle
{
get { return CurrentProfileName + " - Backup"; }
}
The CurrentProfileName property is derived from another property (CurrentProfilePath) that is set whenever someone opens or saves profile. On initial startup, the window title is set properly, but when ever the CurrentProfilePath property changes, the change doesn't bubble up to the window title like I expected it would.
I don't think I can use a dependency property here because the property is a derived one. The base property from which it is derived is a dependency property, but that doesn't seem to have any effect.
How can I make the form title self-updating based on this property?
That's because WPF has no way of knowing that WindowTitle depends on CurrentProfileName. Your class needs to implement INotifyPropertyChanged, and when you change the value of CurrentProfileName, you need to raise the PropertyChanged event for CurrentProfileName and WindowTitle
private string _currentProfileName;
public string CurrentProfileName
{
get { return __currentProfileName; }
set
{
_currentProfileName = value;
OnPropertyChanged("CurrentProfileName");
OnPropertyChanged("WindowTitle");
}
}
UPDATE
Here's a typical implementation of INotifyPropertyChanged :
public class MyClass : INotifyPropertyChanged
{
// The event declared in the interface
public event PropertyChangedEventHandler PropertyChanged;
// Helper method to raise the event
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName);
}
...
}