How to display objects with dynamic fields in wpf data grid? - c#

I want to display and edit some objects in a WPF data grid and I'm looking for a good way to do so. All objects I want to display have the same fields, but every execution the fields of my objects can differ. Here is a piece of the interface to illustrate what I mean:
public interface IMyObject
{
IEnumerable<string> GetFieldNames();
IEnumerable<Type> GetFieldTypes();
object GetField(string name);
void SetField(string name, object value);
}
How can I generate a data grid which displays this kind of objects? I thought of XAML generation to define the columns, but I'm still facing the problem of accessing the fields. I think I could realize this with value converters, another option would be to dynamically create a type which exposes the dynamic fields with properties.
Are there any other ways and which should I favor? I'm keen on hearing your opinions.
Best Regards,
Oliver Hanappi

I would do this to my interface
public interface IMyObject
{
IEnumerable<string> GetFieldNames();
IEnumerable<Type> GetFieldTypes();
//i would add this property, then you can bind directly to it.
//basically it is a collection indexer, indexed by string
object this[String name] { get; set; }
object GetField(string name);
void SetField(string name, object value);
}
I would build the columns in code like so, (where stringKeyCollection is a collection of strings returned from GetFieldNames() - although personally i would keep this information separate from my object - like a master definition)
foreach(String item in stringKeyCollection){
//create the base column (use whatever column type you want
DataGridBoundColumn column = new DataGridBoundColumn();
//create the binding for the column
column.Binding = new Binding("[" + item + "]");
//set the header
column.Header = item;
}
then you have objects in each cell of the grid and you can define templates however you wish.

The fact that the type of your bound objects changes each time is not hugely important, if your grid is set to AutoGenerate columns then it will create the columns for you. However this could lead to some rather un-pretty results.
What i would suggest is this:
with your data objects, annotate each displayable property with a custom attribute, this is simply to mark it for later inspection
once you have obtained your collection of items, take the first item in the list and pass it to a factory function that returns grid columns
the grid column factory function can inspect the data object using reflection looking for properties with the special attribute you used earlier, and create the appropriate grid column with the appropriate binding and value converter
add the collection of grid columns to the grid, and bind the data
this approach depends upon all the items in the collection being of the same type, but should be reasonably snappy.
If you have different items in the collection and they have little or no commonality then you could look to the method where you query each item for it's bindable properties and then mash the whole lot together.

Related

Hide Columns in DataGridView by field reference not column string name

I am writing a program where data is being displayed using a DataGridView and I was hoping there was a way to access the visible property of the columns without specifying the index location, or Column Name string value.
public class test {
public static string value1 { get; set; }
public static string value2 { get; set; }
}
I am using the LINQ to SQL datacontext to query information to be displayed into my DataGridView.
As it currently is, I can only seem to find a way to change the Columns visible property as so (assuming DataGridView is instantiated as dgvDATA)
dgvDATA.columns["value1"].visible = false;
Is there any way to do something similar to the following? I assume if there is it would be through databindings, but I tried that and couldn't figure it out.
dgvDATA.column.value1.visible = false;
I found this article stating that the DataGridView does not have this kind of ability built in but there was a workaround where you could add a database field representing if you want it visible or not.
HOWEVER the article was written in 2011 so I find it hard to believe that something like this was never implemented.
https://dotnetbuildingblocks.wordpress.com/2011/07/30/binding-datagrid-column-visibility-to-a-datacontext/
Please let me know if this is possible! Thank you
DataGridView doesn't support what you are asking for.
Anyway, if your only goal is to avoid using hardcoded field names, then using nameof() makes sense.
In your case it will be
dgvDATA.Columns[nameof(test.value1)].Visible = false;
That way you will have no issues refactoring your code later.
Apparently you want some kind of special DataGridViewColumn, where columns can be identified using some identification that is not available in a standard DataGridViewcolumn. For example you want to identify them by the PropertyInfo of the property that is shown in the column, or maybe the database column name of the database column whose values you show in this column.
In object oriented programming, if you want to create a special kind of DataGridViewColumn you should write a derived class:
class MySpecialDataGridViewColumn : DataGridViewColumn
{
public string DatabaseColumnName {get; set;}
}
Normally this would be enough: as long as you make sure that you only add MySpecialDataGridViewColumn objects to your DataGridView. When you fetch a column, typecast it to MySpecialDataGridViewColumn.
var theColumnThatDisplaysFirstName = myDataGridView.Columns // get all columns
.Cast<MySpecidalDataGridViewColumn>() // cast to your type
.Where(column => column.DatabaseCollumnName == "FirstName")
.SingleOrDefault(); // keep the ones with "FirstName"
Be aware that others will still be able to Add other kind of columns to your DataGridView. If you are afraid of this, make sure that you keep your members private and add functionality to Add / Fetch / Remove MySpecialDataGridViewColumns.
If your DataGridView is to be used by many, consider creating a UserControl that contains a private DataGridView, with functionalities to Add / Retrieve / Remove MySpecialDataGridViewColumn objects to the user control. This way others can't misuse your `DataGridView by adding other types of columns
Of course, if you want to allow others to add their own kind of Columns, you could always use OfType<MySpecialDataGridViewColumn> instead of a Cast. This way you ignore the other type of added columns, of which you are certain that they don't display your database columns

