How do you combine the values of a property from multiple objects - c#

i have a class that has a property that has the Mergeable attribute applied to it.
public class MyObject
{
[MergableProperty(true)]
public double Time{get; set;}
}
When i select multiple of these objects and send them into a propertygrid i want the property grid to show the combined values of the selected objects for that property. How to you handle the Merge Attribute?

Related

MVVMCross add multiple dynamic properties to viewmodel

I have an requirement to render the screen (screen type : form) dynamically based on the service response (rather than defining the UI manually). I was able to successfully bind predefined properties to dynamically created textboxes & textviews. Following is the sample code that used to bind predefined property to dynamic textbox
Type myType = typeof(DynamicViewModel);
PropertyInfo myPropInfo = myType.GetProperty(nameof(dynamicProperty)); //dynamicProperty -static property in VM
var set = this.CreateBindingSet<DynamicActivity, DynamicViewModel>();
set.Bind(editText).To(myPropInfo.Name); //editText - dynamically created textbox
set.Apply();
But the code needs to be further improved by dynamically creating the no of properties - matching with the no of UI elements dynamically created.
The project is created using Xamarin.Android with MVVMCross's latest version. Please share the way to generate dynamic string(or object type) properties in viewmodels that can be binded with dynamically generated view elements(textboxes & textviews).
Thanks
There are a couple of ways to do this.
One is using Reflection as you are doing there, but you could have performance issues.
The other way is to arrange a bit the data and model you are getting from the server to be something like you can then use some Factories to build your View/VM:
So it could be:
public enum DataType
{
String,
Number,
Boolean,
List,
// and any other types that you need
}
public class OptionItemModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class FieldModel
{
public DataType Type { get; set; }
public string DefaultValue { get; set; } // this is a serialized value
public List<OptionItemModel> Options { get; set; } // this only applies to DataType -> List
}
public class StructureModel
{
public List<FieldModel> Fields { get; set; }
}
So then you can have an ObservableCollection on your VM and your items can be created by a factory iterating for each one of the fields of the structure and so you can have custom Item View Models depending on the DataType of the field.
Then you can have a List on your View that uses a Template selector where you can create the Cell/Row depending on the DataType or the ItemViewModel and that would be it.
Then you can have a similar model structure to fill the values and upload them to the server.
Hope it's clear enough

Sortable BindingList for custom column

I have a DataGridView which is data-bound to a BindingList. My DataGridView also has a couple of custom columns that I have added. These are not data-bound, but rather are generated based on items in my BindingList
(ie: an item in my BindingList of type A has a property of type B; my custom column shows B.Name
In this case, "Name" is a property of class B, and thus the property represented by the column is not directly found in the items in the BindingList).
Can anyone help me for this?
I wanted to make it generic.
e.g.
public class Fruits
{
public String Id {get;set}
public String Name {get;set}
}
public class People
{
public String ID {get;set}
public String Name {get;set}
public Fruits FavouriteFruit{get;set}
}
In BindingList as we can do sorting using custom sortable list for the properties on People, in the DatagridView but, I also wanted to sort People based on the FavouriteFruit.
You can inherit DataGRidView and add the required properties.

WPF: Best way to create bindings to unknown types in MVVM

