ObjectListView aspect name as other object property - c#

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

Related

C# How to pass Properties in a generic way without Reflections?

Currently I have three properties in class MyClassOne. The number of properties may increase in future. Whenever any of the property changes, I need to call a method called SavePropertyToFile() which is responsible to set the corresponding property (AnotherPropertyOne, AnotherPropertyTwo or AnotherPropertyThree) of another class.
My sample code (which is of course not working) is below:
Class MyClassOne
{
public bool PropertyOne
{
get => _propertyOne;
set
{
if (_propertyOne == value)
return;
_propertyOne = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyOne); //<------------------
NotifyOfPropertyChange(() => _propertyOne);
}
}
public bool PropertyTwo
{
get => _propertyTwo;
set
{
if (_propertyTwo == value)
return;
_propertyTwo = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyTwo); //<------------------
NotifyOfPropertyChange(() => _propertyTwo);
}
}
public bool PropertyThree
{
get => _propertyThree;
set
{
if (_propertyThree == value)
return;
_propertyThree = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyThree); //<------------------
NotifyOfPropertyChange(() => _propertyThree);
}
}
SavePropertyToFile(bool value, TProperty myPropertyOfAnotherClass)
{
myPropertyOfAnotherClass = value;
// Some more lines of Code
RefreshProgram();
}
}
In the code above, I need to find a way so that I can parse the correct property of AnotherClass. I already had a look at this another SO question, but I am not able to apply any of the answer for my case.
How can I parse different properties to a method?
Update: As per the suggestions in the comments, I have tried to use "Nathan Baulch" answer's option-2 but then I getting warning/error wiggly line as shown in the screenshot below:
If the idea is to make several classes use the same property, my approach is to wrap the values in a class:
public class Changeable<T> : INotifyPropertyChanged
{
private T currentValue;
public Changeable(T initialValue) => currentValue = initialValue;
public T Value
{
get => currentValue;
set
{
if (!EqualityComparer<T>.Default.Equals(currentValue, value))
{
this.currentValue = value;
Changed?.Invoke(this, value);
OnPropertyChanged(nameof(Value));
}
}
}
public event EventHandler<T> Changed;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You can then just let both classes use the same actual object. This approach can also be used to cut down on boilerplate for wpf, instead of declaring getters and setters you can just write:
public Changeable<bool> MyProperty {get;} => new (false);
Just remember to update the binding: {Binding MyProperty.Value}
You can also write various extensions. For example if you want to update one property whenever some other property updates:
MyProperty.BindTo(MyIntProperty, intValue => intValue > 0);
So whenever MyIntProperty changes sign, the MyProperty would be updated and raise its changed event. I'm not going to include all the code behind this, since it is fairly large, but I hope you get the idea. This approach makes it possible to link various UI values in a convenient way, but it can also make the code more difficult to understand and debug, so some caution is advised.

IEnumerable in Model observable

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)

Binding with dynamic columns to a datagrid with mvvm

