In my sample, I had used property changed event. in this handler, I had an declare a method. each and every time that method fire when changing the property,
in That method, I had set the value to the property. when I set the value, it is a call to the event handler. so it's executing the circular. how to make the method call only one time?
private string name;
public string Name
{
get { return name; }
set
{
name= value;
Name.PropertyChanged+=(s,e)=>
{
Changed(s as string);
};
}
}
private void changed(string name)
{
Name = name;
}
in this code, the changed property call every time.
The basic thing is nameof keyword:
changed(nameof(Name));
You can go futher and omit the need of specifying name at all by adding the following CallerMemberName attribute to your method's parameter:
private void changed([CallerMemberName]string name=null){}
In this case you can call this method without property name: changed();
I'd hazard a guess you want to implement MVVM. The most elegant way to implement it so far is to have the following base class:
public abstract class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetPropertyAndNotifyIfNeeded<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
NotifyPropertyChanged(propertyName);
return true;
}
protected void NotifyPropertyChanged([CallerMemberName]string name=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Implementation of your MVVM:
class Class1:Observable
{
public Class1()
{
}
string propertyValue;
public string Property
{
get => propertyValue;
set => SetPropertyAndNotifyIfNeeded(ref propertyValue, value);
}
}
As per your code remove subscription from your property to avoid recursive loop:
Name.PropertyChanged+=(s,e)=>
In your property call changed(nameof(Name));
class a:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
....
private string name;
public string Name
{
get { return name; }
set
{
if (name!=value)
{
name= value;
changed();
}
}
}
private void changed([CallerMemberName]string name=null)
{
PropertyChanged.?Invoke(this, new PropertyChangedEventArgs(name));
}
...
}
As I can see from your question, and I would guess, you would like to raise a Property Changed on it and set a value. Basically implement MVVM.
A really simple and quick way, would be to use a already existing Nuget package, which would simplify your job.
One that you can use is GalaSoft.MvvmLight.
After you add the Nuget package, you can just use it in the following way:
public string name;
public string Name
{
get { return name; }
set
{
name= value;
RaisePropertyChanged(nameof(Name));
}
}
RaisePropertyChanged, is going to Raise the PropertyChanged event for you (as the name itself suggests).
Related
In C#, I have a suffiently complex Model. I already have a WPF Client to manipulate that model. I'm using MVVM. All objects in that model support INotifyPropertyChanged and all properties that are collections support INotifyCollectionChanged.
Take this as a simplied example:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace CollectionTest1
{
public class PropertyChangedSupport : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChange([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Company : PropertyChangedSupport
{
private string name;
public String Name { get { return name; } set { name = value; FirePropertyChange(); } }
public ObservableCollection<Employee> Employees { get; } = new ObservableCollection<Employee>();
}
public class Employee : PropertyChangedSupport
{
private string name;
public String Name { get { return name; } set { name = value; FirePropertyChange(); } }
public ObservableCollection<PresentTimespan> PresentTimespans { get; } = new ObservableCollection<PresentTimespan>();
public Boolean IsPresentAt(DateTime t)
{
foreach (PresentTimespan pt in PresentTimespans)
{
if (pt.Start.CompareTo(t) <= 0 && pt.Finish.CompareTo(t) >= 0) return true;
}
return false;
}
}
public class PresentTimespan : PropertyChangedSupport
{
private string comment;
public String Comment { get { return comment; } set { comment = value; FirePropertyChange(); } }
private DateTime start;
public DateTime Start { get { return start; } set { start = value; FirePropertyChange(); } }
private DateTime finish;
public DateTime Finish { get { return finish; } set { finish = value; FirePropertyChange(); } }
}
public class CompanyStatusView : PropertyChangedSupport
{
private DateTime currentTime;
public DateTime CurrentTime { get { return currentTime; } set { currentTime = value; FirePropertyChange(); } }
private Company currentCompany;
public Company CurrentCompany { get { return currentCompany; } set { currentCompany = value; FirePropertyChange(); } }
public ObservableCollection<Employee> PresentEmployees { get; } = new ObservableCollection<Employee>();
public CompanyStatusView()
{
UpdatePresentEmployees();
}
private void UpdatePresentEmployees()
{
PresentEmployees.Clear();
foreach (Employee e in CurrentCompany.Employees) {
if (e.IsPresentAt(currentTime)) PresentEmployees.Add(e);
}
}
}
}
I'd like to have UpdatePresentEmployees called whenever there are changes in:
Collection Company.Employees.PresentTimespans
Property Company.Employees.PresentTimespans.Start
Property Company.Employees.PresentTimespans.Finish
Collection Company.Employees
Property CurrentTime
Property CurrentCompany
So it's basically any property or collection read by UpdatePresentEmployees.
My best solution so far included registering a lot of event handlers to all the objects mentioned above. That included to have a couple of Dictionary instances to track which added objects I have to subscribe to and especially which I have to unsubscribe from.
The most difficult and annoying part was to subscribe to all the PresentTimespan objects to listen for property changes and all the PresentTimespans collections of Employee to listen for collection changes.
My guess is that there has to be a better way to do this.
After all, in JFace (Java) there is a very interesting solution that uses ObservableTracker. So there you'd only provide the code for UpdatePresentEmployees and ObservableTracker tracks which objects have been read and automatically makes you listen for changes in any of these and also correctly unsubscribes from irrelevant objects. So there are better approaches to this problem in general. What is C# offering? Can it do better than my best solution I mentioned above? Can I avoid some of the boilerplate code? Can it be done with .net provided classes or do I need some additional classes/libraries?
Thanks for your kind help and advice in advance!
You could use BindingList instead of ObservableCollection and attach to the the ListChanged Event. But keep in mind that BindingList has some disadvantages like not being very fast. For further information this could be interesting: difference between ObservableCollection and BindingList
If you dont wanna use BindingList you have to wire your items with events.
As pointed out by Nikhil Agrawal, Rx or ReactiveUI is a good framework for my purpose. So I consider that to be a solution.
Why does C# make me do this:
public class SomeClass {
public event PropertyChangedEventHandler Changed;
public void OnChanged(object sender, PropertyChangedEventArgs e)
{
if (Changed != null)
Changed(sender, e);
}
[XmlIgnore]
private string _name;
public string Name {
get { return _name; }
set
{
_name = value;
OnChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
intead of something like this:
[GeneratesChangeNotifications]
public class SomeClass {
[GeneratesChangeNotification]
public string Name { get; set; }
}
I know you can do this with PostSharp and other 3rd party libraries... but something so integral and otherwise error prone (e.g. misspelling the name in the string), I think, should be built into the language... why doesn't Microsoft do this?... is there some purist language reason why this doesn't happen. It's a common enough need.
So far here is the best that I've come up with... I've written a class called NotifyPropertyChanged... it handles the property name, the test for if it's changed or not, the update of the local variable, and the notification of the change... and the SetProperty function is generic, so one function will work with all types of properties... works pretty well.
(Also notice you can call OnPropertyChanged with several property names if you like.)
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(params string[] props)
{
foreach (var prop in props)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public bool SetProperty<T>(ref T oldValue, T newValue, [CallerMemberName]string prop = "")
{
if (oldValue == null && newValue == null)
return false;
if (oldValue != null && oldValue.Equals(newValue))
return false;
oldValue = newValue;
OnPropertyChanged(prop);
return true;
}
}
Then use it like this:
public class MyClass : NotifyPropertyChanged
{
public string Text { get => _text; set => SetProperty(ref _text, value); }
string _text;
}
My RaisePropertyChanged always uses the caller member name, or if needed reflects the property name passed in in an expression (ie, if I need to make one property notify for another). You cannot always guarantee that all properties can notify, and being able to notify more than one at a time is therefore useful.
In addition, with the standard model, using an event allows anyone to subscribe, which an attributed model can not.
It is pretty straightforward to get a method caller, or even get the property name change by using compiler services, like this:
public class EmployeeVM:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName=null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// The compiler converts the above line to:
// RaisePropertyChanged ("Name");
}
}
private string _phone;
public string Phone
{
get { return _phone; }
set
{
_phone = value;
OnPropertyChanged();
// The compiler converts the above line to:
// RaisePropertyChanged ("Phone");
}
}
}
But is it possible to get the caller of the "set" function from inside the set itself? I don't know how you'd syntactically define it in that scope. AKA, who is calling Phone= ?
Look at StackFrame, in particular GetMethod that gives you method name (you'll need to pick one of previous stack frames, depending on if writing helper function to do so). Sample from the article:
StackTrace st = new StackTrace();
StackTrace st1 = new StackTrace(new StackFrame(true));
Console.WriteLine(" Stack trace for Main: {0}",
st1.ToString());
Console.WriteLine(st.ToString());
There are other similar questions that can be found by searching for StackFrame like How do I find the type of the object instance of the caller of the current function?
Unfortunately [CallerMemberName] AttributeUsage is set to AttributeTargets.Parameter, so it can only be used for parameters, like in method signatures
But you can use StackFrame like Alexei Levenkov mentioned
public string Phone
{
get { return _phone; }
set
{
string setterCallerName = new StackFrame(1).GetMethod().Name;
_phone = value;
OnPropertyChanged();
}
}
Is it posible to get a contact without using class Contacts and his SearchAsync method? I proceed to explain my problem.
I have an ObservableCollection
private ObservableCollection<ContactPictureItemModel> _ContactPictures;
being ContactPictureItemModel something like this
public class ContactPictureItemModel
{
private string _Email;
private byte[] _Picture;
private string _DisplayName;
public ContactPictureItemModel(string email, byte[] picture, string displayName)
{
this._Email = email;
this._Picture = picture;
this._DisplayName = displayName;
}
public string Email
{
get { return _Email; }
set { _Email = value; }
}
public byte[] Picture
{
get { return _Picture; }
set { _Picture = value; }
}
public string DisplayName
{
get { return _DisplayName; }
set { _DisplayName = value; }
}
}
Every object in this ObservableCollection represents a contact picture that application has "cached" everytime user has picked a contact from EmailAddressChooserTask.
I need when calling this method
public ContactPictureItemModel GetContactPictureItem(string email, string displayName)
{
ContactPictureItemModel contactPictureResult;
foreach (ContactPictureItemModel contact in ContactPictures)
{
if (email.Equals(contact.Key))
{
contactPictureResult = contact;
break;
}
}
if (contactPictureResult == null)
{
//Retrieve contact using "email" parameter
}
return contactPictureResult;
}
and contact is not found in the ObservableCollection, to be able to get the contact using parameters "email" and "displayName" without using any async task. I need the function retrieves the ContactPictureItemModel object.
Is that possible?
Thanks!
It's not possible to synchronously access a contact, since there's no API for it.
You could create the view model in a "loading" state and then "fill it in" when the async method completes. Just make sure the model class implements INotifyPropertyChanged
Thanks to Richard Szalay I found the solution. I'm gonna explain it to help everyone stuck like me in these questions (sorry about Java notation, code is shorter this way)
These are the private fields who has the object that represents a contact and his picture
public class ParticipantItemModel {
private string _Email;
private string _DisplayName;
private bool _Paid;
[XmlIgnore]
private BitmapImage _ContactPicture;
[...]
}
Every private field has his own property for getting and setting his value. Like this one
public string Email {
get { return _Email; }
set { _Email = value; }
}
When dealing with _ContactPicture its properties are a little special. For getting its value, if _ContactPicture is null and the picture isn't cached into the ObservableCollection, I search the contact using the Contacts.SearchAsync task and I return an "empty Image". When the contact is found (despite the view has already been loaded), I set the property ContactPicture with the correct image, raising the PropertyChangedEventArgs event (as you can see at its setter property)
[XmlIgnore]
public BitmapImage ContactPicture
{
get {
if (_ContactPicture != null) {
return _ContactPicture;
} else {
BitmapImage contactPictureSource = App.ContactPictures.GetContactPicture(Email, DisplayName);
if (contactPictureSource != null) {
return contactPictureSource;
} else {
Contacts contacts = new Contacts();
contacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
contacts.SearchAsync(DisplayName, FilterKind.DisplayName, Email);
return new BitmapImage();
}
}
}
set {
_ContactPicture = value;
//When _ContactPicture is setted, an event is raised by calling to NotifyPropertyChanged()
NotifyPropertyChanged("ContactPicture");
}
}
void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e) {
Contact contact = null;
foreach (var result in e.Results) {
foreach (ContactEmailAddress contactEmail in result.EmailAddresses) {
if (Email.Equals(contactEmail.EmailAddress)) {
contact = result;
this.ContactPicture = GetSourceImageFromContactPicture(contact.GetPicture());
break;
}
}
}
}
}
Also, the INotifyPropertyChanged interface must be implemented in order to raise the event PropertyChangedEventArgs. This event will make the application know the element (the contact's picture) has changed and it will refresh using the binded data
public class ParticipantItemModel : INotifyPropertyChanged {
[...]
// Declare the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
// NotifyPropertyChanged will raise the PropertyChanged event passing the
// source property that is being updated.
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
As result, the contact is shown without picture but almost automatically picture is loaded.
I hope it has been helpful
I have a really, really simple class and I am tried to use the get/set properties but they just aren't working for me... I am sure it is the most obvious thing that I am over looking but I just can't see why they aren't working. I have checked out the code that utilizes this class and its fine that I can see.
In the main code, if I type
Report r = new Report();
string str = "Taco";
r.displayName = str;
The report is declared alright and everything is set to empty strings or a new list or whatever the parameter's default is. But every time I ran this the displayName always remained blank after the code finished executing...
so I tried putting a stop point in the Class displayName set property at set {_displayName = displayName;} and the value always passed in (displayName) was an empty string.... even though the string clearly says "Taco" in the main code.... I have no idea but I am sure its right in my face. If you need more code I can provide it...
Report r = new Report();
string str = "Taco";
r.setReportDisplayName(str);
But for some reason the above works.
public class Report
{
private string _reportPath = string.Empty;
public string reportPath
{
get { return _reportPath; }
set { _reportPath = reportPath; }
}
private string _displayName = string.Empty;
public string displayName
{
get { return _displayName; }
set { _displayName = displayName; }
}
private List<parameter> _parameters = new List<parameter>();
public List<parameter> parameters
{
get { return _parameters; }
set { _parameters = parameters; }
}
public Report() { }
public Report(string path, string display, List<parameter> param)
{
_reportPath = path;
_displayName = display
_parameters = param;
}
public void setReportDisplayName(string str)
{
_displayName = str;
}
}
You are defining your properties incorrectly. This should be done as:
private string _displayName = string.Empty;
public string displayName
{
get { return _displayName; }
set { _displayName = value; }
}
That being said, if you are using this for Silverlight, you most likely will want to implement INotifyPropertyChanged. Without this, data binding will not reflect changes made in code.
To implement this interface, you'll need to add this implementation. A "standard" way to implement this is via:
public class Report : INotifyPropertyChanged
{
// Declare the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
// Raise the PropertyChanged event
protected void NotifyPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
At this point, you need to define your properties like:
private string _displayName = string.Empty;
public string DisplayName
{
get { return _displayName; }
set
{
if (_displayName != value)
{
_displayName = value;
NotifyPropertyChanged("DisplayName");
}
}
}
Doing this will allow you to data bind to your "Report" class. You may also want to consider using ObservableCollection<T> instead of List<T> for any collections you want to use with data binding.
You need to be assigning the value of the special variable value in your sets. value is what will be holding the (heh) value that was assigned to your property as it is passed into the set.
public string reportPath
{
get { return _reportPath; }
set { _reportPath = value; }
}