I am looking for a way to display data in a DataGrid from types that are unknown at compile-time.
I have the following base class
public abstract class Entity
{
// Some implementation of methods ...
}
In run-time, I load a plug-in DLL and use reflection to get a list of all the types derived from Entity. For example:
public class A : Entity
{
public LocalAddress Address{ get; set; }
}
public class B : Entity
{
public Vendor Vendor { get; set; }
public string Name { get; set; }
}
Then I retreive a list of their instances from DB
public IEnumerable<Entity> Entities { get; set; } // A list of instances of type A for example
Entities is the DataGrid's ItemsSource, But what's the best way I can bind the properties to the DataGrid?
Since the properties can be complex, I also need to be able to bind to a specific path, for example Address.HomeNum ...
Clarifications
I only need to show a one grid of a type's instances at a time. The complete scenario is this:
I get a list of types that derive from Entity from the plug-in DLL through reflection
I show their names in a List. (in this example that list will contain A and B
When the user clicks on a specific item, let's say A, I get a list of A instances from DB - so far so good.
I want to display that list of A's instances in a DataGrid.
When the user selects another item from the list (meaning another type, lets say B), I get a list of B's instances from DB and need to display those in the grid and so on ...
The plug-in DLL is a class library with no xamls (also my users are the ones making this plug-ins and I don't want them to have to write DataTemplates for their entities.
I also can't make predifned DataTemplates as I don't know the types I'll need to display until run-time. Each type can have different types and amount of properties. All I know in complie-time is that they all derived from Entity.
The grid should also be editable.
A DataGrid seems inappropriate in this case. If your list was bound to two separate entities, it would break badly.
A better option would potentially be to use some other ItemsControl and set up a DataTemplate for each type of Entity. This would allow you to build custom editors per entity, and have a "list" of them to edit.
If you know the entities will always be of a single type, I'd instead build the collection of that specific type, and bind to it.
Since you don't know the property names of the Entities beforehand, I think your best option is to keep your DataGrid in Xaml but move the defintion and the Bindings of its DataGridColumns to the code behind.
AddColumnsForProperty(PropertyInfo property, string parentPath = "")
{
var title = property.Name;
var path = parentPath + (parentPath=="" ? "" : ".") + property.Name;
if(property.PropertyType == typeof(string))
{
var column = new DataGridTextColumn();
column.Header = title;
column.Binding = new Binding(path);
dataGrid.Columns.Add(column);
}
else if(property.PropertyType == typeof(bool))
{
//use DataGridCheckBoxColumn and so on
}
else
{
//...
}
var properties = property.GetProperties();
foreach(var item in properties)
{
AddColumnsForProperty(item, path);
}
}
Now if you execute these you'll have your dataGrid columns filled. and by adding all instances of the desired type in an observable collection and bind it to ItemsSource of the DataGrid it should work. selectedItem should be an instance of one the classes derived from Entity. The listbox contains new A() and new B() (or any existing instances of A and B) so selectedItem can be used in the following statement.
var propertyList = selectedItem.GetType().GetProperties();
foreach (var property in propertyList)
AddColumnsForProperty(PropertyInfo property);
how to write DataGridColumnTemplate in code
Edit:
Member can't be used in this scenario because INotifyPropertyChanged should get involved, so I replaced members with properties.
I would use attributes to specify what exactly is bindable (including composite object):
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public abstract class EntityAttribute : Attribute
{
internal abstract IEnumerable<EntityColumn> GetColumns(object instance, PropertyInfo property);
}
This attribute supports plain properties as well as composite structures. You should simply inherit and implement the method.
EntityColumn represents single value. Simplified version can be implemented like this:
public class EntityColumn
{
private readonly Action<object> _setMethod;
private readonly Func<object> _getMethod;
public string Caption { get; private set; }
public object Value
{
get { return _getMethod(); }
set { _setMethod(value);}
}
internal EntityColumn(string caption, Action<object> setMethod, Func<object> getMethod)
{
_getMethod = getMethod;
_setMethod = setMethod;
Caption = caption;
}
}
Later you can create single DataTemplate for EntityColumn and use it for all properties for all possible entities. Entity Object will contain additional method to return all EntityColumn relevant to it:
public IList<EntityColumn> GetColumns()
{
var objectType = GetType();
var properties = objectType.GetProperties();
return properties.SelectMany(
p => p.GetCustomAttributes<EntityAttribute>().SelectMany(a => a.GetColumns(this, p))).ToList();
}
For collection of Entities you can introduce EntityCollection which will absorb column information and provide structure similar to DataSet.
This implementation gives you flexibility of dynamic structure and keeps almost everything strongly typed. You can even extend attributes and EntityColumn to support validation.
As of displaying object, you'd rather use ItemsControl or even self written control inherited from ItemsControl to take advantage of knowing about Entity and EntityCollection classes.

ListView with custom item layout in C#

I have a class called EventBox that extends TableLayoutPanel. It's a table with one single row and dynamically adjusting number of columns.
During its lifecycle, this EventBox adds/removes items from itself (buttons, combo boxes etc).
What I want is to create a ListView (or something similar) that would contain multiple EventBox objects and visually display them in a list.
I've created a class called TestEventList, but I do not know what to extend!
I've tried TableLayoutPanel (I believe it's overkill), ListBox (wrong!) and now ListView.
However, ListView's Items property has a method Add which only accepts ListViewItem objects as parameters.
How can I describe my EventBox as a ListViewItem?
Or better yet, what other choices do I have?
EDIT: I obviously want the list to be able to keep track of its items: add, remove at index etc.
Firstly, ListView will not do anything on its own. You need to set ListView.View to an instance of GridView.
I recently had to solve the dynamic column problem. The solution I chose is bindable and MVVM compatible, just in case you want to use that pattern (i was). I created a behavior (to avoid extending GridView) that will dynamically inject and remove columns as a source structure updates. This behavior needs dependency property that you bind to a instance of a class that defines the columns. The column class should allow you to define columns where a column is the property you are binding to on the source data, and a key (to represent the cell type).
public class ColumnDefinition
{
public string Key{ get; set}
public string ContentBindingPath { get; set;}
}
When the columns structure changes, the behavior builds and injects (or removes) columns into the attached GridView. The behavior builds each column based upon a series of key/value pairs defined on the behavior. This is to allow the XAML to specify the cell template to apply to the new columns, enforcing seperation of concerns.
public class CellTemplateDefinition
{
public string Key { get; set; }
public DataTemplate ColumnTemplate { get; set;}
}
public class DynamicColumnBehavior: Behavior<GridView>
{
public IEnumerable<ColumnDefinition> Columns
{
get { return (IEnumerable<ColumnDefinition>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
// Using a DependencyProperty as the backing store for Columns. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(IEnumerable<ColumnDefinition>), typeof(DynamicColumnBehavior), new UIPropertyMetadata(null));
public static void OnColumnsChanged(DependencyObject sender, DependencyPropertyChangedEventArgsargs)
{
DynamicColumnBehavior behavior = sender as DynamicColumnBehavior;
if(behavior != null) behavior.UpdateColumns();
}
public IEnumerable<CellTemplateDefinition> Cells { get; set; }
private void UpdateColumns(){ throw new NotImplementedException("I left this bit for you to do ;)");}
}

Binding Custom List Property To DatagridView

I have a problem that is difficult to explain. Essentially I have a list of a certain class we can call MyObj. One of the properties of this object is a custom list itself. I would like to bind this List to a dataGridView and have this particular property that is also a list show up. Any ideas? Am I being clear enough? :-P..
Here is the idea. I have my own custom list object overriding the ToString() method:
public class CategoriesList : List<Category>
{
public override string ToString()
{...}
}
This is used as a property in an object such as:
public MyObj
{
public string Property1 {get; set; }
public string Property2 {get; set; }
public CategoriesList Categories {get; set; }
}
In turn, I have a list of these objects such as:
List<MyObj> myDataSouce = SomeRepository.GetMyObjList();
Where I bind this to a datagrid view:
MyDataGridView.DataSource = myDataSource;
Property1 and Property2 are automatically generated. Is there any way to have the CategoriesList property be added as well? I previously thought Overriding the ToString() method on a class would be enough..
I am really lost on this one as I have no idea how to even google for it :-P
Assuming that you'd like to display a specific value in place of the list in the datagridview, you'll want to use a custom TypeConverter. Otherwise you'll need to place a control in the datagridview column that supports lists, like a drop down list and bind to that.
For the former:
Basically decorate your categories property with a custom typeconverter:
[TypeConverter(typeof(MyConverter))]
public CategoriesList Categories { get; set; }
Then use a custom type converter that basically tells the datagrid that when it encounters the categories property what do display:
public class MyConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is CategoriesList) {
return value.ToString();
}
return base.ConvertFrom(context, culture, value);
}
}
You'll need to add your column to be databound manually by adding an unbound column and specify the DataPropertyName for the property to be mapped to that column, in this case "Categories"
If you're looking to display second level properties as well then this may help:
http://blogs.msdn.com/b/msdnts/archive/2007/01/19/how-to-bind-a-datagridview-column-to-a-second-level-property-of-a-data-source.aspx
This might help... look at my answer there, I haven't tried it with a property that is also a type of list but I think the idea is the same.
Or this one as well, I also have an answer there with a sample code too...

Categories