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; }
}
Related
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).
I can see this question has been asked before but nothing seems to work for me.
I have a wpf desktop app.
i have this comboBox:
<ComboBox ItemsSource="{Binding Users, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Value.Login"
SelectedValue="{Binding SelectedManagerUser,
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Value"
IsSynchronizedWithCurrentItem="True" />
The data source is a dictionary object:
public Dictionary<string,UserRecord> Users
{
get
{
//get data
}
set { _Users = value; RaisePropertyChanged(Constants.VM_Users); }
}
I add a new entry in my MVVM and update the data.
I then set the selected item in my mvvm:
private UserRecord _selectedManagerUser;
public UserRecord SelectedManagerUser
{
get
{
return _selectedManagerUser;
}
set
{
_selectedManagerUser = value;
RaisePropertyChanged("SelectedManagerUser");
}
}
SelectedManagerUser = Users[temp];
public class UserRecord : ViewModelBase
{
private int _Active;
private int _UserRecordId;
private string _UserRef;
private string _FName;
private string _SName;
private string _Login;
private string _Salt;
private int _IsAdmin;
private string _FullName;
private string _Branch;
private string _Position;
private string _Department;
public int Disabled { get { return _Active; } set { _Active = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Active); } }
public int UserRecordId { get { return _UserRecordId; } set { _UserRecordId = value; RaisePropertyChanged("UserRecordId"); } }
public string UserRef { get { return _UserRef; } set { _UserRef = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_UserRef); } }
public string FName { get { return _FName; } set { _FName = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_FName); } }
public string SName { get { return _SName; } set { _SName = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_SName); } }
public string Login { get { return _Login; } set { _Login = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Login); } }
public string Salt { get { return _Salt; } set { _Salt = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Salt); } }
public int IsAdmin { get { return _IsAdmin; } set { _IsAdmin = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_IsAdmin); } }
public string Branch { get { return _Branch; } set { _Branch = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Branch); } }
public string Position { get { return _Position; } set { _Position = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Position); } }
public string Department { get { return _Department; } set { _Department = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Department); } }
public string FullName { get { return FName + ", " + SName; } set { _FullName = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Fullname); } }
}
I know the new item has been added because -
I can see it int the dropdown
I set a breakpoint in my code and inspect.
The combo box just displays an empty value.
Anything else I can try?
thanks
Not sure what's going wrong on your side, but it might be helpful to look at a working solution.
XAML:
<ComboBox ItemsSource="{Binding Users}"
DisplayMemberPath="Value.Name"
SelectedValue="{Binding SelectedUser}"
SelectedValuePath="Value" />
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += WindowLoaded;
var vm = new ViewModel();
vm.Users.Add("u1", new UserRecord { Name = "User 1" });
vm.Users.Add("u2", new UserRecord { Name = "User 2" });
vm.Users.Add("u3", new UserRecord { Name = "User 3" });
DataContext = vm;
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// make sure it works after DataContext was set
var vm = (ViewModel)DataContext;
vm.SelectedUser = vm.Users["u2"];
}
}
public class UserRecord
{
public string Name { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Dictionary<string, UserRecord> Users { get; }
= new Dictionary<string, UserRecord>();
private UserRecord selectedUser;
public UserRecord SelectedUser
{
get { return selectedUser; }
set
{
selectedUser = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedUser)));
}
}
}
Your SelectedManagerUser property should be changed to this. The SelectedManagerUser property is set with new value but you do not raise that event so the UI will not be updated.
private UserRecord _selectedManagerUser;
public UserRecord SelectedManagerUser
{
get
{
return _selectedManagerUser;
}
set
{
_selectedManagerUser = value;
RaisePropertyChanged("SelectedManagerUser");
}
}
Download Prism from Nuget and inherit your class from BindableBase.
After it use this:
private UserRecord selectedManagerUser;
public UserRecord SelectedManagerUser
{
get { return this.selectedManagerUser; }
set { this.SetProperty(ref this.selectedManagerUser, value); }
}
One of two things can cause this. First, it might be because you're not setting SelectedManagerUser to be an instance of UserRecord that is NOT in the Dictionary, or that Dictionaries still suck for databinding. Lemme cover them both.
When you work with ItemsSource and SelectedItem bindings, if you want SelectedItem changes to be reflected in the UI, you must set it to an instance that can be found within ItemsSource. The control, by default, will look for the item in the source that is referentially equal to the selected item. I'm 99% sure it will use IEquatable<T> instead of reference checking if your items implement it.
If that's not your problem, then it's because Dictionaries suck for databinding.
Dictionaries are TERRIBLE for databinding. Just awful. If you need a keyed collection and you want to bind against it, create a custom collection extending KeyedCollection. With some extra work TItem can implement INPC (make the key read only, tho) and the collection can implement INCC. Works great for binding. Why do I mention this? Read on...
Your problem is that, within the ComboBox, SelectedItem is actually of type KeyValuePair<string,UserRecord>, and NOT UserRecord. So the binding will NOT work. If you grab a copy of Snoop and examine the bindings at runtime, you'll see this.
The problem is that the control doesn't know jack squat about Dictionaries. All it knows is IEnumerable<T>. Dictionary<K,T> implements IEnumerable<KeyValuePair<K,T>>, so the control creates an item for each key value pair. SelectedItem is also a key value pair. So, when you bind that to a property of type UserRecord, yes it is able to use the SelectedValuePath to set the value properly, but it cannot (does not) [ninja edit: unless this behavior has changed over the past few years :/] iterate the enumerable in order to find the correct key value pair when you set the value in your view model.
If UserRecord's key value is a property within the type, then definitely create a KeyedCollection for it. KeyedCollection<Tkey,TItem> implements 'IEnumerable` so it works seamlessly with bindings. If not, wrap it in a proxy, or add the property.
And when I say "wrap it in a proxy", anybody who says "what, like a KeyValuePair?" I'm going to punch you through the internet. The proxy becomes the value you bind against. Don't waste your time with this SelectedValuePath nonsense. Work with the proxies directly. When you need your value, extract it at the last moment, not immediately after the binding executes.
Update the model from the view model
I have read some post about the MVVM but I not sure if understand the
way that the view model is updating the model
Currently I have two text boxes in the UI which is bound to the XAML view and call to the view model when the event was raised .
when should be the place in the view model when I updating the model?
This is the view model
class ViewModel:INotifyPropertyChanged
{
private String _url;
private String _TemplateType;
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
The model
internal class DefineAddinModel
{
public string TemplateType { get; set; }
public String URL { get; set; }
}
The ViewModel usually acts as a wrapper around the Model and contains a reference to the Model which is can update either in response to commands or automatically in property setters.
UPDATE:
Here's an example of having the VM act as a wrapper around the Model. This may seem useless in your example but you will find in many cases the VM's getters/setters need to do some sort of transformation on the values rather than simply passing them through.
class ViewModel:INotifyPropertyChanged
{
private DefineAddinModel model;
public string URL
{
get { return model.URL; }
set
{
if (value != model.URL)
{
model.url = value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return model.TemplateType; }
set
{
if (value != model.TemplateType)
{
model.TemplateType = value;
OnPropertyChanged("TemplateType");
}
}
}
The better way to update your Model Is by using an event, its safer, so choose weather using a button click or lost focus, or whatever you want
void button_click(object sender,eventsarg e)
{
MyObj.URL = App.Locator.MyVM.MyDefineAddinModel.URL;// App.Locator because MVVMLight is tagged
MyObj.TemplateType = App.Locator.MyVM.MyDefineAddinModel.TemplateType ;
}
but personnaly i Use the following steps :
1- In your ViewModel create a CurrentItem object of type DefineAddinModel and without OnPropertyChanged then bind it to the View(UI) DataContext of the RootElement on the View )
2- for the model I use the INotifyPropertyChanged for each propery
3- after binding the datacontext of your root element to the CurrentItem of your ViewModel then bind just URL and TemplateType properties to your Controls, so any thing changes on the textbox will update CurrentItem properties
you can also chose the type of the binding (On LostFocus, or OnPropertyChanged)
You need to bind your TextBoxes to the two properties URL and TemplateType.
Try to use Commands (in the ViewModel)instead of events (in The CodeBehind) since you are in MVVM.
For updating the model : use a button with it's Command property bound to OnSave just like this example:
private String _url;
private String _TemplateType;
private DefineAddinModel _defineAddin;
public DefineAddinModel DefineAddin
{
get {return _defineAddin;}
set
{
_defineAddin = value;
OnPropertyChanged("DefineAddin");
}
}
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
public RelayCommand OnSaved
{
get;
set;
}
public ViewModel()
{
DefineAddin = new DefineAddinModel();
OnSaved = new RelayCommand(()=>
{
DefineAddin.URL = URL ;
DefineAddin.TemplateType = TemplateType;
});
Think about using third parties like MVVMLight it helps you a lot with MVVM and the helpers around it (Commands, Messenger, ViewModelLocator ...)
I think that the correct answer here is 'it depends'.
In most general cases, the advantage of actually using a ViewModel is also to track 'transient state', i.e. the state of an 'edit in progress' operation.
In this particular case, you would not push your changes directly to the Model every time a value is updated, instead you would do this via an 'Update' ICommand implementation that will collect all the data from the ViewModel and push it down to the Model.
This approach gives you many advantages:
The user of the view can change their mind as many times as they want, and only when they are happy will the Model actually get updated with their definitive choices
It greatly reduces the load on your persistence service, since only final changes are pushed through.
It allows you to do final validation on a complete set of values, rather than transient states, and hence reduces programming complexity and overhead.
It also makes your UI far more fluid since all the examples above are pushing updates on the UI Dispatcher, and avoids you having to cater for this via Tasks or other async approaches.
The backing model is never in an inconsistent state, since I would imagine that all values on one View/ViewModel are related, and only make sense when updated together using an ACID approach.
Here's an example of how I'd do it.
public class ViewModel:INotifyPropertyChanged {
private String _url;
private String _TemplateType;
public ViewModel(){
UpdateCommand = new DelegateCommand(OnExecuteUpdate, OnCanExecuteUpdate);
}
public bool OnCanExecuteUpdate(object param){
// insert logic here to return true when one can update
// or false when data is incomplete
}
public void OnExecuteUpdate(object param){
// insert logic here to update your model using data from the view model
}
public ICommand UpdateCommand { get; set;}
public string URL{
get { return _url; }
set {
if (value != _url) {
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType {
get { return _TemplateType; }
set {
if (value != _TemplateType) {
_TemplateType= value;
OnPropertyChanged("TemplateType");
}
}
}
... etc.
}
public class DelegateCommand : ICommand {
Func<object, bool> canExecute;
Action<object> executeAction;
public DelegateCommand(Action<object> executeAction)
: this(executeAction, null) {}
public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute) {
if (executeAction == null) {
throw new ArgumentNullException("executeAction");
}
this.executeAction = executeAction;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter) {
bool result = true;
Func<object, bool> canExecuteHandler = this.canExecute;
if (canExecuteHandler != null) {
result = canExecuteHandler(parameter);
}
return result;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() {
EventHandler handler = this.CanExecuteChanged;
if (handler != null) {
handler(this, new EventArgs());
}
}
public void Execute(object parameter) {
this.executeAction(parameter);
}
}
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