custom list of object binding error in grid view - c#

my c# class is
public class Otp
{
public String Time;
public Otp()
{
}
public void setTime(String Time)
{
this.Time = Time;
}
public String getTime()
{
return this.Time;
}
}
my problem is when i bind a list of object to grid view it give me error how can i solve this problem?

The databinding only can bind against properties and your Time is defined as a member variable. To make Time a property, define it with getter and setter:
public String Time { get; set; }
And there is no need in additional get and set methods.

public String Time
{
get
{
//your code here
return this.Time;//ex.
}
set
{
//your code here
this.Time = Time;//ex.
}
}

Related

How do I detect changes in nested properties?

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.

Collection of comboBoxes in WinForms

I am working on small winforms app. One of my forms contains few comboBoxes:
As I am trying to use MVP pattern in my project, so I decided to create View and Presenter for that form. The communicate via adequate interface.
ComboBox can be fully decribed (for my needs) with its DataSource (i.e. list os strings) and SelectedIndex. That' s why I created proper interface:
public interface IMyView
{
MyViewPresenter { set; }
IEnumerable<string> ComboBox1stDataSource { get; set; }
int ComboBox1SelectedIndex { get; set; }
IEnumerable<string> ComboBox2ndDataSource { get; set; }
int ComboBox2ndSelectedIndex { get; set; }
//for third comboBox it will be the same
}
I implemented that interface in my View class:
public partial class MaterialDatabasePropertiesForm : Form, IMaterialDatabasePropertiesView, IMyView
{
public MaterialDatabasePropertiesPresenter Presenter { private get; set; }
public IEnumerable<string> ComboBox1stDataSource
{
get { return comboBox1st.DataSource as List<string>; }
set { comboBox1st.DataSource = value; }
}
public int ComboBox1SelectedIndex
{
get { return comboBox1st.SelectedIndex; }
set { comboBox1st.SelectedIndex = value; }
}
public IEnumerable<string> ComboBox2ndDataSource
{
get { return comboBox2nd.DataSource as List<string>; }
set { comboBox2nd.DataSource = value; }
}
public int ComboBox2ndSelectedIndex
{
get { return comboBox2nd.SelectedIndex; }
set { comboBox2nd.SelectedIndex = value; }
}
}
When everything is set like above I use properties declared in Interface in my Presenter to change properties of comboBoxes in form.
Although it may seem like a good solution it isn' t enough for me. In my origial application I have 14 comboBoxes, and that number may change in future.
What I am trying to is making it more elastic. I was thinking about creatin some collection of comboBoxes in view, but I can' t figure it out.
My sample solutin is bad, as it doesn' t even compile:
private List<List<string>> collectionOfComboBoxesDataSources = new List<List<string>>()
{
ref comboBox1st.DataSource, // I get error:
ref comboBox2nd.DataSource, // "Cannot acces non-static field
ref comboBox3rd.DataSource // <comboBoxName> in static context"
};
//this property would be part of IMyView
public List<List<string>> CollectionOfComboBoxesDataSources
{
get { return collectionOfComboBoxesDataSources; }
set { collectionOfComboBoxesDataSources = value; }
}
What can I do to create collection (or something working similar) to acces my comboBoxes properties?
You could try to iterate through your form.
List<ComboBox> listOfCombobox = new List<ComboBox>();
foreach(var combobox in this.controls.OfType<ComboBox>())
{
listOfCombobox.Add(combobox);
}
If you're trying to do this.
Then you can access the list via index, so you can access your properties of each combobox.

How to sync data in MVVM when using collections

