how to handle event for userControl properties? - c#

I have a property for userControl like this:
public enum Mode { Full, Simple }
public Mode NavigatorMode { get; set; }
and now, I need to write an event, when user change the property (NavigatorMode) form Full mode to simple mode, or reverse
how can I do that?

Implement INotifyPropertyChanged to your class:
public class YourClass : INotifyPropertyChanged
{
// Your private variable
private Mode mode;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public YourClass()
{
}
public Mode NavigatorMode
{
get { return mode; }
set
{
mode = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged(mode);
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(Mode modeParam)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(modeParam));
}
}
}

How about implementing the INotifyPropertyChanged interface in your control?
Or simply writing a custom event:
public event EventHandler<Mode> ModeChanged;
public Mode NavigatorMode
{
get { return _navigatorMode; }
set
{
_navigatorMode = value;
if(ModeChanged != null)
ModeChanged(this, _navigatorMode);
}
}
And outside your usercontrol you can handle that event and do something based on the mode.

Related

PropertyChanged WPF MVVM Light

I am using MVVM light in WPF. The Model class properties are constantly changed due to changes of the underlying data-access layer.
Model:
public class SBE_V1_Model : ViewModelBase
{
public SBE_V1_Model(String name)
{
Name = "MAIN." + name;
SetupClient();
}
private void SetupClient()
{
client = new ConnectionHelper(this);
client.Connect(Name);
}
public Boolean Output
{
get
{
return _Output;
}
set
{
if (value != this._Output)
{
Boolean oldValue = _Output;
_Output = value;
RaisePropertyChanged("Output", oldValue, value, true);
}
}
}
}
If Output property changes, then bindings will be notified, so this works. But what is the correct way to update the property from the data-access source, which knows the new value?
public class ConnectionHelper : ViewModelBase
{
public Boolean Connect(String name)
{
Name = name;
tcClient = new TcAdsClient();
try
{
dataStream = new AdsStream(4);
binReader = new AdsBinaryReader(dataStream);
tcClient.Connect(851);
SetupADSNotifications();
return true;
}
catch (Exception ee)
{
return false;
}
}
private void tcClient_OnNotification(object sender, AdsNotificationEventArgs e)
{
String prop;
notifications.TryGetValue(e.NotificationHandle, out prop);
switch (prop)
{
case "Output":
Boolean b = binReader.ReadBoolean();
RaisePropertyChanged("Output", false,b, true);
break;
}
}
}
Why doesnt the RaisePropertyChanged call in connectionhelper update the property of the model? If this is the wrong way, should I set up some kind of listener?
In your SBE_V1_Model class you should subscribe to receive PropertyChange notifications from the ConnectionHelper ViewModel.
// Attach EventHandler
ConnectionHelper.PropertyChanged += OnConnectionHelperPropertyChanged;
...
// When property gets changed, raise the PropertyChanged
// event of the ViewModel copy of the property
OnConnectionHelperPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Something") //your ConnectionHelper property name
RaisePropertyChanged("Ouput");
}
Also look into MVVM light messenger. Here is a link you might be interested from StackOverflow.
You should only use PropertyChanged in the ViewModel, not in the Model.
You can use PropertyChanged in Models only in special times.
RaisePropertyChanged("Output", false,b, true);
In that PropertyChanged you are always saying that Output Property was changed.
I recommend you implement INotifyPropertyChanged
class MyClass : INotifyPropertyChanged
{
public bool MyProperty{ get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
To notify any property change you have to use:
OnPropertyChanged("MyProperty");

How to notify a bound element oh a value change when value derives from model?

I have the visibility of a progress bar bound to The following property within my viewmodel:
public string CalcProgVisibility
{
get
{
return Calculation.CalcProgVisibility;
}
set
{
}
}
Calculation is my model, which can change the value. When the value changes within the model, what do I need to do to make sure the view is aware of this change?
EDIT:
Here is the property within my model too. I am using onpropertychanged but its not making it to the view.
I am changing the value within the model, the view is bound to my viewmodel and the viewmodel si trying to return a value taken from the model. I am updating the value on the model, and cannot push the fact that it has updated the value all the way down to the view, I can only get the viewmodel to see it has changed...
I updated the entire code. I hope it's clear now.
Define your control BindingMode = TwoWay
<TextBox Visibility="{Binding Path=CalcProgVisibility, Mode=TwoWay}"...
and call the OnPropertyChanged method on the setter of the property in your view model and also in your model
//Model
public class Calculation : INotifyPropertyChanged
{
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
//ViewModel
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(Calculation model)
{
this.CalcProgVisibility = model.CalcProgVisibility;
model.PropertyChanged += (s, e) => UpdateEntity(s as Calculation);
}
private void UpdateEntity(Calculation source)
{
CalcProgVisibility = source.CalcProgVisibility;
}
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Your Viewmodel has to implement the INotifyPropertyChanged Interface. To fire it in your case your viewmodel must also be aware of changes in your model object. So your model object could also implement INotifyPropertyChanged, or you use some form of the observer pattern.
If your model implements INotifyPropertyChanged, your viewmodel must manually register for this event and implement an handler. This could in turn trigger the PropertyChange event of the viewmodel then.
Another but in my opinion ugly way would be to scan (per timer or background thread) through your viemodel and check if a value changed since the last scan and then trigger a property changed event.
The first solution could look like this:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StackOverflow
{
[TestClass]
public class IntegrationTest
{
[TestMethod]
public void NotifyPropertyChangeShouldFireOnViewModelWhenModelChanges()
{
//Arrange
Model model = new Model();
ViewModel sut = new ViewModel(model);
bool notifyPropertyChangeOnViewModelWasCalled = false;
sut.PropertyChanged += (sender, e) => { notifyPropertyChangeOnViewModelWasCalled = true; };
//Act
model.CalcValue = 4711;
//Assert
Assert.IsTrue(notifyPropertyChangeOnViewModelWasCalled, "NotifyPropertyChange was not fired on ViewModel");
}
}
public class ObjectWithNotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Model : ObjectWithNotifyPropertyChanged
{
private double calcValue;
public double CalcValue
{
get
{
return calcValue;
}
set
{
if (calcValue != value)
{
calcValue = value;
RaisePropertyChanged();
}
}
}
}
public class ViewModel : ObjectWithNotifyPropertyChanged
{
public ViewModel(Model model)
{
this.model = model;
model.PropertyChanged += model_PropertyChanged;
}
void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "CalcValue":
RaisePropertyChanged("CalcValue");
break;
}
}
private Model model;
public double CalcValue
{
get
{
return model.CalcValue;
}
}
}
}

INotifyPropertyChanged not firing on ViewModel inside ItemsControl

I'm using ObservableCollection<MyItemViewModel> myItemVMList as the ItemsSouce. I am able to bind Command perfectly but the INotifyPropertyChanged isn't working. Here's my code:
public class MyItemViewModel: INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
public MyItem MyItem { set; get; }
private RelayCommand _ChangeMyItemPropertyValue;
public ICommand ChangeMyItemPropertyValueCommand {
get {
if (_ChangeMyItemPropertyValue == null) _ChangeMyItemPropertyValue = new RelayCommand(o => ChangeMyItemPropertyValue());
return _ChangeMyItemPropertyValue;
}
}
private ChangeMyItemPropertyValue() {
MyItem.SomeProperty = someDifferentValue;
// NEITHER OF THESE CALLS WORK
OnPropertyChanged("MyItem.SomeProperty");
OnPropertyChagned("SomeProperty");
}
}
Needless to say, the binding is set as Content="{Binding MyItem.SomeProperty}" inside the DataTemplate, and it shows the correct value. Problem is it isn't updated when I run the function.
SideNote: If I implement the INotifyPropertyChanged inside MyItem it works, but I want it on the ViewModel.
If I implement the INotifyPropertyChanged inside MyItem it works, but I want it on the ViewModel
Yes, because that's how it's designed. How it's supposed to know it should listen to your ViewModel's property changed event? It doesn't bind to it, it binds to the model, so it listens to the changes on the model.
You have two choices basically:
Implement INotifyPropertyChanged on MyItem
Bind to the ViewModel
Content="{Binding SomeProperty}"
And add a wrapper property:
public string SomeProperty
{
get { return MyItem.SomeProperty; }
set
{
MyItem.SomeProperty = value;
OnPropertyChanged("SomeProperty");
}
}
You should prefer binding to the ViewModel if you want to follow MVVM practices.
Side note: If you add [CallerMemberName] to OnPropertyChanged like this:
protected void OnPropertyChanged([CallerMemberName] string name = null) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
You'll be able to skip the property name altogether:
public string SomeProperty
{
get { return MyItem.SomeProperty; }
set
{
MyItem.SomeProperty = value;
OnPropertyChanged(); // <-- no need for property name anymore
}
}

