IEnumerable in Model observable - c#

I am in a situation where I have to use a existing model which I am not allowed to change the existing Properties.
public class Car : ModelBase
{
private string _model;
public string Model
{
get { return _model; }
set { this.Update(x => x.Model, () => _model= value, _model, value); }
}
private IEnumerable<Person> _allowedDrivers;
public IEnumerable<Person> AllowedDrivers
{
get { return _allowedDrivers; }
set { this.Update(x => x.AllowedDrivers, () => _allowedDrivers=value, _allowedDrivers, value); }
}
}
Now we recently started implementing a WPF UI and I need to use these existing models. Is there any way I can use the IEnumerable and let it work like a ObservableCollection without changing it really? What are my options.
The thing is I remove a AllowedDriver and then add a AllowedDriver and the UI is not updating at all. This is logical, I then made(for testing purposes) the IEnumerable an ObservableCollection and then the UI works. Do I have any other options in keep using the IEnumerable but gets updated?

Is there any way I can use the IEnumerable and let it work like a
ObservableCollection without changing it really?
You can create another class which inherits your Car class. Since your ObservableCar inherits Car class you have access to AllowedDrivers property.
So, you can declare your desired observable collection and initialize with an observable collection converted from AllowedDrivers. This initialization should be inside get.
public class ObservableCar: Car {
public ObservableCar(){
_observableAllowedDriver = new ObservableCollection<Person>(AllowedDrivers);
}
private ObservableCollection<Person> _observableAllowedDriver;
public ObservableCollection<Person> ObservableAllowedDriver
{
get { return _observableAllowedDriver; }
}
}

You could simply assign an ObservableCollection to the AllowedDrivers property and operate on that collection:
var drivers = new ObservableCollection<Person>();
car.AllowedDrivers = drivers;
Now adding a new Person to the drivers collection will actually update the UI, because a bound UI element does a runtime check whether a collection implements INotifyCollectionChanged:
drivers.Add(new Person(...));
The above of course assumes that your ModelBase.Update method isn't doing anything strange, and eventually assigns the value argument of the property setter to its backing field _allowedDrivers so that the property getter returns the collection instance that was passed to the setter.
EDIT: If possible at all, it would make sense to change the Car class to use ICollection<T> instead of IEnumerable<T>:
public class Car : ModelBase
{
...
private ICollection<Person> _allowedDrivers;
public ICollection<Person> AllowedDrivers
{
get { return _allowedDrivers; }
set { this.Update(x => x.AllowedDrivers, () => _allowedDrivers=value, _allowedDrivers, value); }
}
}
You could then still assign an ObservableCollection, but get rid of the drivers variable:
car.AllowedDrivers = new ObservableCollection<Person>();
car.AllowedDrivers.Add(new Person(...));

Thanks for everyones input. I solved it via making/casting the IEnumerable to an ObservableCollection.
Like so:
public class Car : ModelBase
{
public Car()
{
AllowedDrivers = new ObservableCollection<Person>();
}
private string _model;
public string Model
{
get { return _model; }
set { this.Update(x => x.Model, () => _model= value, _model, value); }
}
private IEnumerable<Person> _allowedDrivers;
public IEnumerable<Person> AllowedDrivers
{
get { return _allowedDrivers; }
set { this.Update(x => x.AllowedDrivers, () => _allowedDrivers=value, _allowedDrivers, value); }
}
}
And then using it in my viewmodel or so I use it like this:
var allowedDrivers = (ObservableCollection<Person>)Car.AllowedDrivers;
allowedDrivers.Add(person)

Related

ObjectListView aspect name as other object property