Recently I have started to dig into MVVM to structure a WPF application I am working on. I am struggling to understand how I can keep collections in sync between Model and ViewModel, and in conjunction with that, how to validate information the user will enter.
Suppose I have a (theoretical) class Building, the model, that will store a building layout, during runtime in memory, and otherwise in xml via serialization. Building has a member List, and each entry Floor in that list can have other Lists, like List and List, which could again have members which are Lists (ie. List).
The model:
namespace TestMVVM
{
public class Building
{
public string strName { get; set; }
public List<Floor> floors { get; set; }
}
public class Floor
{
public int iNumber { get; set; }
public List<Room> rooms { get; set; }
}
public class Room
{
public int iSize { get; set; }
public string strName { get; set; }
public List<Door> doors { get; set; }
}
public class Door
{
public bool bIsLocked { get; set; }
}
}
In the View, the List of type Floor will be editable in a DataGrid. The user can enter a new row in the DataGrid to add a Floor to the Building class. In another DataGrid, Rooms could be added to a Floor. This is quite easy when I make all Lists into ObservableCollections, and directly couple them with the View. However, this also means there is no proper separation of concerns, and it gets messy once validation comes into play.
So I wrote a ViewModel class, BuildingViewModel. It will hold a reference to an instance of the model. This is where I run into trouble: the ViewModel will hold an ObservableCollection of type FloorViewModel. But when the user adds an entry, how do I also add an entry to the List in the model? And mostly, keep the data in sync? What if a Room is added to a Floor, or a Door to a Room, how to know where in the Model to update which data? Ie. how to sync nested List member data?
Subsequently I would to make sure no duplicate Floors can be created; ie. if the user adds a floor with a number that is already in the List, the DataGrid must report an error. Same if an existing floor is edited, and same for Room names. I would think that kind of error checking cannot happen within the FloorViewModel class, because it has no access to other instances of itself.
I have searched a lot but found no clear answer to this. It would seem like a rather common situation? Maybe I am simply going in the wrong direction with this?
This is the current ViewModel, where ViewModelBase is a generic class holding implementations of INotifyProretyChanged and INotifyDataErrorInfo.
namespace TestMVVM
{
public class BuildingViewModel : ViewModelBase
{
private Building building;
public string strName
{
get { return building.strName; }
set
{
building.strName = value;
if (value == "") AddError("strName", "Name cannot be empty.");
OnPropertyChanged("strName");
}
}
public ObservableCollection<FloorViewModel> floors
{
// what goes here? how to sync members of floor to the model, and validate data?
}
public BuildingViewModel(Building b)
{
building = b;
}
}
public class FloorViewModel : ViewModelBase
{
public ObservableCollection<Room> rooms
{
// what goes here? how to sync members of room to the right Floor of the model, and validate data?
}
}
// etc
}
There is a problem in the classes, that You provided. Try to apply the law of Demeter, watch this video about how to structure correctly the House object (even same example), than You only call the correct level's addX() method, that will validate.
Look you need to read again MVVM concept.. All the idea is to have one view model per each view. In our situation try this:
namespace TestMVVM
{
public class BuildingViewModel : ViewModelBase
{
private Building building;
private ObservableCollection<Floor> _floors;
public string strName
{
get { return building.strName; }
set
{
//building.strName = value;
if (String.IsNullOrEmpty(value))
{
AddError("strName", "Name cannot be empty.");
return;
}
building.strName = value;
OnPropertyChanged("strName");
}
}
public ObservableCollection<Floor> floors
{
get
{
return _floors;
}
set
{
_floors = value;
}
}
public BuildingViewModel(Building b)
{
building = b;
}
public void AddNewFloor(Floor)
{
// valid your floor
// floors.Add(floor);
}
}
Now I suggest you to add function that will validate your changes in floors and not in the setter of the property.
Or override/create ObservableCollection class and redefine all methods :
public class MyObservableCollection<T> : ICollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public int Count { get { return _reference.Count; } }
public bool IsReadOnly { get { return _reference.IsReadOnly; } }
private readonly IList<T> _reference;
public MyObservableCollection(IList<T> reference)
{
_reference = reference;
}
public IEnumerator<T> GetEnumerator()
{
return _reference.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
_reference.Add(item);
SendNotification();
}
public void Clear()
{
_reference.Clear();
SendNotification();
}
public bool Contains(T item)
{
return _reference.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_reference.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
var result = _reference.Remove(item);
SendNotification();
return result;
}
private void SendNotification()
{
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(new NotifyCollectionChangedAction()));
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("..."));
}
}
}
Why you don't change type (List to ObservableCollection) on Model ?
In this case :
public ObservableCollection<FloorViewModel> floors
{
get{return building.floors;}
}

Binding changes unattached variable

