Best way to manage ListViewItems in a Detailed ListView? - c#

I've adopted the following pattern for putting ListViewItems in a ListView with multiple columns (when I want to display information about a list of MyObject types), and I'm just curious to see if this is the best way to accomplish this task, or if there's anything more efficient and readable in code:
Create an inherited ListViewItem class that takes a MyObject object in the constructor - I'll call this MyObjectListViewItem - and a Refresh() method that clears and re-populates the ListViewItem subitems.
Populate the ListView with my new MyObjectListViewItem items.
example:
public MyObject MyObject { get; set; }
public MyObjectListViewItem(MyObject myObj)
{
MyObject = myObj;
this.Refresh();
}
public void Refresh()
{
this.SubItems.Clear();
this.Text = myObj.FirstColumnProperty;
this.SubItems.Add(myObj.SecondColumnProperty); // etc...
}
Suggestions? Better ways?

Have you considered using a BindingSource, or creating your own which implements IBindingListView? This keeps concerns about the data and its state scoped to the data itself and not on any controls which consume it. Since .NET controls are already built to work with BindingSources, you can take advantage of some more robust functionality. Instead of explicitly invoking a screen refresh, the control is simply responsible for responding to events raised by the binding source, and a controller that notifies whether the control is ready to be refreshed without forcing it.

Making ListViewItems that know how to build themselves is a good idea.
If you extend that idea a little, you make the columns know how to build each subitem, which also allows them to be able to automatically sort the ListView, support grouping and copy/drag and drop rows. This is just a few of the things that ObjectListView does for you.
ObjectListView is an open source wrapper around a .NET WinForms ListView control that makes the ListView much easier to use -- as well as adding some very nice new features and getting around some annoying bugs/limitations.
If you did like #Rex's idea of using a BindingSource, the ObjectListView project also provides a data-aware DataListView which is data bindable.

Related

How can I reset a ComboBox when the underlying list changes?

I'm currently working on a WinForms graphical interface in C#. The form has a ComboBox that is displaying the contents of a custom class I wrote. This class implements the IList interface so that it can be used as the DataSource for that ComboBox.
The issue is that when I add or remove items to/from the underlying object, the ComboBox does not visually reflect this.
I have tried reassigning the list to the DataSource property whenever the contents change, but that doesn't seem to make a difference.
Most of the answers on this site that deal with this issue or similar seem to require a number of extra classes, or editing XAML documents, in order to achieve this.
Is there a simpler method to achieving this? If it makes a difference, the ComboBox will not be visible at the point where changes are made to the IList sourcing it. The only idea I've had so far is to possibly delete the ComboBox and replace it with a new one when needed, but I'm not sure if this is possible to do at runtime, and is obviously not very elegant either way.

How to manage data bindings in a 100% custom grid control

I'm writing a custom control from scratch (using Graphics) that will represent a set of columns each of which will have a list of cells, a lot like a grid view except this will have some custom features.
I'm having a hard time trying to figure out how to manage all the data binding problem. I'd like this grid to have a DataSource property which would basically need to be a jagged array (2D) of some objects each of which would need to be bound to a cell in the control. I'm still not sure which properties each of these objects will have, and so far I'm only using text. What I've been trying to do is have my model implement this interface:
public interface IDiagramDataSource
{
IBindingList Columns { get; }
}
and then have each column implement this:
public interface IDiagramColumnDataSource : INotifyPropertyChanged
{
IBindingList Cells { get; }
}
But I'm still having a hard time figuring out the best way to keep all these objects synchronized to the corresponding objects in my grid. I'd love to be able to put all the binding mess in an independent class but not sure how to do this. For now I'll just repaint the whole thing whenever there is any change in the data source, but I'd like to eventually be able to identify which property in which cell or which cell in which column have changed so that I can update only the necessary items in my grid.
Is there any pattern to follow when you want a control to be bindable to nested lists? I'm guessing there is since the DataGridView control does this.
I would suggest 2 option:
First suggestion has been suggested by HighCore, is to using WPF rather than custom winform object to do the job. WPF also has much more controls (ListView, even the generic ItemContainer, if I am not mistaken). Just implement the INotifyPropertyChanged at the ViewModel, then call OnPropertyChanged for the root (aggregate) element everytime you need to refresh the control. Don't forget to add UpdateSourceTrigger for ItemsSource Binding. WPF controls also has good implementation for drag and drop function.
I won't detail the implementation in WPF since there are much articles explaining it.
However if you think that it must be done with custom graphic drawing, then it is quite complex. You must trigger the method to re-draw your control graphic everytime any changes occured in the item list for it to take effect. It will be better if you can do partial update to your graphic (maybe using panels to trick it), but it is harder.
If not handled correctly (moreover if your control contains huge amount of data and/or control size) then it can causes some high memory loads.

WPF DataGrid: handling CanUserAddRows=true cleanly with MVVM

