Click here to Skip to main content
15,890,609 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hey guys,

I am kinda new here, hope my question isn't plain stupid.

I have a BO "TableRow" with properties Time and Value implementing INotifyPropertyChanged. Then I use a Collection (which implements INotifyCollectionChanged-Events) of TableRows to fill some DataGridViews, which are setup with virtual mode, with Data.

My problem is: The value of Time should be the same for all DataGridViews. The other property is specific to it's own DataGridView. So when i manipulate Row 1 of Column "Time" in one view, all views should update their first row with the new value.

Right now I am solving this by using a self implemented type ObservableDictionary<t> for the Value property on my BO. So each DataGridView can look up it's own value of Value with a key, but all will look at the same value for Time. (They are all bound to the same List of BOs.)

Is there an easier way to solve this? Meddling around with those Dictionaries makes things more complicated than I feel they should be.
Posted

1 solution

Not a solution - but maybe it'll help you get to one:

I'd set up a test framework without the UI objects - no DataGridViews - and explore in the debugger what I need to do to get the data to behave how I want it.

Once you've got your collections (and collection elements) properly notifying changes, and responding to changes as needed, only then would I start plumbing it into a UI (which often has its own quirky behaviours that can confuse things).

HTH,
Chris
 
Share this answer
 
Comments
Patrick Epting 14-Jan-14 11:35am    
Thank you for your advice. I will definitely leave the UI be, till the rest is working. But I am kind of missing the right idea. It is working as intended as it is right now, but I feel like it overcomplicates further work. (Which I need to do.)

Examples for problems I face:

- My DataGridViews create a column for each property via reflection. I will need to add DataGridViews that don't use all properties my BO offers. I could meddle with Attributes to solve this if I want to stick with my current solution. But then everything will get quite bloated. Or I could go with multiple BOs. So my BOs would have no Dictionary, just primitive values. Then I would need to create a collection of BOs for each of my later DataGridViews and keep all those collections updated when changes occur at one collection. Both doesn't seem compelling. Any ideas to improve here?

- Validation is very simple, so I would like to just call a validation method on the property setters. But with a Dictionary this isn't possible. (Don't think validation via attributes is simple possible, as the validation rules will be set on runtime. Attribute parameters are set at compile time as I know. Feel free to correct me there.)
Chris Ross 2 15-Jan-14 4:56am    
Re needing different sets of properties for the various DGVs - have you considered creating interfaces that express the column sets you require in your DGVs(one interface for each distinct set of columns)? Then, when the DGV does its reflection it should reflect on the type exposed (the interface) not the underlying type (the BO type).

Your BO then implements each interface. Here's some sample code for creating Collections you can pass to the various DGVs:

namespace DGVPlay
{
public partial class Form1 : Form
{
private readonly Collection<Data> m_myData = new Collection<Data>();

public Form1()
{
InitializeComponent();

Collection<ISubset1> s1 = new Collection<ISubset1>(m_myData.Select(d => (ISubset1)d).ToList());
Collection<ISubset2> s2 = new Collection<ISubset2>(m_myData.Select(d => (ISubset2)d).ToList());
}
}

public class Data : ISubset1, ISubset2
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DOB { get; set; }
public int SomeOtherThing { get; set; }
}

interface ISubset1
{
string Name { get; set; }
DateTime DOB { get; set; }
}

interface ISubset2
{
string Name { get; set; }
int SomeOtherThing { get; set; }
}
}
Patrick Epting 15-Jan-14 10:05am    
Well, just need to check if I am getting this right. I would now create:

interface ITime
{
int Time { get; set; }
}

interface IValueSet1
{
float Value{ get; set; }
}

interface IValueSet2
{
float SomeOtherValue { get; set; }
}

public class Data : ITime, IValueSet1, IValueSet2
{
public int Time { get; set; }
public float Value{ get; set; }
public float SomeOtherValue { get; set; }
}


Then 1 of my DGVs would look for ITime + IValueSet1 and the other would look for ITime + IValueSet2?

Can this be done in a dynamic way? I don't know how many values my application will be facing, before actual runtime. (Each DGV will always hold Time+Value, but there could be any number of DGVs.)
Chris Ross 2 17-Jan-14 7:14am    
Unless you have a specific need for ITime, you don't need that interface. Identical properties (and methods) can be declared on multiple interfaces - and all map to the same actual property (or method) on the class that implements the item. So, you can declare the Time property in both of the IValueSetX interfaces and in eac hcase it'll refer to the Time property on the actual class. With that change your DVGs don't need to look for multiple interfaces, they each just relate to a single interface.

Unfortunately, however, I must have overlooked the fact that your application needs things to be dynamic. That changes things. (Although in principal you could dynamically emit new code for dynamically designed interfaces, that's seriously advanced stuff and really only belongs in the world of compilers. It's not something for an end-user application to do.)

The need for an unknown (at compile time) set of variants means my idea won't work (unless you create interfaces for all combinations of Time + one other property ... and that seems ugly and isn't flexible).

Since you are dynamically creating DGVs you must have some way of knowing which property (+ Time) is to be associated with a DGV. I recommend (a) forget my idea of interfaces; (b) assign your Collection<BO> to the data source of each DGV, and, importantly, (c) programatically specify the columns to be presented in the DGV (i.e. don't use the automatically created set of columns the DGV creates for you).
Patrick Epting 17-Jan-14 9:05am    
Good point about the need of ITime on my example.

Maybe I should explain my current solution, which isn't that good, a bit further:

My BO looks like this:

public class BO: ObservableObjectBase
{
private int _time;

private ObservableDictionary<string,> _value = new ObservableDictionary<string,float>();

public int Time
{
get
{
return _time;
}
set
{
if (_time != value && value >= 0)
{
_time = value;
OnPropertyChanged(MethodInfo.GetCurrentMethod());
}
}
}
public ObservableDictionary<string,> Value
{
get
{
return _value;
}
}
}

BOs are put into a ObservableCollection<bo>.

Then I have created a UserControl which uses the DGV in Virtual Mode. I already found out, that normal DataBinding will most likely result in bad performance. At least my tests with it were quite bad. So I dont use the auto generated columns, as I can't use built-in DataBinding, but create them myself already using reflection. (Well, I guess that's the same way it would be done anyway.) This custom control has a property Database of type IList. When this property is set, my UserControl will look whether IObservableCollection is implented. If this is the case, it will subscribe to the INotifyPropertyChanged events.

Also my UserControl holds a property "ValueName" of type string, which is used to access properties of type ObservableDictionary. (Again I find out which properties implement IObservableDictionary using reflection.)

So every UserControl I create now contains a DGV with columns "Value" and "Time". Each UserControl has it's own key for accessing the Dictionary Value, but each of them will access the same property Time.

But well, this approach doesn't seem that good. For example when I need another DGV with columns "Time", "Value", "Bla", I would have to expand my BO. But then each DGV would create those columns. You told me to programatically specify the columns. Is there an easy way to do that? I feel like I am thinking all too complicated.

Also, I would like "validation" to happen on the setter. As that would be the simple way. (For example Time isn't allowed to be negative.) That's not possible with Dictionary properies.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900