I have Model Class Employee:
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
and View Model class EmployeeViewModel which contains an Employee Object
public class EmployeeViewModel : INotifyPropertyChanged
{
public EmployeeViewModel()
{
}
private Employee currentEmployee;
public Employee CurrentEmployee
{
get { return this.currentEmployee; }
set
{
this.currentEmployee = value;
this.NotifyPropertyChanged("CurrentEmployee");
}
}
//Some code .....
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Now the View (WPF) will use Employee object in the View Model as ItemSource to display Employee data
Now the question is: I have Update button on the view and when I change the Employee properties in the view (via text boxes) I want to update the model (so afterward i can update the database), how to update this model from the view.
As I checked there something weird with your Model Class. It is the one that should implement INotifyPropertyChanged then create backing fields for each property something like this.
Model Class
public class Employee:INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name= value;
OnPropertyChanged("Name");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
**ViewModel Class**
class EmployeeViewModel
{
private IList<Employee> _employees;
public EmployeeViewModel()
{
_employees= new List<Employee>
{
new Employee{ID=1, Name ="Emp1"},
new Employee{ID=2, Name="Emp2"}
};
}
public IList<Employee> Employees
{
get
{
return _employees;
}
set
{
_employees= value;
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater();
return mUpdater;
}
set
{
mUpdater = value;
}
}
private class Updater : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
#endregion
}
}
You may put your logic inside OnPropertyChanged event. This method is always called whenever you made changes on the UI
If you are using ObservableCollection modify your List by searching the item based on ID then if found do the modification of values. Whatever changes you have made will automatically affect the UI items if they are bound to your ObservableCollection then use the modified collection to update your DB records
Related
I'am new to WPF and MVVM and I was given the task to continue working on one of the unfinished project that is made using the said technology. I've written a sample code below that is similar to the structure of the project.
My concern is, the loop used in GetBookPages() to display the details on the grid might take some time to finish.
public class BookModel
{
public string BookTitle { get; set; }
public List<BookDetailModel> BookDetails { get; set; }
}
public class BookDetailModel
{
public int Pages { get; set; }
public string Others { get; set; }
// ....
}
public class BookViewModel : INotifyPropertyChanged
{
private BookModel _model;
private ObservableCollection<BookDetailViewModel> _bookDetailSource;
private BookService _service;
public BookViewModel()
{
_model = new BookModel();
_service = new BookService();
GetBookPages();
}
/// <summary>
/// This is the item source of datagrid that is located in view
/// </summary>
public ObservableCollection<BookDetailViewModel> BookDetailSource
{
get { return _bookDetailSource; }
set
{
if (value == _bookDetailSource)
return;
_bookDetailSource = value;
OnPropertyChanged();
}
}
private void GetBookPages()
{
BookModel bookModel = _service.GetBookData();
var listOf = new List<BookDetailViewModel>();
bookModel.BookDetails.ForEach(e =>
{
// This is were the system's bottle neck is.
// can someone please suggests me a good work around.
listOf.Add(
new BookDetailViewModel
{
Others = e.Others,
// ....
});
});
BookDetailSource = new ObservableCollection<BookDetailViewModel>(listOf);
}
public string BookTitle
{
get { return _model.BookTitle; }
set
{
if (value == _model.BookTitle)
return;
_model.BookTitle = value;
OnPropertyChanged();
}
}
#region Property Change
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class BookDetailViewModel : INotifyPropertyChanged
{
private BookDetailModel _model;
#region Constructor
public BookDetailViewModel()
{
_model = new BookDetailModel();
ViewPageDataCommand = new RelayCommand(x => ViewPageData());
RemovePageCommdand = new RelayCommand(x => RemovePage());
}
#endregion
#region Properties
public int Page
{
get { return _model.Pages; }
set
{
if (value == _model.Pages)
return;
_model.Pages = value;
OnPropertyChanged();
}
}
public string Others
{
get { return _model.Others; }
set
{
if (value == _model.Others)
return;
_model.Others = value;
OnPropertyChanged();
}
}
#endregion
// These are the button command inside the grid's row
public ICommand ViewPageDataCommand { get; private set; }
public ICommand RemovePageCommdand { get; private set; }
private void ViewPageData()
{
// view the page data by clicking the row button inside the grid
}
private void RemovePage()
{
// will remove the currently selected row inside the grid
}
#region Property Change
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class BookService
{
public BookModel GetBookData()
{
var data = GetBookData(99);
data.BookDetails = GetBookDetail(99);
return data;
}
private BookModel GetBookData(int bookId)
{
// return 1 row only
}
private List<BookDetailModel> GetBookDetail(int bookId)
{
// return List<BookDetailModel> that might consists of more than 100 index's
}
}
I hope you understand what I mean. Your suggestion will be much appreciated. Thanks in advance!
I have an ObservableCollection of items in which one of the property is bool.
When i set the itemsSource of the datagrid as the ObservableCollection, it auto-generates the column with checkbox column for the bool property.
I would like to know how we can tick the checkbox in code, lets say if we have the mark all option?
I tried updating the ObservableCollection records property value with true, but it doesnt help updating the UI.
Please help.
[EDIT: Below code works as suggested in the answer]
My Class is as follows
public class InvoiceDoc : INotifyPropertyChanged
{
private bool _Selected;
[DisplayName("Selected")]
public bool Selected
{
get { return _Selected; }
set { _Selected = value; this.OnPropertyChanged(); }
}
[DisplayName("Date")]
public DateTime DocDate { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged !=null)
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The datagrid is as follows
<DataGrid x:Name="dgInvoices" Margin="32,110,32,59" AutoGeneratingColumn="dgInvoices_AutoGeneratingColumn"/>
setting the ItemsSource is as follows
docs = new ObservableCollection<InvoiceDoc>(); ;
dgInvoices.ItemsSource = docs;
I am expecting the grid to auto check the check box once is set the value in the collection.
Binding to an ObservableCollection is only reactive if an Item is added or removed.
Your elements inside your Collection have to implement INotifyPropertyChanged so the UI recognises the changes
EDIT:
Lets say you have the following objects in your Collection:
public class MyClass {
public string Name { get; set; }
public bool IsActive { get; set; }
}
This class has now to be modified to the following:
public class MyClass : INotifyPropertyChanged{
private string _name;
private bool _isActive;
public string Name
{
get { return this._name; }
set { this._name = value; this.OnPropertyChanged();}
}
public bool IsActive
{
get { return this._isActive; }
set { this._isActive = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If there are any Errors, remove the CallerMemberNameAttribute and invoke the this.OnPropertyChanged(); with the Propertyname.
I am using WPF and MVVM for a project. I have a view with a GridView control. User can Insert/Update/Delete In Grid View. when any of the action happen changes reflect in ViewModel. This part is working Ok. But when I want to save the changes in Database I need to loop through each Item in ItemSource one by one. which takes the extra time to complete. I want to process only those Items which are changes.
To accomplish this , I add a boolean property in my Model to indicate whether the Item is changed or note. But problem is that I can not see any way to set this boolean property whenever any other property is changed.
Can any body help me how to do it?
EDIT
I have a SelectedItem Property , and I am assuming that whenever an Item is selected in GridView , User will update or insert the row. so on SelectedItem property I have set boolean property of SelectedItem to True. and while looping to save records I am saving all those records who have True in their boolean property. I know its not the perfact way, but right now I do not have any other way to do it. Your thoughts?
You could subscribe to the PropertyChanged event on your Model and set the Flag to True.
But keep in mind that you have to set the Flag to false after you loaded the data from the database, because the initialization of the model will also call the propertychanged event.
Example for class with IsDirty-Flag:
public class Sample : INotifyPropertyChanged
{
private int id;
private string name;
private bool isDirty;
public event PropertyChangedEventHandler PropertyChanged;
public int Id
{
get { return id; }
set
{
if(id != value)
{
id = value;
RaisePropertyChanged("Id");
}
}
}
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
public bool IsDirty
{
get { return isDirty; }
set
{
if (isDirty != value)
{
isDirty = value;
RaisePropertyChanged("IsDirty");
}
}
}
protected virtual void RaisePropertyChanged(string propertyName)
{
if (propertyName != "IsDirty")
{
IsDirty = true;
}
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
if you are using an ObservableCollection, you can also add an eventhandler to track the rows that are newly added or deleted
If you use MVVM you should have implementation of INotifyPropertyChanged. And you can add some logic to set up yours boolean property in OnPropertyChanged handler.
If you are willing to take a dependency on a build time tool you can do this with ILWeaving.
So if you combine Fody with the PropertyChanged addin then IsDirty functionality is supported out of the box.
Then Martins example can be simplified to this
public class Sample : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Id { get; set; }
public string Name { get; set; }
public bool IsChanged { get; set; }
}
Note the use if IsChanged instead of IsDirty.
And then this will exist in the compiled assembly
public class Sample : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
int id;
public int Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
IsChanged = true;
OnPropertyChanged("Id");
}
}
}
bool isChanged;
public bool IsChanged
{
get { return isChanged; }
set
{
if (isChanged != value)
{
isChanged = value;
OnPropertyChanged("IsChanged");
}
}
}
string name;
public string Name
{
get { return name; }
set
{
if (!string.Equals(name, value, StringComparison.Ordinal))
{
name = value;
IsChanged = true;
OnPropertyChanged("Name");
}
}
}
}
With database first scenario, I have a many - many relation and use EF as ORM & DAL:
Customer: ID, Name, Address ||
Product: ID, Name ||
CustomerProduct: CutomerID, ProductID
I add a custom property to the Product entity class , called Isincludedforcustomer.
public partial class Product: EntityObject
{
public bool isincludedforcustomer;
public bool Isincludedforcustomer
{
get { return isincludedforcustomer; }
set {isincludedforcustomer= value; }
}
When a Customer is selected, I have a method to assign the new property.
IsProductinclinframe(Displayedcustomerproducts, products);
How can i implement property changed to this property ?
I generally put a call to the PropertyChanged event in the setter of the property.
public partial class Product: EntityObject, INotifyPropertyChanged
{
public bool isincludedforcustomer;
public bool Isincludedforcustomer
{
get { return isincludedforcustomer; }
set
{
isincludedforcustomer= value;
RaisePropertyChanged("Isincludedforcustomer");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
I am learning about observable collections, so I wrote a small program to test my progress.
I have a observable collection class that I supply initial values from a list and bind the observablecollection to a Datagrid. It works great, but when I clear the list using myListOfPlayers.myList.Clear(), the Datagrid does not clear. I thought that the INotifyPropertyChanged property would handle that. What am I doing wrong?
public class PlayerList : ObservableCollection<PlayerName> //observable collection class
{
public PlayerList()
: base()
{
Clear();
foreach (var p in myListOfPlayers.myList.OrderBy(x => x.Score))
{
Add(p);
}
}
public PlayerList(List<PlayerName> list)
: base(list)
{
Clear();
foreach (var p in list)
{
Add(p);
}
}
}
I implement INotifyPropertyChanged in the PlayerName class:
public class PlayerName : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private int _score;
public int Score
{
get { return _score; }
set
{
_score = value;
OnPropertyChanged("Score");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If you clear myListOfPlayers.myList, the PlayerList is not supposed to be cleared... There is no relation between these 2 lists: you just used myListOfPlayers.myList to initialize the content of PlayerList, but they are two different lists. What you do on one of them doesn't affect the other