INotifyPropertyChanged won't update grid in the window [duplicate] - wpf

This question already has answers here:
INotifyPropertyChanged WPF
(3 answers)
using of INotifyPropertyChanged
(3 answers)
Closed last year.
I've been this for a while now but I can't seem to be figuring out why my code won't update the window. The events are called and the invoke methods are invoked, but still, my window won't update
public class RaceContext : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Race CurrentRace { get; set; }
public string TrackName { get => CurrentRace == null ? "" : "Track name : " + CurrentRace.Track.Name; }
public List<IParticipant> Participants { get; set; }
public ObservableCollection<DriverListView> DriverListViews
{
get { return _DriverListViews; }
set { _DriverListViews = value; OnPropertyChanged(nameof(DriverListViews)); }
}
public ObservableCollection<DriverListView> _DriverListViews = new ObservableCollection<DriverListView>();
public void OnNextRace(object sender, NextRaceEventArgs e)
{
CurrentRace = e.Race;
e.Race.DriverChanged += OndriversChanged;
e.Race.DriverMoved += OnDriverMoved;
DriverListViews = new ObservableCollection<DriverListView>();
CurrentRace.Participants.ForEach(item =>
{
DriverListViews.Add(new DriverListView()
{
Driver = item
});
});
OnPropertyChanged();
}
public void OndriversChanged (object sender, DriversChangedEventArgs e)
{
Participants = CurrentRace.Participants;
e.DriverTimers.ForEach(timer =>
{
var view = DriverListViews.Where(item => item.Driver.Equals(timer.Driver)).First();
view.RaceStopwatch = timer.TotalRaceTimer.Elapsed;
view.LapStopwatch = timer.LapTimer.Elapsed;
});
OnPropertyChanged(nameof(DriverListViews));
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is the list view class
public class DriverListView
{
public IParticipant Driver { get; set; }
public int Position { get; set; }
public TimeSpan RaceStopwatch { get; set; }
public int LapCount { get; set; }
public TimeSpan LapStopwatch { get; set; }
public string LapPercentage { get; set; }
public int SectionCount { get; set; }
public string ParticipantName { get => Driver.Name; }
public TeamColors TeamColor { get => Driver.TeamColor; }
public int ParticpantPoints { get => Driver.Points; }
}
So when OnDriverChanged is called, the INotifyPropertyChanged is called. the window does not update the records
Thanks

Related

WPF: How to notify the modification of an interface that changes in a DLL on the bindings side

How to notify the modification of an interface that changes in a DLL on the bindings side.
To explain:
Dll code is not editable:
public interface IPlayer
{
int Id { get; }
string Name { get; }
Settings Settings { get; }
PlayerCategory Category { get; }
}
public class TennisPlayer: IPlayer
{
public virtual int Id { get; }
public virtual string Name { get; set; }
public Tennisman(int id, string name)
{
Id = id;
Name = name;
}
public Settings Settings { get; set; }
public PlayerCategory Category { get; set; }
}
My code:
public partial class PlayerItem : NotifyUserControl
{
private DispatcherTimer timer = new DispatcherTimer();
public static readonly DependencyProperty PlayerProperty =
DependencyProperty.Register("Player", typeof(IPlayer),
typeof(PlayerItem),
new PropertyMetadata(null, OnCaptionPropertyChanged));
public IPlayer Player
{
get { return (IPlayer)GetValue(PlayerProperty); }
set
{
SetValue(PlayerProperty, value);
}
}
public string PlayerName
{
get => Player != null ? Player.Name : "";
set => OnPropertyChanged();
}
public PlayerItem()
{
InitializeComponent();
timer.Interval = new TimeSpan(0, 0, 4);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
OnPropertyChanged(nameof(PlayerName));
}
The external dll gives me player classes( for example: tennisman, footballer...) based on the same interface.
But I don't know it and I don't have to do class by class.
I must be missing something huge, but I managed to find nothing in my research.

I need your suggestion regarding loop in Model to ViewModel MVVM

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!

Caliburn CanExecute not working even though firing

Trying to implement simple validation with caliburn. All I want is to enable/disable save button based on certain conditions.
View:
`<xctk:MaskedTextBox x:Name="pm_personId" cal:Message.Attach="[Event LostFocus] = [Action CanSave()]" Mask="00-000-000?"/>
<Button Content="Save" x:Name="Save" />`
Model:
public class PersonModel
{
public String personId { get; set; }
public PersonModel() {}
public PersonModel(String id)
{
this.id = personId;
}
}
ViewModel:
[ImplementPropertyChanged]
public class PersonViewModel : Screen
{
public PersonModel pm { get; set; }
public PersonViewModel()
{
pm = new PersonModel();
}
public bool CanSave()
{
MessageBox.Show(pm.personId);
if (pm.personId != null)
return true;
else return false;
}
}
The MessageBox is fired with the right value but button is not enable. Am I missing anything. Either am missing something with caliburn or it's doing too much magic. Am beginning to suspect that the time it may save you initially will be lost in debugging, just my exeprience.
Thanks #CCamilo but your answer was incomplete. For other people who encounter a similar problem, below is my final working code:
[ImplementPropertyChanged]
public class PersonModel
{
public String personId { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public PersonModel() {}
public PersonModel(String id)
{
this.id = personId;
}
}
[ImplementPropertyChanged]
public class PersonViewModel : Screen
{
public PersonModel pm { get; set; }
public PersonViewModel()
{
pm = new PersonModel();
this.pm.PropertyChanged += pm_PropertyChanged;
}
void pm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyOfPropertyChange(() => CanSave);
}
public bool CanSave
{
get { return pm.personId != null; }
}
}
The error you have is with CanSave() method.. It should be a property instead:
public bool CanSave
{
get
{
if (pm.personId != null)
return true;
else return false;
}
}

How to use an object as a data source c#

I'm trying to figure out how to make an object data source where I can select which columns to display in Visual Studio and all that. Here's what I have so far, but I'm not sure what else I'm supposed to do?
public class ItemData
{
public string ItemName { get; set; }
public string Description { get; set; }
public string Quantity { get; set; }
public string ManuPartNumber { get; set; }
public string ListID { get; set; }
public string VendorRef { get; set; }
public string VendorName { get; set; }
public string EditSequence { get; set; }
public string UPC { get; set; }
}
public class ItemDataSource : IEnumerable<ItemData>
{
private ICollection<ItemData> list;
public ItemDataSource()
{
try
{
list = QBCom.GetItemList();
}
catch (Exception e)
{
list = new List<ItemData>();
}
}
public ItemDataSource(IEnumerable<ItemData> data)
{
list = data.ToList();
}
public IEnumerator<ItemData> GetEnumerator()
{
foreach (var item in list)
{
yield return item;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable<ItemData>)this).GetEnumerator();
}
}
I am not talking about ASP.net, I'm talking about stuff like DataTables and so forth. Just a regular winforms program.
I assume you want to bind to a DataGridView if so I would inherit from BindingSource and set the list to the DataSource. You can then set the datagridview DataSource to the object to view the columns in the visual studio properties window.
public class ItemDataSource : BindingSource
{
private ICollection<ItemData> list;
public ItemDataSource()
{
try
{
list = QBCom.GetItemList();
}
catch (Exception e)
{
list = new List<ItemData>();
}
this.DataSource = list;
}
public ItemDataSource(IEnumerable<ItemData> data)
{
list = data.ToList();
this.DataSource = list;
}
}

How to make dataannotations outside the entities

I'm using Wpf and EntityFrameWork in my project. I wanted to use DataAnnotations since it is a better way to validate the data. I managed to make it in my entity files. For example one of my entities include these:
public partial class Employee : INotifyPropertyChanged, IDataErrorInfo
{
public Employee()
{
this.Address = new HashSet<Address>();
this.Certificate = new HashSet<Certificate>();
this.ContactInformation = new HashSet<ContactInformation>();
this.Course = new HashSet<Course>();
this.Education = new HashSet<Education>();
this.EmployeeTeam = new HashSet<EmployeeTeam>();
this.Evaluation = new HashSet<Evaluation>();
this.ExamInformation = new HashSet<ExamInformation>();
this.JobTitle = new HashSet<JobTitle>();
this.MilitaryService = new HashSet<MilitaryService>();
this.Relationship = new HashSet<Relationship>();
this.WorkExperience = new HashSet<WorkExperience>();
}
public int Id { get; set; }
private string _CitizenId;
[Required(ErrorMessage = "CitizenId is required!")]
[RegularExpression(#"^\d+$", ErrorMessage = "Only numbers allowed")]
public string CitizenId
{
get
{
return _CitizenId;
}
set
{
_CitizenId = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CitizenId"));
}
}
}
private int _RegNumber;
[Required (ErrorMessage = "Registration Number is required!")]
[RegularExpression(#"^\d+$", ErrorMessage = "Only numbers allowed")]
public int RegNumber
{
get
{
return _RegNumber;
}
set
{
_RegNumber = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("RegNumber"));
}
}
}
public Nullable<short> ResourceTypeId { get; set; }
public Nullable<short> EmployeeRoleId { get; set; }
private string _FirstName;
[Required(ErrorMessage="Name is required!")]
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage="Numeric expressions are not allowed!")]
public string FirstName
{
get
{
return _FirstName;
}
set
{
_FirstName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
private string _MiddleName;
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage = "Numeric expressions are not allowed!")]
public string MiddleName
{
get
{
return _MiddleName;
}
set
{
_MiddleName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MiddleName"));
}
}
}
private string _Surname;
[Required(ErrorMessage= "Surname is required!")]
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage = "Numeric expressions are not allowed!")]
public string Surname {
get
{
return _Surname;
}
set
{
_Surname = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Surname"));
}
}
}
private int _CustomerNo;
[Required(ErrorMessage = "Customer Number is required!")]
[RegularExpression(#"^\d+$", ErrorMessage = "Only numbers allowed")]
public int CustomerNo
{
get
{
return _CustomerNo;
}
set
{
_CustomerNo = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CustomerNo"));
}
}
}
public System.DateTime BirthDate { get; set; }
private string _BirthPlace;
[Required(ErrorMessage="Birh Place is required!")]
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage = "Numeric expressions are not allowed!")]
public string BirthPlace
{
get
{
return _BirthPlace;
}
set
{
_BirthPlace = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("BirthPlace"));
}
}
}
public string Gender { get; set; }
public string MaritalStatus { get; set; }
public System.DateTime WorkBeginDate { get; set; }
//indicates that whether the start date is bigger than the end date which is a validation problem
// [CustomValidationsForDate("WorkBeginDate", ErrorMessage="Starting date cannot be bigger than the ending date")]
public Nullable<System.DateTime> WorkEndDate { get; set; }
private string _LogonName;
[Required(ErrorMessage = "Logon Name is required!")]
public string LogonName
{
get
{
return _LogonName;
}
set
{
_LogonName = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("LogonName"));
}
}
}
private int _DistanceToWork;
[RegularExpression(#"^\d+$", ErrorMessage = "Only numbers allowed")]
[Required(ErrorMessage="This field is required!")]
public int DistanceToWork
{
get
{
return _DistanceToWork;
}
set
{
_DistanceToWork = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DistanceToWork"));
}
}
}
public Nullable<bool> HasInternet { get; set; }
public Nullable<bool> HasRemoteConnection { get; set; }
public int MilitaryStatus { get; set; }
public Nullable<int> DrivingLicense { get; set; }
public bool IsSmoker { get; set; }
public bool IsActive
{ get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public string LeavingReason { get; set; }
public Nullable<System.DateTime> StartDate { get; set; }
public Nullable<System.DateTime> EndDate { get; set; }
public virtual ICollection<Address> Address { get; set; }
public virtual ICollection<Certificate> Certificate { get; set; }
public virtual ICollection<ContactInformation> ContactInformation { get; set; }
public virtual ICollection<Course> Course { get; set; }
public virtual ICollection<Education> Education { get; set; }
public virtual ICollection<EmployeeTeam> EmployeeTeam { get; set; }
public virtual ICollection<Evaluation> Evaluation { get; set; }
public virtual ICollection<ExamInformation> ExamInformation { get; set; }
public virtual ICollection<JobTitle> JobTitle { get; set; }
public virtual ICollection<MilitaryService> MilitaryService { get; set; }
public virtual ICollection<Relationship> Relationship { get; set; }
public virtual ICollection<WorkExperience> WorkExperience { get; set; }
//Following methos have been used in order to apply DataAnnotations
public event PropertyChangedEventHandler PropertyChanged;
public string Error { get { return String.Empty; } }
public string this[string property]
{
get { return ValidateProperty(this, property); }
}
public static string ValidateProperty(object instance, string propertyName)
{
PropertyInfo property = instance.GetType().GetProperty(propertyName);
object value = property.GetValue(instance, null);
List<string> errors = (from v in property.GetCustomAttributes(true).OfType<ValidationAttribute>()
where !v.IsValid(value)
select v.ErrorMessage).ToList();
return (errors.Count > 0) ? String.Join("\r\n", errors) : null;
}
}
}
So like I said I'm applying dataannotations inside the entities but what I want is doing that outside because whenever a change occurs in my database, all the entities will be refreshed so what I have here is not a dynamic structure.
So could you please help?
Thanks in advance.
Best regards.
Ege
You should create a model (view model) class and map from your entities (domain models) to the view model. AutoMapper can help speed this up. You then put your annotations on your view models and use these in your views.
This means that if your entity model changes you just need to update the mapping. It also means that you can create multiple different models for the same entity so your validation doesn't have to be the same for each view that uses it.

Resources