So I have a WPF MVVM application with a datagrid, and I'm having some trouble setting up the datagrid. I have simplified down the model to make this easier to see the issue, but try to assume that the model can not change (unless for some reason it absolutely has to). I have an observable collection of a custom object as my item source. Let's call the object MyObject. So I have this as my datagrid's itemsource:
ObservableCollection<MyObject> myObjectCollection;
Each object contains an observable collection of another type of custom object. Let's call these new objects MyObjectElement. So each MyObject has the property:
ObservableCollection<MyObjectElement> myObjectElementCollection;
Each MyObjectElement has 3 properties:
string Name;
string Element;
bool IsField;
It can safely be assumed that each MyObject has the same number of MyObjectElements in the myObjectElementCollection, and they all have matching Name and IsField properites. I want a column for each MyObjectElement in a MyObject's myObjectElementCollection only if the IsField property is set to true. Element is the value of each cell.
Now that is the first part of the problem: getting that DataGrid to generate with support for two-way binding. The second part of this problem is the DataGrid Column Types.
I have 2 classes, MyObjectImage and MyObjectTextBox. These both inherit from MyObjectElement. The collection myObjectElementCollection in MyObject does not actually contain any MyObjectElements. It only contains MyObjectImage and MyObjectTextBox objects. If the type of of MyObjectElement is MyObjectTextBox, then it should just show the string.
However, if the type is MyObjectImage the datagrid column should show an image. MyObjectImage contains a property that is a string called ImageType. This string will always be 1 of three values: "PNG", "XAML", or "SVG". If it is equal to "PNG", it is safe to assume that Element is a base64 string of the png. If it is equal to "XAML" it is safe to assume that the image is stored as a large XAML string in Element. If it is equal to "SVG", then it is safe to assume that the image would be an svg as a string, which I do have a converter function that will turn that into a XAML string. The user would change out the image via double clicking on the cell, though that is semi-irrelevant to the problem. It is also safe to assume that the ImageType property will match with it's associated ImageTpe properties in the other myObjectElementCollections.
If you are able to input any advice to any part of this datagrid problem, that would be awesome! I've been banging my head on this for weeks! To make things a bit easier, here is some sample code of the model's structure (it is safe to assume that INotifyPropertChanged is implemented in the class ObservableObject):
My Object
public class MyObject : ObservableObject
{
private ObservableCollection<MyObjectElement> _MyObjectElements;
public ObservableCollection<MyObjectElement> MyObjectElements { get { return _MyObjectElements; } set { if (_MyObjectElements!= value) { _MyObjectElements= value; RaisePropertyChanged("MyObjectElements"); } } }
}
MyObjectElement
public class MyObjectElement : ObservableObject
{
private string _Name;
private string _Element;
private bool _IsField;
public string Name { get { return _Name; } set { if (_Name != value) { _Name = value; RaisePropertyChanged("Name"); } } }
public string Element { get { return _Element; } set { if (_Element != value) { _Element = value; RaisePropertyChanged("Element"); } } }
public bool IsField { get { return _IsField; } set { if (_IsField != value) { _IsField = value; RaisePropertyChanged("IsField"); } } }
}
MyObjectImage
public class MyObjectImage : MyObjectElement
{
private string _ImageType;
public MyObjectImage() : base()
{
}
public MyObjectImage(string name, string element, string imageType) : base(name, element)
{
ImageType = imageType;
}
public string ImageType { get { return _ImageType; } set { if (_ImageType != value) { _ImageType = value; RaisePropertyChanged("ImageType"); } } }
}
MyObjectTextBox
public class MyObjectTextBox : MyObjectElement
{
public MyObjectTextBox() : base()
{
}
public MyObjectTextBox(string name, string element) : base(name, element)
{
}
}
Again, any and all help is appreciated with this problem; Thank you!
I was able to accomplish the first part of the problem through a method that can be found here. The second part of this problem should be able to be accomplished through templating the incoming items.I can easily create an image property that does a conversion to a bitmap image from any of those as the display for the datagrid column.

Determine if a property value is set by user (i.e. binding)

Preamble: I am making a logging tool for our company needs. We use MVVM - pattern in our projects via Caliburn. At this moment my tool is capable of weaving compiled code via Mono.Cecil assembly and injecting call of a logging method in public properties set methods.
Fabula: Let's say, I have a class Person
public class Person
{
public string Name
{
get { return _name; }
set
{
Logger.MyLogMethod(_name, value, "Person");
_name = value;
}
}
private string _name;
}
and a class Logger
public static class Logger
{
public static void MyLogMethod(string oldvalue, string newvalue, string propertyname)
{
//condition to log only user changes
}
}
And an instance of a person has its Name property binded to textbox
<TextBox Text="{Binding SelectedPerson.Name}"/>
And we have a button, which click calls a code, which will set new value to SelectedPerson.
So, we have two ways of setting our Person's Name property at a runtime. It's either by user typing in textbox, or a user clicking a button, therefore calling some code.
I would like to know: how to distinct these two cases. In other words - when a set method of a property is called - how to determine, if it was called by binding engine or by our code.
So far I have one approach that is doable in my opinion - to take a look at StackTrace. Not sure how to do it thought.
You can make the Name property private. Then you expose a public property for binding and another for the rest of the application.
You can know differentiate a call from binding from a call from somewhere else
public class Person
{
string NameForOthers
{
get { return Name; }
set
{
Logger.MyLogMethod(_name, value, "Person name set by other place");
Name= value;
}
}
public string NameForBinding
{
get { return Name; }
set
{
Logger.MyLogMethod(_name, value, "Person name set by binding");
Name= value;
}
}
string Name
{
get { return _name; }
set
{
Logger.MyLogMethod(_name, value, "Person");
_name = value;
}
}
private string _name;
}
Then you use it for your binding
<TextBox Text="{Binding SelectedPerson.NameForBinding}"/>

ReactiveUI RaiseAndSetIfChanged() Backing Field Accesibility

Problem: Cannot set backing field of property to private as I get the following exception when setting a value to Name.
System.ArgumentException : You must declare a
backing field for this property named: _Name
My Code:
public class MyVM : ReactiveObject
{
private string _Name;
public string Name
{
get { return _Name; }
set { this.RaiseAndSetIfChanged(x => x.Name, value); }
}
}
To Fix this i have been able to set _Name to public:
public string _Name;
Which fixed the problem, but why do I have to expose the backing field as public? The examples I see on the Net use private backing fields...?
Use the new overload instead, unless you're not using >= VS2012:
this.RaiseAndSetIfChanged(ref theBackingField, value);

Categories

Resources