How to create an object which has a property that can only have a certain values

initially it looked a simple problem to me but the more I think more I am confused on what is the best way to achieve what I want.
We are making a WPF application following MVVM
so scenario is -
we have a class say MyClass which has a property of type Complex, it also has a property called Category of type int.
Public Class MyClass
Public Property Category As Integer
Public Property MyProperty As Complex
End Class
Now MyProperty can have a certain values only based on its category.
We have a list of an object which contains all possible values of MyProperty against each category.
My question is considering MVVM, where this list of possible items be placed?
should we make it part of the object like a new property which has all possible values of MyProperty and then have a check when we set the property ? or somewhere else ?
Public Property AvailablePropertyValues As IEnumerable(Of Complex)
Keeping it in object makes it real simple when we bind this object to view, as we don't have to filter the list but I know its trivial to filter lists for each item and we should not consider ease of creating view while modelling our objects.
Any ideas on how to model my object ?
I think you should have all the data that shows up on the screen come from the viewmodel via bindings, that includes the possible choices you want to select from, and the actual selected value, and then validate either through the setters (that's what i see setters to be used for, to insert custom validation logic based on the input value) or nicer, by implementing an IValidatableObject interface so you can also have UI notification of invalid values.
For IValidatableObject you can see the topic http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3 or http://msprogrammer.serviciipeweb.ro/2012/03/19/ivalidatableobject-and-idataerrorinfo/

CollectionViewSource Sorting Dynamically

I have a Car class with various properties.
I have a static Cars collection class in which, for simplicity, I have defined a bunch of car items which I can make available in XAML via an object data provider.
I have a collection view source defined in XAML which binds successfully to my ObjectDataProvider as it should.
I have a listbox which shows the collection.
I added the sort to the CVS as recommended in all the standard tutorials and all works fine.
My Question:
Suppose I want to sort on a different field. Surely there is a way to change this without having to give the code to the customer. So I implemented a combo box.
I use the following code to load a list of properties from the Car class into the combo box but I don’t just get a list of properties. I also get their data types. I don’t want this.
Car xyz=new Car(); //Make a temp Car Object so we can get a list of properties.
//Assign this to the combobox for listing.
cbxSortPrimary.ItemsSource = xyz.GetType().GetProperties();
Result (what displays in the combo box):
System.String Model
Double Price
Int32 NoOfPrevOwners
DataType PropertyName
ect... ect...
ect... ect...
ect... ect...
My goal is to load in the property names to the combo box. Then use the selected property name to build a line of code like:
myListBox.Items.SortDescriptions.Add(new SortDescription(ComboBox.SelectedItem.ToString(), ListSortDirection.Descending));
Where the ComboBox.SelectedItem.ToString() will contain the name of the property to be sorted on.
So how do I get rid of the data type in front of the property name. I know I can do all sorts of messy loading into another list, then a bunch of string handling looking for the first space from the right and chopping everything before that. But surely there must be an easier way.
Effectively what I am looking to do is to let the user sort on a different property of the Car class (So I need to load the properties somewhere and make them available for the user to select, hence the combobox). What I am asking is that there must be an easy way to get the list of properties without all the string manipulation code, and hopefully without much reflection (unless I am already using it without knowing), as it seems like a very basic requirement.
Thanks in advance for any help!
It's not messy at all:
var properties = new List<string>();
foreach (var info in typeof(Car).GetProperties())
{
properties.Add(info.Name);
}
cbxSortPrimary.ItemsSource = properties;

MVVM binding enum values : lots of proxy properties

I'm writing a ViewModel library which works with my WPF Custom Controls.
My problem is that my DomainModel has a large amount of Data Types:
Cd,Pens,Gadgets,Books,ecc.
All these Data Types are enumerated with an enum (I have more or less 1 hundred DataTypes), and each data Type corresponds to a DB table.
So the idea is to have a ViewModel library which exposes one property for each data type, thus my UI controls can directly bind the properties of my viewModel. The viewModel for each property return an ObservableCollection.
For instance, if I'd like to have my combo box populated with the "Gadgets" data, in my XAML I 'll have something like :
<my:XCombo ItemsSource="{Binding Gadgets}" .... />
and in my ViewModel I'll have :
public ObservableCollection<Gadgets> Gadgets
{
get
{
//get gadgets data from my domain model
return _model.GetData(DataEnum.Gadgets);
}
}
Now, in order to do that, I need in my ViewModel one property for each enumeration value, but I'd like to avoid to put 1 hundred property accessors. I'm lazy and this can be very error prone.
I know, in c#4 we have dynamic properties, so in this way I can avoid to write 100 property accessors, but I MUST use .net 3.5 which has no dynamic properties, i cannot use .net 4 ;(
Is there anyone who has already had this problem or any suggestion?
Thanks a lot in advance.
You could try to use an indexer property which returns the respective data
public IList this[DataEnum type]
{
return _model.GetData(type);
}
Then bind it using that:
ItemsSource="{Binding [Gadgets]}"

Bind an object which has a property of type List<> to a repeater

I've created two classes in business layer.
the first one is called Users with id (int), pass (string) and privileges (Privilege) properties and the second one is called Privilege and has id (int) and privilegeName (string) properties.
I've a method that returns all the users, and I use a repeater (actually I bind it to a DataList to auto create the ItemTemplate for me and then use a repeater) and it works and displays all the properties well except for my List property. it generates instead something like this System.Collections.Generic.List`1[WebApplication2.Public.BLL.Users]
I want to display it in a friendly way like "User Privileges : Privi1, Privi2" but still I want to keep the layers of my application clean and structured, for example I won't store them in a database in the same table and just store them as a text and append it.
I hope to find a simple and good solution...Thanks in advance guys =)
PS : I don't want to display the object Privilege, I want to display privilege.privilegeName
When using repeaters, there are two approaches, one is the one suggested by Bugai13: to have a custom property that displays it. This is fine for certain types of nested data.
Your other option is to just have a repeater inside a repeater, and bind it appropriately (to what would be a list assigned to your main data object, depending on how you O/R Mapper works).
You can have the code for the custom display property not in the data model, but in your presentation layer somewhere (depending on your framework/design), so it's not a "bad" thing to do that. It's up to you, with whatever "feels" best.
Just create property at your Bussiness object, and bind it:
public string PrivilegiesString
{
get
{
var sb = new StringBuilder("User Privileges : ");
foreach(var item in privileges)
{
sb.AppendFormat("{0}, ",item.privilegeName);
}
return sb.ToString();
}
}

Categories