I am new to ObjectListView and I haven't found how to use a property of a object inside the model as AspectName for ObjectListView.
For example, the list will show Cars names and their owner name
public class Person
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
public class Car
{
private Person _owner;
private string _name;
public Person Owner
{
get => _owner;
set => _owner = value;
}
public string Name
{
get => _name;
set => _name = value;
}
}
If I set the AspectName of a column to Owner.Name it doesn't show anything on the list. I have made a small workaround of defining a property OwnerName inside the Car model, but I have more complex models which is a pain to have to define that property.
Is there a way I can do that?
Thanks
Based on what you have said then this should work. So there must be some other problem which is causing this issue.
You could try setting the AspectGetter programmatically instead such as this;
this.olvColumn2.AspectGetter = delegate (Object obj)
{
Car car = (Car)obj;
if (car == null)
return "Unknown";
else
return car.Owner.Name;
};
You can then put a breakpoint inside this code and see when/if it is called and what happens.
Remember that you must define/set all of the aspect getters etc. before you call objectListView1.SetObjects

How to bind observebale collection to collections in the model

Basically this is a very very similar question to this one, with the big difference that I cannot easily "just use an observable collection inside the model"; a good example is the keycollection of a dictionary.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class Data
{
private Dictionary<String, String> _randomData;
public Data()
{
_randomData = new Dictionary<String, String>();
}
public ICollection<string> RandomDataKeys {
get {
return _randomData.Keys;
}
}
public void AddElement(string k, string v) {
_randomData[k] = v;
}
}
public class DataViewModel
{
private Data _data;
public DataViewModel(Data data)
{
_data = data;
RandomData = new ObservableCollection<String>(_data.RandomDataKeys);
//obviously above wouldn't work, since it just copies the keys.
}
public ObservableCollection<String> RandomData {get; set;}
}
Now what is the common approach here? A backup is to change the "add" function to tell the viewmodel that a new item is added (but that would require the model to have information about the viewmodel, explicitly calling a function on the viewmodel to tell the viewmodel to keep a duplicate of the model's data, this feels wasteful and slow).
The problem is, that when you're doing this RandomData = new ObservableCollection<String>(_data.RandomDataKeys); , you basically create a new instance of collection. So when you update your _data.RandomDataKeys, this changed are not reflected in your observable collection.
Read this, please
The best way would be to use ObservableCollection in your Data class, but if you don't want to do so, you can change your
public ObservableCollection<String> RandomData {get; set;}
To
public ObservableCollection<String> RandomData
{
get { return new ObservableCollection(_data.RandomDataKeys); }
}
But that is definately not a good idea, to create each time new collection.
So you can try to implement INotifyPropertyChanged interface in your Data class and bind directly to your _data.RandomDataKeys

How does WPF know to use INotifyPropertyChanged when I bind to IEnumerable?

In a view model(SomeViewModel below), a Data property returns IEnumerable<IData> where both interfaces do not implement INotifyPropertyChanged.
However, the underlying Data field is ObservableCollection<ObservableData> and both classes implement the INotifyPropertyChanged.
Finally in XAML, `Data is bound to a DataGrid.
<DataGrid ItemsSource="{Binding Data}" AutoGenerateColumns="True"/>
I thought this binding could introduce the binding memory leak described in KB938416, but to my surprise it does not.
When the method ChangeData is called, I can see DataGrid is updated and the OnPropertyChanged called a handler.
My question is: How does WPF know to use INotifyPropertyChanged when the bound data returns IEnumerable<IData> (that both do not implement INotifyPropertyChanged)??
public interface IData
{
string Name { get; }
}
// In addition to IData, implements INotifyPropertyChanged
public class ObservableData : IData, INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return this._name; }
set
{
if (_name == value) { return; }
_name = value;
OnPropertyChanged("Name");
}
}
// 'OnPropertyChanged' omitted for brevity
}
// here is some ViewModel
public class SomeViewModel
{
private ObservableCollection<ObservableData> _data = new ObservableCollection<ObservableData>();
// In XAML, a DataGrid's ItemsSource is bound to this.
public IEnumerable<IData> Data { get { return _data; } }
public void ChangeData()
{
// test OC's notification
_data.Add(new ObservableData {Name = "new" });
// test ObservableData's notification
_data[0].Name += " and changed";
}
}
In your case INotifyPropertyChanged is not needed for the Data property.
Data is of type ObservableCollection which internally implements INotifyCollectionChanged.
Due to this the view gets notified whenever you add or remove items.
Even though your Data property is returned with the type of IEnumerable<IData>, the object itself is still a ObservableCollection<ObservableData>. WPF can just use the is or as operators to test whether any particular object implements INotifyPropertyChanged, regardless of the handle provided.
IEnumerable<IData> test = Data;
if (test is INotifyPropertyChanged) {
//This if block enters because test is really ObservableCollection<ObservableData>
INotifyPropertyChanged test2 = (INotifyPropertyChanged)test;
}

How can I add Remove method to ObservableCollection?

I have a raddataform control which uses ObservableCollection as input source and auto generates fields.I have implemented Insert and Edit Logic in the person class itself which implements IEditableObject and INotifyPropertyChanged through BeginEdit and EndEdit methods. but public void Delete() method wont work there.also I learned that ObservableCollection has CollectionChanged event which has NotifyChangedCollectionAction.Remove .So how can I implement delete(remove) logic on ObservableCollection so that it can delete corresponding field using linq?
Here's code :
public class EmployeeDataContext
{
private ICollectionView employees = null;
public ICollectionView Employees
{
get
{
if (this.employees == null)
{
ObservableCollection<Person> newEmployees = new ObservableCollection<Person>();
DataClassesDataContext db = new DataClassesDataContext();
var query = from c in db.EPersons
select c;
foreach (var q in query)
{
newEmployees.Add(new Person((DateTime)q.EStartingDate, q.EFirstName,q.ELastName, (Person.OccupationPositions) q.EOccupation,q.EPhoneNumber, (int)q.ESalary));
}
//newEmployees.CollectionChanged += (sender, args) =>
// {
// if (args.Action == NotifyCollectionChangedAction.Remove)
// }
return this.employees;
}
}
}
Extract the DataConext object from your event handler's evenargs on your codebehind for BeginEdit and EndEdit.
Then call your viewmodel's Delete method referencing the DataContext retieved.
I'm not 100% sure that I fully understand your question, but if you're asking how you can add a custom Remove method to the ObservableCollection<T> class, then you can use Extension Methods. Perhaps something like this:
public static class ExtensionMethods
{
public static bool Remove<T>(this ObservableCollection<T> collection)
{
var someObject;
// custom logic here
return collection.Remove(someObject);
}
}

How to set CheckListBox itemsSource to all properties of a class object?

I am using WPF checkListBox And I want to populate its elements with all properties of the class given below. And I have a class named Person
namespace MyProject
{
public class Person
{
public enum PersonFields
{
PersonPermission1,
PersonPermission2,
}
bool _personPermission1;
bool _personPermission2;
public bool PersonPermission1
{
get
{
return _personPermission1;
}
set
{
if (_personPermission1!= value)
{
_personPermission1= value;
}
}
}
public bool PersonPermission2
{
get
{
return _personPermission1;
}
set
{
if (_personPermission2!= value)
{
_personPermission2= value;
}
}
}
}
}
I want to populate a checkListBox with its Properties dynamically. as in given image.
I you really want to get the names of all your properties, you can get a list of them like this:
typeof(Person).GetTypeInfo().DeclaredProperties.Select(prop => prop.Name).ToList(),
I noticed you also have a matching inner enum, so you could use its values instead:
Enum.GetNames(typeof(Person.PersonFields));
In both cases you'll still need additional code to set property values based on user actions.
I think a better approach would be to have a Dictionary of permissions:
var personPermissions = new Dictionary<Person.PersonFields, bool>
{
{ Person.PersonFields.PersonPermission1, false },
{ Person.PersonFields.PersonPermission2, false }
}
Now you could bind the Dictionary to ItemsSource, display Key and bind Value to checkbox.
Yeah I got my answer...
chkListBoxPerson.ItemsSource = typeof(Person).GetProperties();
chkListBoxPerson.DisplayMemberPath = "Name";
Here chkListBoxPerson is the name of my CheckListBox.

Categories

Resources