I've been looking into MVVM lately, and after I discovered Caliburn.Micro things have been pretty swell; I'm still in the early learning stages, but I belive I have an OK feel for the MVVM basics.
I'm bumping into issues with WPF DataGrid, though - and it's pretty much the same issues that I had with WinForms DataGridView: how the heck do you handle CanUserAddRows=true in-grid item adding cleanly?
I obviously don't want to add DataGrid-specific hacks to my ViewModel, since it ideally should be repurposable for other View controls. At the same time, I'd like to be able to get a notification when a new row item has been added, so I can persist it right away.
I'm binding the DataGrid to a BindableCollection<FooModel> FooItems - with a clean MVVM design, if I understand things correctly, I would be able to handle FooItems.CollectionChanged and react to Add/Remove events. However, the DataGrid fires the Add event as soon as a default-constructed item is added - this is obviously not the right time to persist the object!
After a lot of googling and digging through StackOverflow, I'm getting the impression that DataGrid is being utterly retarded in how it's firing the Add/Remove events. People who use it with CanUserAddRows=true seem to only work on in-memory collections, where people who persist data seem to use separate input fields + buttons Commands to add new items.
My FooModel implements INotifyPropertyChanged but not IEditableObject - as far as I can tell that shouldn't be the problem, though, since IEO seems related to property edit/undo, whereas my problem is with when the Add event is fired...
So, what do you do to handle in-grid editing cleanly?
It sounds like the WPF DataGrid behaves in much the same way as the WinForms DataGridView, in that it creates an item in the data source as soon as the user begins entering into the 'new row'. Subsequent edits would result in changes to the properties of the new item in the collection.
If you were to use BindingList<FooModel> instead, you get the additional event called ListChanged - providing your FooModel type implements INotifyPropertyChanged, the event will fire when the properties of the item are changed (as well as when items are added/removed from the collection).
Hope this helps!
I know it's been a long time since this was asked but I'm working on something similar now and thought I'd post my solution. You may consider this a hack but it's the best way I could figure to get DataGrid to tell me when it was out of edit mode (for my own reasons).
I looked through the DataGrid code and found the closest thing to end edit I could override, which turns out to be OnExecutedCommitEdit(). I then just raise an event after that function finishes. When my event subscriber gets called DataGrid is no longer in edit mode and I can do whatever I need to it. Here's the code:
/// <summary>
/// Deriving a new DataGrid because we need an event to tell us when editing is complete.
/// </summary>
public class DataGridMod : DataGrid
{
public delegate void EditCompletedDelegate();
public EditCompletedDelegate EditCompleted;
public DataGridMod() { }
protected override void OnExecutedCommitEdit(ExecutedRoutedEventArgs e)
{
base.OnExecutedCommitEdit(e);
if (EditCompleted != null)
EditCompleted();
}
}

WinForms data binding with a Save button?

How is data binding in C# WinForms supposed to work when you have a Save button? I don't want the data updated until I press Save!
I have two forms (list and detail) backed by a BindingList<T> collection and my custom object from that collection, respectively. I can bind each form to the list or object appropriately. However, any changes made in the detail form are immediately reflected in the list form - I don't want to save the changes and update the details shown in the list until the Save button is pressed.
Is data binding designed to support this? Is there a common pattern for doing so?
Whichever way I look at it, binding doesn't seem to be able to support this scenario. I've considered the following:
Pass a clone of the object to the detail form, but then I have to reconcile the changes on Save - changes may have been made to the copy in the list in the meantime.
Implementing IEditableObject and calling EndEdit on save almost works as I can prevent the list being notified of the changes made until Save is pressed, but if something else causes a refresh the list is updated with the interim data.
I'm currently left with dispensing with data binding in my detail view, and doing it all manually. Which is rather annoying.
Data binding really wasn't designed for this kind of thing, unfortunately.
The best solution we've found here is to edit object clones, then reconcile the changes, as you mentioned. You may want to come up with a custom collection and/or object interface that helps you manage this type of thing.
Even if the cloning method is slightly more work up front, it's going to be wayyyy less complicated and frustrating than using IEditableObject trying to catch all the possible events that update the data. Not only that, it's a more straightforward approach, and you won't end up with spaghetti code.
If you are set on using a binding list, your best bet would be to implement IBindingList to create the functionality that you desire. It may also be possible to pull this off by simply inheriting from BindingList and overriding the appropriate methods to change the binding list's behavior.
http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx
If you are not set on using a binding list, it is probably best to do the data manipulations manually based off of the control's events.
Best of luck.

Design problems with .Net UserControl

I have created a UserControl that has a ListView in it. The ListView is publicly accessible though a property. When I put the UserControl in a form and try to design the ListView though the property, the ListView stays that way until I compile again and it reverts back to the default state.
How do I get my design changes to stick for the ListView?
You need to decorate the ListView property with the DesignerSerializationVisibility attribute, like so:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ListView MyListView { get { return this.listView1; } }
This tells the designer's code generator to output code for it.
Fredrik is right, basically, when you need to enable the designer to persist the property to page so it can be instantiated at run time. There is only one way to do this, and that is to write its values to the ASPX page, which is then picked up by the runtime.
Otherwise, the control will simply revert to its default state each and every time.
Always keep in the back of your mind that the Page (and its contents) and the code are completely seperate in ASP.NET, they are hooked up at run time. This means that you dont get the nice code-behind designer support like you do in a WinForms app (where the form is an instance of an object).
Just so I'm clear, you've done something like this, right?
public ListView MyListView { get { return this.listView1; } }
So then you are accessing (at design time) the MyListView property on your UserControl?
I think if you want proper design-time support you're better off changing the "Modifier" property on the ListView itself (back on the original UserControl) to Public - that way you can modify the ListView directly on instances of the UserControl. I've had success doing that anyway.

Categories