How to implement INotifyPropertyChanged for derived classes?

I have a base class:
public class PersonBaseClass : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
if (value != name)
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
}
and a derived class
public class TeacherClass : PersonBaseClass, INotifyPropertyChanged
{
private string id;
public string Id
{
get { return id; }
set
{
if (value != id)
{
id = value;
NotifyPropertyChanged("Id");
}
}
}
}
and this magic code at the end of each one above!
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then I show a list of Teachers collection in a list in xaml. Now if I change Id, changes will appear for user, but changes in Name which is a property in base class doesn't appear. In debug, I see after setting Name value, the handler inside NotifyPropertyChanged method is null which seems it is the problem.
How can I solve it to changes of base class also appear in the list?
Have only PersonBaseClass implementing INotifyPropertyChanged and make NotifyPropertyChange as protected so you can call it from child classes. There is not need to implement it twice. That should fix the problem as well.
Your "magic code" section should only be in PersonBaseClass. You can make the NotifyPropertyChanged function protected so that the same function can be called from TeacherClass as well.

Adding property changed event to a custom control

For an application I have to use a custom button that react when one of its properties value is being changed. I addded a field named Data to the new button:
public class ButtonData
{
public string Name;
public string Color;
//And more stuff...
}
Then I have the folowing code for new button, I want it to be able to update itself (change background color and some other stuff) whenever its Data property gets updated from somewhere in application. I found some ideas about implementing INotifyPropertyChanged interface and I set it up in my custom button like this:
public partial class ButtonPin : Button, INotifyPropertyChanged
{
private ButtonData _data;
public ButtonData Data
{
get { return _data; }
set
{
if (value == _data) return;
_data = value;
OnPropertyChanged("Data");
}
}
private bool _buttonDataAdded;
public ButtonPin()
{
InitializeComponent();
}
public ButtonPin(ButtonData data)
{
Data = data;
_buttonDataAdded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Now I am not sure how to use this! For example, if the Color in Data objects gets changed
somehow somewhere and its get assigned to current button's Data field, I want this button to change its background color. Something like
var data = new ButtonData();
data.Name = "Hi!";
data.Color = Color.Red;
buttonPin1.Data = data; //Here I need the changes to occur
You have implemented the interface INotifyPropertyChanged on the ButtonPin class, not on the ButtonData class, and you want to detect a change on an object of type ButtonData, thus you need to implement the interface INotifyPropertyChanged on the ButtonData class.
To detect the change you need to hook up to the PropertyChanged event of the ButtonData object in the setter of the ButtonPin.Data property. Something like this.
private bool _data;
public ButtonData Data {
get { return _data; }
set {
if (value != _data) {
// Unhook previous events
if (_data != null)
_data.PropertyChanged -= HandleButtonDataPropertyChanged;
// Set private field
_data = value;
// Hook new events
if (_data != null)
_data.PropertyChanged += HandleButtonDataPropertyChanged;
// Immediate update since we have a new ButtonData object
if (_data != null)
Update();
}
}
}
private void HandleButtonDataPropertyChanged(object sender, PropertyChangedEventArgs e) {
// Handle change in ButtonData
Update();
}
private void Update() {
// Update...
}

Categories

Resources