I've I really weird problem with my WPF / C# application. I've got a property which returns another property. Now I make a variable and set it to one of these properties. If I now change the value by binding, the variable is also changed.
To simplify it, here's the code:
Here's the first property:
public MainDataObject CmObj_Temp { get; set; }
Which is used here:
public MainDataObject CmObj_MainData {
get {
return TemporaryDataStore.CmObj_Temp;
}
set {
TemporaryDataStore.CmObj_Temp = value;
this.RaisePropertyChanged(() => this.CmObj_MainData);
}
}
From which I set a variable here:
CmObj_Backup = TemporaryDataStore.CmObj_Temp;
or also like this (makes no different):
CmObj_Backup = ((VM)this.DataContext).CmObj_MainData;
And also use for binding here:
<TextBox Text="{Binding CmObj_MainData.Str_Internnr, Mode=TwoWay}"/>
Now if I change the text of the Textbox it also changes it here:
CmObj_Backup.Str_Internnr);
Can someone tell my why?
How can I change that?
Thx
This is an smaller form of my code:
public class DataObject
{
public string Str_Test1 {get; set;}
public string Str_Test2 {get; set;}
// --> Much more properties
}
public static class TempData
{
public static DataObject DObj1 {get;set;}
}
public class ViewModel
{
public DataObject DObj2 {
get {
return TempData.DObj1;
}
set {
TempData.DataObjet.DObj1 = value;
this.RaisePropertyChanged(() => this.DObj2);
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
var VM = new ViewModel();
this.DataContext = VM;
}
public void SomeWhereInTheSoftware()
{
((ViewModel)this.DataContext).DObj2.Str_Test1 = "Before";
ObjBackup = ((ViewModel)this.DataContext).DObj2;
((ViewModel)this.DataContext).DObj2.Str_Test1 = "After";
// --> Here is ObjBackup.Str_Test1 also "After"!!
}
}
If you would show full code blocks instead of randomly chosen lines of code it would be easier to follow. Your example isnt very clear to me.
However, I think you are having an issue because you think you are have 2 copies of an object when you really have 1. Objects are kept as reference so if you create a MainObject and a CopyObject you cant just set CopyObject equal to MainObject and expect to have a real copy.
Again, I could be way off given I dont understand your question fully but for example:
class A {
public string Message { get; set; }
}
public static void Main()
{
A mainData = new A();
mainData.Message = "Main Data Message";
A backupData = mainData;
backupData.Message = "Backup Data Message";
Console.WriteLine(mainData.Message);
// Prints Backup Data Message
Console.WriteLine(backupData.Message);
// Prints Backup Data Message
}
Edit: cloning as a solution
As Viv mentioned in the comment the solution to your problem would be to clone the object, which creates an actual copy of the object as opposed to a reference to the object.
you would update class A in this way:
class A : ICloneable
{
public string Message { get; set; }
public override object Clone()
{
A clone = new A();
clone.Message = this.Message;
return clone;
}
}
reference to ICloneable is here http://msdn.microsoft.com/en-us/library/system.icloneable.aspx

Updating a TextBlock Binding

I'm working on a WP7 app and I've had some trouble updating a TextBlock bound to a property. I'm new to MVVM, and C# in general, so I'm not sure what I'm doing wrong.
In the end I have solved this problem, but I don't understand why my solution works (always fun ...), so I'd really appreciate your guidance.
In my app's Model, I originally had something like this:
// Broken
namespace MyApp.MyModel
{
public class MetaData : INotifyPropertyChanged
{
private StatusType status;
public StatusType Status
{
get { return status; }
set
{
status = value;
statusMessage = ConvertStatusToSomethingMeaningful(value);
}
}
private string statusMessage;
public string StatusMessage
{
get { return statusMessage; }
private set
{
statusMessage = value;
// This doesn't work
NotifyPropertyChanged("StatusMessage");
}
}
...
}
}
Status is a enum, and when it's set by my app, it also sets StatusMessage too (which is a more human readable description to show the user). My View's TextBlock is bound to StatusMessage, but it doesn't update using the above code.
However, if I move NotifyPropertyChanged("StatusMessage") into Status, my View's TextBlock updates like it should. However, I don't understand why this works when the original code above doesn't?
// Fixed
namespace MyApp.MyModel
{
public class MetaData : INotifyPropertyChanged
{
private StatusType status;
public StatusType Status
{
get { return status; }
set
{
status = value;
StatusMessage = ConvertStatusToSomethingMeaningful(value);
// This works
NotifyPropertyChanged("StatusMessage");
}
}
public string StatusMessage { get; private set; }
...
}
}
Many thanks in advance for helping a newbie out :)
Issue in this line:
statusMessage = ConvertStatusToSomethingMeaningful(value);
StatusMessage setter is never called (NotifyPropertyChanged("StatusMessage") called exactly there)
StatusMessage = ConvertStatusToSomethingMeaningful(value);
will be the right call
Probably my implementation of this would be next:
namespace MyApp.MyModel
{
public class MetaData : INotifyPropertyChanged
{
private StatusType status;
public StatusType Status
{
get { return status; }
set
{
if (status != value)
{
status = value;
NotifyPropertyChanged("Status");
NotifyPropertyChanged("StatusMessage");
}
}
}
public string StatusMessage
{
get { return ConvertStatusToSomethingMeaningful(status); }
}
...
}
}

Categories