Solved

Convert C# to VB.net: yield instruction

Posted on 2009-06-29
8
1,921 Views
Last Modified: 2013-12-17
Hello Experts,

I'm trying to convert some code from C# to VB.Net. I normally use the "DeveloperFusion" tool (http://www.developerfusion.com/tools/convert/csharp-to-vb/), but it fails with the code I have.
Among other things, it says that the "yield" statement is unsupported.
Does anyone knows how can I convert it?
I am attaching the complete source code that I'm trying to convert. (taken from http://blogs.vbcity.com/hotdog/archive/2008/12/19/9225.aspx)

Thanks!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
 
namespace Subro.Controls
{
/// <summary>
/// Add this component in runtime or designtime and assign a datagridview to it to enable grouping on that grid.
/// You can also add an
/// </summary>
[DefaultEvent("DisplayGroup")]
public class DataGridViewGrouper : Component, IGrouper
{
public DataGridViewGrouper()
{
source.DataSourceChanged += new EventHandler(source_DataSourceChanged);
source.GroupingChanged += new EventHandler(source_GroupingChanged);
}
 
public DataGridViewGrouper(DataGridView Grid)
: this()
{
this.DataGridView = Grid;
}
public DataGridViewGrouper(IContainer Container)
: this()
{
Container.Add(this);
}
 
private DataGridView grid;
[DefaultValue(null)]
public DataGridView DataGridView
{
get { return grid; }
set
{
if (grid == value) return;
if (grid != null)
{
grid.Sorted -= new EventHandler(grid_Sorted);
grid.RowPrePaint -= new DataGridViewRowPrePaintEventHandler(grid_RowPrePaint);
grid.RowPostPaint -= new DataGridViewRowPostPaintEventHandler(grid_RowPostPaint);
grid.CellBeginEdit -= new DataGridViewCellCancelEventHandler(grid_CellBeginEdit);
grid.CellDoubleClick -= new DataGridViewCellEventHandler(grid_CellDoubleClick);
grid.CellClick -= new DataGridViewCellEventHandler(grid_CellClick);
grid.MouseMove -= new MouseEventHandler(grid_MouseMove);
grid.SelectionChanged -= new EventHandler(grid_SelectionChanged);
}
RemoveGrouping();
selectedrows.Clear();
grid = value;
if (grid != null)
{
grid.Sorted += new EventHandler(grid_Sorted);
grid.RowPrePaint += new DataGridViewRowPrePaintEventHandler(grid_RowPrePaint);
grid.RowPostPaint += new DataGridViewRowPostPaintEventHandler(grid_RowPostPaint);
grid.CellBeginEdit += new DataGridViewCellCancelEventHandler(grid_CellBeginEdit);
grid.CellDoubleClick += new DataGridViewCellEventHandler(grid_CellDoubleClick);
grid.CellClick += new DataGridViewCellEventHandler(grid_CellClick);
grid.MouseMove += new MouseEventHandler(grid_MouseMove);
grid.SelectionChanged += new EventHandler(grid_SelectionChanged);
}
}
}
 
#region Select /  Collapse/Expand
 
Point capturedcollapsebox = new Point(-1, -1);
void grid_MouseMove(object sender, MouseEventArgs e)
{
if (e.X < HeaderOffset && e.X >= HeaderOffset - collapseboxwidth)
{
DataGridView.HitTestInfo ht = grid.HitTest(e.X, e.Y);
if (IsGroupRow(ht.RowIndex))
{
var y = e.Y - ht.RowY;
if (y >= CollapseBox_Y_Offset && y <= CollapseBox_Y_Offset + collapseboxwidth)
{
checkcollapsedfocused(ht.ColumnIndex, ht.RowIndex);
return;                        
}
}
}
checkcollapsedfocused(-1, -1);
}
void InvalidateCapturedBox()
{
if (capturedcollapsebox.Y == -1) return;
grid.InvalidateCell(capturedcollapsebox.X, capturedcollapsebox.Y);
}
void checkcollapsedfocused(int col, int row)
{
if (capturedcollapsebox.X != col || capturedcollapsebox.Y != row)
{
InvalidateCapturedBox();
capturedcollapsebox = new Point(col, row);
InvalidateCapturedBox();
}
}
 
void grid_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex == -1) return;
if (e.RowIndex == capturedcollapsebox.Y)
{
bool show = IsCollapsed(e.RowIndex);
CollapseExpand(e.RowIndex, show);
if (show)
grid.CurrentCell = grid[1, e.RowIndex + 1];
}
}
/// <summary>
/// selected rows are kept seperately in order to invalidate the entire row
/// and not just one cell when the selection is changed
/// </summary>
List<int> selectedrows = new List<int>();
void grid_SelectionChanged(object sender, EventArgs e)
{
invalidateselected();            
selectedrows.Clear();
foreach (DataGridViewCell c in grid.SelectedCells)
if (IsGroupRow(c.RowIndex))
selectedrows.Add(c.RowIndex);
invalidateselected();
}
void invalidateselected()
{
if (selectedrows.Count == 0 || grid.SelectionMode == DataGridViewSelectionMode.FullRowSelect) return;
foreach (int i in selectedrows)
grid.InvalidateRow(i);
}
 
 
bool IsCollapsed(int index)
{
if (++index >= grid.Rows.Count) return false;
return !grid.Rows[index].Visible;
}
void CollapseExpand(int index, bool show)
{
grid.SuspendLayout();
foreach (DataGridViewRow row in GetRows(index))
row.Visible = show;
grid.ResumeLayout();
}
public void ExpandAll()
{
CollapseExpandAll(true);
}
public void CollapseAll()
{
CollapseExpandAll(false);
}
void CollapseExpandAll(bool show)
{
if (grid == null || !GridUsesGroupSource) return;
grid.SuspendLayout();
source.SuspendBinding();
int cnt = source.Count;
for (int i = 0; i < cnt; i++)
{
if (!IsGroupRow(i))
grid.Rows[i].Visible = show;
}
grid.ResumeLayout();
source.ResumeBinding();
}
void grid_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
if (IsGroupRow(e.RowIndex))
{
CollapseExpand(e.RowIndex, true);
grid.SuspendLayout();
grid.CurrentCell = grid[1, e.RowIndex + 1];
grid.Rows[e.RowIndex].Selected = false;
SelectGroup(e.RowIndex);
grid.ResumeLayout();
}
}
IEnumerable<DataGridViewRow> GetRows(int index)
{
while (!IsGroupRow(++index) && index < source.Count)
yield return grid.Rows[index];
}
void SelectGroup(int offset)
{
foreach (DataGridViewRow row in GetRows(offset))
row.Selected = true;
}
#endregion
 
public IEnumerable<IGroupRow> GetGroups()
{
return source.GetGroups();
}
public bool IsGroupRow(int Index)
{
return source.IsGroupRow(Index);
}
void source_DataSourceChanged(object sender, EventArgs e)
{
if (PropertiesChanged != null)
PropertiesChanged(this, e);
}
public event EventHandler PropertiesChanged;
public IEnumerable<PropertyDescriptor> GetProperties()
{
foreach (PropertyDescriptor pd in source.GetItemProperties(null))
yield return pd;
}
void grid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
if (IsGroupRow(e.RowIndex))
e.Cancel = true;
}
protected override void Dispose(bool disposing)
{
DataGridView = null;
source.Dispose();
base.Dispose(disposing);
}
void grid_Sorted(object sender, EventArgs e)
{
this.capturedcollapsebox = new Point(-1, -1);
ResetGrouping();
}
GroupingSource source = new GroupingSource();
public GroupingSource GroupingSource
{
get
{
return source;
}
}
public void RemoveGrouping()
{
if (GridUsesGroupSource)
try
{
grid.DataSource = source.DataSource;
grid.DataMember = source.DataMember;
source.RemoveGrouping();
}
catch { }
}
void source_GroupingChanged(object sender, EventArgs e)
{
OnGroupOnChanged();
}
public event EventHandler GroupingChanged;
void OnGroupOnChanged()
{
if (GroupingChanged != null)
GroupingChanged(this, EventArgs.Empty);
}
bool GridUsesGroupSource
{
get
{
return grid != null && grid.DataSource == source;
}
}
public void ResetGrouping()
{
if (!GridUsesGroupSource) return;
source.ResetGroup();
}
 
[DefaultValue(null)]
public GroupingInfo GroupOn
{
get
{
return source.GroupOn;
}
set
{
if (GroupOn == value) return;
if (value == null)
RemoveGrouping();
else
CheckSource().GroupOn = value;
}
}
public bool IsGrouped
{
get
{
return source.GroupOn != null;
}
}
[DefaultValue(SortOrder.Ascending)]
public SortOrder SortOrder
{
get
{
return source.GroupSortDirection;
}
set
{
source.GroupSortDirection = value;
}
}
 
 
public void SetGroupOn(DataGridViewColumn col)
{
SetGroupOn(col == null ? null : col.DataPropertyName);
}
 
public void SetGroupOn(PropertyDescriptor Property)
{
CheckSource().SetGroupOn(Property);
}
public void SetGroupOn(GroupingDelegate gd)
{
CheckSource().SetGroupOn(gd);
}
public void SetGroupOnStartLetters(GroupingInfo g, int Letters)
{
CheckSource().SetGroupOnStartLetters(g, Letters);
}
public void SetGroupOnStartLetters(string Property, int Letters)
{
CheckSource().SetGroupOnStartLetters(Property, Letters);
}
public void SetGroupOn(string Name)
{
if (string.IsNullOrEmpty(Name))
RemoveGrouping();
else
CheckSource().SetGroupOn(Name);
}
GroupingSource CheckSource()
{
if (grid == null)
throw new Exception("No target datagridview set");
if (!GridUsesGroupSource)
{
source.DataSource = grid.DataSource;
source.DataMember = grid.DataMember;
grid.DataSource = source;
}
return source;
}
#region Painting
void grid_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
if (IsGroupRow(e.RowIndex))
{
e.Handled = true;
PaintGroupRow(e);
}
}
const int collapseboxwidth = 10;
const int lineoffset = collapseboxwidth / 2;
int HeaderOffset
{
get
{
if (grid.RowHeadersVisible) return grid.RowHeadersWidth - lineoffset;
return lineoffset * 4;
}
}
Pen linepen = Pens.SteelBlue;
bool DrawExpandCollapseLines
{
get
{
return grid.RowHeadersVisible;
}
}
void grid_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
if (!DrawExpandCollapseLines || e.RowIndex >= source.Count) return;
int next = e.RowIndex + 1;
int r = grid.RowHeadersWidth;
int x = HeaderOffset - lineoffset;
int y = e.RowBounds.Top + e.RowBounds.Height / 2;
e.Graphics.DrawLine(linepen, x, y, r, y);
if (next < source.Count && !IsGroupRow(next))
y = e.RowBounds.Bottom;
e.Graphics.DrawLine(linepen, x, e.RowBounds.Top, x, y);
 
}
 
private bool showheader = true;
[DefaultValue(true)]
public bool ShowGroupName
{
get { return showheader; }
set
{
if (showheader == value) return;
showheader = value;
if (grid != null) grid.Invalidate();
}
}
private bool showcount = true;
[DefaultValue(true)]
public bool ShowCount
{
get { return showcount; }
set
{
if (showcount == value) return;
showcount = value;
if (grid != null) grid.Invalidate();
}
}
 
/// <summary>
/// This event is fired when the group row has to be painted and the display values are requested
/// </summary>
public event EventHandler<GroupDisplayEventArgs> DisplayGroup;
GroupDisplayEventArgs GetDisplayValues(DataGridViewRowPrePaintEventArgs pe)
{            
IGroupRow row = source[pe.RowIndex] as IGroupRow;
GroupDisplayEventArgs e = new GroupDisplayEventArgs(row, source.GroupOn);
bool selected = selectedrows.Contains(pe.RowIndex);
e.Selected = selected;
e.BackColor = selected ? grid.DefaultCellStyle.SelectionBackColor : grid.DefaultCellStyle.BackColor;
e.ForeColor = selected ? grid.DefaultCellStyle.SelectionForeColor : grid.DefaultCellStyle.ForeColor;
e.Font = pe.InheritedRowStyle.Font;
if (showcount)
e.Summary = "(" + row.Count + ")";
if (showheader)
e.Header = source.GroupOn.ToString();
e.GroupingInfo.SetDisplayValues(e);
if (e.Cancel) return null;
if (DisplayGroup != null)
{
DisplayGroup(this, e);
if (e.Cancel) return null;
}
return e;
}
void PaintGroupRow(DataGridViewRowPrePaintEventArgs e)
{
var info = GetDisplayValues(e);
if (info == null) return; //cancelled
var r = e.RowBounds;
r.X = 1;
r.Height--;
using (var b = new SolidBrush(info.BackColor))
e.Graphics.FillRectangle(b, r);
//line under the group row
e.Graphics.DrawLine(Pens.SteelBlue, r.Left, r.Bottom, r.Right, r.Bottom);
//collapse/expand symbol               
{
var cer = GetCollapseBoxBounds(e.RowBounds.Y);
if (capturedcollapsebox.Y == e.RowIndex)
e.Graphics.FillEllipse(Brushes.Yellow, cer);
e.Graphics.DrawEllipse(linepen, cer);
bool collapsed = IsCollapsed(e.RowIndex);
int cx;
if (DrawExpandCollapseLines && !collapsed)
{
cx = HeaderOffset - lineoffset;
e.Graphics.DrawLine(linepen, cx, cer.Bottom, cx, r.Bottom);
}
cer.Inflate(-2, -2);
var cy = cer.Y + cer.Height / 2;
e.Graphics.DrawLine(linepen, cer.X, cy, cer.Right, cy);
if (collapsed)
{
cx = cer.X + cer.Width / 2;
e.Graphics.DrawLine(linepen, cx, cer.Top, cx, cer.Bottom);
}
}
//group value
{
r.X = HeaderOffset + 1;
 
   
using (var fb = new SolidBrush(info.ForeColor))
{
var sf = new StringFormat { LineAlignment = StringAlignment.Center };
if (info.Header != null)
{
var size = e.Graphics.MeasureString(info.Header, info.Font);
e.Graphics.DrawString(info.Header, info.Font, fb, r, sf);
r.Offset((int)size.Width + 5, 0);
}
if (info.DisplayValue != null)
{
using (var f = new Font(info.Font.FontFamily, info.Font.Size + 2, FontStyle.Bold))
{
var size = e.Graphics.MeasureString(info.DisplayValue, f);
e.Graphics.DrawString(info.DisplayValue, f, fb, r, sf);
r.Offset((int)size.Width + 10, 0);
}
}
if (info.Summary != null)
{
e.Graphics.DrawString(info.Summary, info.Font, fb, r, sf);
}
}
}
}
const int CollapseBox_Y_Offset = 5;
private Rectangle GetCollapseBoxBounds(int Y_Offset)
{
return new Rectangle(HeaderOffset - collapseboxwidth, Y_Offset + CollapseBox_Y_Offset, collapseboxwidth, collapseboxwidth);
}
#endregion
public bool CurrentRowIsGroupRow
{
get
{
if (grid == null || grid.CurrentCell==null) return false;
return IsGroupRow(grid.CurrentCell.RowIndex);
}
}
}
public class GroupDisplayEventArgs : CancelEventArgs
{
public readonly IGroupRow Row;
public readonly GroupingInfo GroupingInfo;
public GroupDisplayEventArgs(IGroupRow Row, GroupingInfo Info)
{
this.Row = Row;
this.GroupingInfo = Info;
}
public object Value { get { return Row.Value; } }
public string DisplayValue { get; set; }
public string Header { get; set; }
public string Summary { get; set; }
public Color BackColor { get; set; }
public Color ForeColor { get; set; }
public Font Font { get; set; }
public bool Selected { get; internal set; }
}
public interface IGroupRow
{
int Index { get; }
object Value { get; }
int Count { get; }
object[] Rows { get; }
}
 
[DefaultEvent("GroupingChanged")]
public class GroupingSource : BindingSource
{
public GroupingSource()
{
}
public GroupingSource(object DataSource)
: this()
{
this.DataSource = DataSource;
}
public GroupingSource(object DataSource, string GroupOn)
: this(DataSource)
{
}
GroupingInfo groupon;
[DefaultValue(null)]
public GroupingInfo GroupOn
{
get
{
return groupon;
}
set
{
if (groupon == value) return;
RemoveGrouping(value == null);
if (value != null)
{
if (value.Equals(groupon)) return;
groupon = value;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
OnGroupingChanged();
}
}
}
 
public void RemoveGrouping()
{
RemoveGrouping(true);
}
void RemoveGrouping(bool callListChanged)
{
if (groupon == null) return;
groupon = null;
ResetGroup();
if (callListChanged)
{
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
OnGroupingChanged();
}
}
public void SetGroupOn(string Property)
{
SetGroupOn(GetProperty(Property));
}
PropertyDescriptor GetProperty(string Name)
{
var pd = this.GetItemProperties(null)[Name];
if (pd == null)
throw new Exception(Name + " is not a valid property");
return pd;
}
public void SetGroupOn(PropertyDescriptor p)
{
if (p == null) throw new ArgumentNullException();
if (groupon == null || !groupon.IsProperty(p))
GroupOn = new PropertyGrouper(p);
}
public void SetGroupOn(GroupingDelegate gd)
{
SetGroupOn(gd, null);
}
public void SetGroupOn(GroupingDelegate gd, string descr)
{
if (gd == null) throw new ArgumentNullException();
GroupOn = new DelegateGrouper(gd, descr);
}
public void SetGroupOnStartLetters(GroupingInfo g, int Letters)
{
GroupOn = new StartLetterGrouper(g, Letters);
}
public void SetGroupOnStartLetters(string Property, int Letters)
{
SetGroupOnStartLetters(GetProperty(Property), Letters);
}
public bool IsGroupRow(int Index)
{
if (info == null) return false;
if (Index < 0 || Index >= Count) return false;
return info.Rows[Index] is GroupRow;
}
 
 
SortOrder order = SortOrder.Ascending;
[DefaultValue(SortOrder.Ascending)]
public SortOrder GroupSortDirection
{
get { return order; }
set
{
if (order == value) return;
order = value;
ResetGroup();
}
}
class GroupRow : IGroupRow
{
public int Index { get; set; }
public object Value { get; set; }
public object[] Rows { get; set; }
public int Count
{
get { return Rows.Length; }
}
internal List<object> List = new List<object>();
public void Finalize(int Index)
{
this.Index = Index;
Rows = List.ToArray();
List = null;
}
}
public IEnumerable<IGroupRow> GetGroups()
{            
foreach (IGroupRow g in Info.Groups.Values)
yield return g;            
}
class GroupInfo
{
public readonly GroupingSource Owner;
 
public GroupInfo(GroupingSource Owner)
{
this.Owner = Owner;
set();
}
public int TotalCount
{
get
{
return Rows.Count;
}
}
public IList Rows;
//public List<GroupRow> Groups = new List<GroupRow>();
public IDictionary<object,GroupRow> Groups ;
void set()
{
Groups = null;
GroupingInfo gi = Owner.groupon;
if (gi == null)
{
Rows = Owner.List;
return;
}
if (Owner.GroupSortDirection == SortOrder.None)
Groups = new Dictionary<object, GroupRow>();
else
{
GenericComparer<object> comparer = new GenericComparer<object>();
comparer.Descending = Owner.GroupSortDirection == SortOrder.Descending;
Groups = new SortedDictionary<object, GroupRow>(comparer);
}
foreach (object row in Owner)
{
object key = gi.GetGroupValue(row);
GroupRow gr;
if(!Groups.TryGetValue(key,out gr))
{
gr=new GroupRow();
gr.Value = key;
Groups.Add(key, gr);
}
gr.List.Add(row);
}
 
//var groups = Owner.Cast<object>().GroupBy<object, object>(o => gr.GetGroupValue(o));
int i = 0;                
Rows = new List<object>(Groups.Count + Owner.BaseCount);
foreach (GroupRow g in Groups.Values)
{
g.Finalize(i++);
Rows.Add(g);
foreach (object row in g.Rows)
Rows.Add(row);
}
}
 
 
 
}
 
 
GroupInfo info;
GroupInfo Info
{
get
{
if (info == null)
{
info = new GroupInfo(this);
if (bsource != null)
SyncWithBSource();
}
return info;
}
}
void OnGroupingChanged()
{
if (GroupingChanged != null)
GroupingChanged(this, EventArgs.Empty);
}
public event EventHandler GroupingChanged;
 
public void ResetGroup()
{
info = null;
}
BindingSource bsource;
void DisposeBindingSourceEvents()
{
if (bsource == null) return;
bsource.CurrentChanged -= new EventHandler(bsource_CurrentChanged);
}
protected override void Dispose(bool disposing)
{
DisposeBindingSourceEvents();
base.Dispose(disposing);
}
protected override void OnDataSourceChanged(EventArgs e)
{
ResetGroup();
DisposeBindingSourceEvents();
bsource = DataSource as BindingSource;
if (bsource != null)
{
bsource.CurrentChanged += new EventHandler(bsource_CurrentChanged);
if (NeedSync) SyncWithBSource();
}
base.OnDataSourceChanged(e);
}
bool suspendlistchange;
protected override void OnListChanged(ListChangedEventArgs e)
{
if (suspendlistchange) return;
 
switch (e.ListChangedType)
{
case ListChangedType.ItemChanged:
if (groupon != null && groupon.IsProperty(e.PropertyDescriptor))
ResetGroup();
break;
case ListChangedType.ItemAdded:
if (info != null)
info.Rows.Add(List[e.NewIndex]);
break;
case ListChangedType.ItemDeleted:
ResetGroup();
break;
case ListChangedType.Reset:
ResetGroup();
break;
case ListChangedType.PropertyDescriptorAdded:
case ListChangedType.PropertyDescriptorChanged:
case ListChangedType.PropertyDescriptorDeleted:
props = null;
break;
}
base.OnListChanged(e);
}
public override object AddNew()
{
suspendlistchange = true;
try
{
var res = base.AddNew();
if (info != null)
info.Rows.Add(res);
return res;
}
finally
{
suspendlistchange = false;
}
}
public override void ApplySort(PropertyDescriptor property, ListSortDirection sort)
{
if (property is PropertyWrapper)
property = (property as PropertyWrapper).Property;
base.ApplySort(property, sort);
}
public override void ApplySort(ListSortDescriptionCollection sorts)
{
base.ApplySort(sorts);
}
 
 
 
 
public override void RemoveAt(int index)
{
if (info == null || groupon == null)
base.RemoveAt(index);
else if (!IsGroupRow(index))
{
var i = List.IndexOf(this[index]);
suspendlistchange = true;
try
{
info.Rows.RemoveAt(index);
List.RemoveAt(i);
base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
}
finally
{
suspendlistchange = false;
}
}
}
 
 
public override void Remove(object value)
{
if (value is GroupRow) return;
int index = this.IndexOf(value);
if (index != -1)
{
RemoveAt(index);
}
}
 
protected override void OnCurrentChanged(EventArgs e)
{
base.OnCurrentChanged(e);
if (NeedSync && !(Current is GroupRow))
{
bsource.Position = bsource.IndexOf(Current);
}
}
void bsource_CurrentChanged(object sender, EventArgs e)
{
if (NeedSync)
SyncWithBSource();
}
 
bool NeedSync
{
get
{
if (bsource == null || suspendlistchange) return false;
if (bsource.IsBindingSuspended) return false;
return Current != bsource.Current;
}
}
 
private void SyncWithBSource()
{
Position = IndexOf(bsource.Current);
}
public override int IndexOf(object value)
{
return Info.Rows.IndexOf(value);
}
 
public class PropertyWrapper : PropertyDescriptor
{
public readonly PropertyDescriptor Property;
public readonly GroupingSource Owner;
public PropertyWrapper(PropertyDescriptor Property, GroupingSource Owner)
: base(Property)
{
this.Property = Property;
this.Owner = Owner;
}
public override bool CanResetValue(object component)
{
return Property.CanResetValue(component);
}
public override Type ComponentType
{
get { return Property.ComponentType; }
}
public override object GetValue(object component)
{
if (component is GroupRow)
{
if (Owner.groupon.IsProperty(Property))
return (component as GroupRow).Value;
return null;
}
return Property.GetValue(component);
}
public override bool IsReadOnly
{
get { return Property.IsReadOnly; }
}
public override Type PropertyType
{
get { return Property.PropertyType; }
}
public override void ResetValue(object component)
{
Property.ResetValue(component);
}
public override void SetValue(object component, object value)
{
Property.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return Property.ShouldSerializeValue(component);
}
}
 
PropertyDescriptorCollection props;
public override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
if (listAccessors == null)
{
if (props == null)
{
/*
props = new PropertyDescriptorCollection(
base.GetItemProperties(null).Cast<PropertyDescriptor>()
.Select(pd => new PropertyWrapper(pd, this)).ToArray());*/
props = base.GetItemProperties(null);
PropertyDescriptor[] arr = new PropertyDescriptor[props.Count];
for (int i = 0; i < props.Count; i++)
{
arr[i] = new PropertyWrapper(props[i],this);
}
props = new PropertyDescriptorCollection(arr);
}
return props;
}
return base.GetItemProperties(listAccessors);
}
public int BaseCount
{
get
{
return base.Count;
}
}
public object GetBaseRow(int Index)
{
return List[Index];
}
public override int Count
{
get
{
return Info.TotalCount;
}
}
public override object this[int index]
{
get
{
return Info.Rows[index];
}
set
{
Info.Rows[index] = value;
}
}
}
#region Grouping Info objects
public abstract class GroupingInfo
{
public abstract object GetGroupValue(object Row);
public virtual bool IsProperty(PropertyDescriptor p)
{
return false;
}
public virtual bool IsProperty(string Name)
{
return Name == ToString();
}
public static implicit operator GroupingInfo(PropertyDescriptor p)
{
return new PropertyGrouper(p);
}
public virtual void SetDisplayValues(GroupDisplayEventArgs e)
{
var o = e.Value;
e.DisplayValue = o == null ? "<Null>" : o.ToString();
}
}
public class PropertyGrouper : GroupingInfo
{
public readonly PropertyDescriptor Property;
public PropertyGrouper(PropertyDescriptor Property)
{
if (Property == null) throw new ArgumentNullException();
this.Property = Property;
}
public override object GetGroupValue(object Row)
{
return Property.GetValue(Row);
}
public override string ToString()
{
return Property.Name;
}
}
public delegate object GroupingDelegate(object Row);
public class DelegateGrouper : GroupingInfo
{
public readonly string Name;
public readonly GroupingDelegate GroupingDelegate;
public DelegateGrouper(GroupingDelegate Delegate, string Name)
{
if (Delegate == null) throw new ArgumentNullException();
this.Name = Name;
if (Name == null) this.Name = Delegate.ToString();
this.GroupingDelegate = Delegate;
}
public override string ToString()
{
return Name;
}
public override object GetGroupValue(object Row)
{
return GroupingDelegate(Row);
}
}
 
 
public class StartLetterGrouper : GroupingInfo
{
public readonly GroupingInfo Grouper;
public readonly int Letters;
public StartLetterGrouper(GroupingInfo Grouper)
: this(Grouper, 1)
{
}
public StartLetterGrouper(GroupingInfo Grouper, int Letters)
{
if (Grouper == null) throw new ArgumentNullException();
this.Grouper = Grouper;
this.Letters = Letters;
}
public override string ToString()
{
return Grouper.ToString();
}
public override bool IsProperty(PropertyDescriptor p)
{
return Grouper.IsProperty(p);
}
public override object GetGroupValue(object Row)
{
var val = Grouper.GetGroupValue(Row);
if (val == null) return null;
var s = val.ToString();
if (s.Length < Letters) return s;
return s.Substring(0, Letters);
}
}
#endregion
#region Interfaces
public interface IDataGridViewGrouperOwner
{
DataGridViewGrouper Grouper { get; }
}
public interface IGrouper
{
void SetGroupOn(string col);
void SetGroupOn(PropertyDescriptor col);
void RemoveGrouping();
GroupingInfo GroupOn { get; set; }
event EventHandler PropertiesChanged;
event EventHandler GroupingChanged;
IEnumerable<PropertyDescriptor> GetProperties();
}
#endregion
}
 
 
/*
* Added the Generic comparer here, for ease of use on blog posting ;)
*/
 
namespace Subro
{
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.ComponentModel;
/// <summary>
/// Comparer that tries to find the 'strongest' comparer for a type.
/// if the type implements a generic IComparable, that is used.
/// otherwise if it implements a normal IComparable, that is used.
/// If neither are implemented, the ToString versions are compared.
/// INullable structures are also supported.
/// This way, the DefaultComparer can compare any object types and can be used for sorting any source.
/// </summary>
/// <example>Array.Sort(YourArray,new GenericComparer());</example>
public class GenericComparer : IComparer
{
public GenericComparer()
{
}
public GenericComparer(Type Type)
{
this.Type = Type;
}
Type type;
public Type Type
{
get
{
return type;
}
set
{
if (value == null) throw new ArgumentNullException();
type = value;
comp = null;
}
}
Type targettype;
/// <summary>
/// normally the same as the type, but can be set to a different type
/// </summary>
public Type TargetType
{
get
{
if (targettype == null) return type;
return targettype;
}
set
{
if (TargetType == value) return;
targettype = value;
comp = null;
}
}
 
 
IComparer comp;
IComparer GetGenericComparer(Type From, Type To)
{
while (To != typeof(object))
{
if (typeof(IComparable<>).MakeGenericType(To).IsAssignableFrom(From))
return (IComparer)Activator.CreateInstance(typeof(StrongCompare<,>).MakeGenericType(From, To));
To = To.BaseType;
}
return null;
}
public IComparer GetComparer(Type From, Type To)
{
var gen = GetGenericComparer(From, To);
if (gen != null)
return gen;
else if (typeof(IComparable).IsAssignableFrom(type))
{
return (IComparer)Activator.CreateInstance(typeof(NonGenericCompare<>).MakeGenericType(type));
}
else if (type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition())
{
var basetype = type.GetGenericArguments()[0];
return (IComparer)Activator.CreateInstance(typeof(NullableComparer<>).MakeGenericType(basetype),
GetComparer(basetype, To == From ? basetype : To));
}
return new StringComparer();
}
class NullableComparer<T> : IComparer
where T : struct
{
public readonly IComparer BaseComparer;
public NullableComparer(IComparer BaseComparer)
{
this.BaseComparer = BaseComparer;
}
object getval(object o)
{
return ((Nullable<T>)o).Value;
}
public int Compare(object x, object y)
{
return BaseComparer.Compare(getval(x), getval(y));
}
}
class StrongCompare<F, T> : IComparer
where F : IComparable<T>
{
public int Compare(object x, object y)
{
return ((F)x).CompareTo((T)y);
}
}
class NonGenericCompare<T> : IComparer
where T : IComparable
{
public int Compare(object x, object y)
{
return ((T)x).CompareTo(y);
}
}
class StringComparer : IComparer
{
public int Compare(object x, object y)
{
return string.Compare(x.ToString(), y.ToString());
}
}
public bool Descending
{
get
{
return factor < 0;
}
set
{
factor = value ? -1 : 1;
}
}
int factor = 1;
int compare(object x, object y)
{
if (x == y) return 0;
if (x == null) return -1;
if (y == null) return 1;
if (type == null)
Type = x.GetType();
if (comp == null)
comp = GetComparer(type, TargetType);
return comp.Compare(x, y);
}
public int Compare(object x, object y)
{
return factor * compare(x, y);
}
}
 
public class GenericComparer<T> : GenericComparer, IComparer<T>
{
public GenericComparer()
: base(typeof(T))
{ }
public int Compare(T a, T b)
{
return base.Compare(a, b);
}
}
 
 
public class PropertyDescriptorComparer : GenericComparer
{
public readonly PropertyDescriptor Prop;
public PropertyDescriptorComparer(PropertyDescriptor Prop)
: this(Prop, true)
{
}
public PropertyDescriptorComparer(PropertyDescriptor Prop, bool Descending)
: base(Prop.PropertyType)
{
this.Prop = Prop;
this.Descending = Descending;
}
}
}

Open in new window

0
Comment
Question by:jorgesv13
8 Comments
 
LVL 59

Accepted Solution

by:
Kevin Cross earned 200 total points
ID: 24736766
I got the same error, but it did convert most of code...will look at the yield portion.  It looks like the comments and regions are all at the top, but can move around as needed.

''' <summary>
''' Add this component in runtime or designtime and assign a datagridview to it to enable grouping on that grid.
''' You can also add an
''' </summary>
 
 
 
#Region "Select /  Collapse/Expand"
 
 
''' <summary>
''' selected rows are kept seperately in order to invalidate the entire row
''' and not just one cell when the selection is changed
''' </summary>
 
 
#End Region
 
 
 
 
 
#Region "Painting"
 
 
 
''' <summary>
''' This event is fired when the group row has to be painted and the display values are requested
''' </summary>
'cancelled
'line under the group row
'collapse/expand symbol               
'group value
 
 
#End Region
 
 
 
 
 
'public List<GroupRow> Groups = new List<GroupRow>();
 
'var groups = Owner.Cast<object>().GroupBy<object, object>(o => gr.GetGroupValue(o));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
'
'props = new PropertyDescriptorCollection(
'base.GetItemProperties(null).Cast<PropertyDescriptor>()
'.Select(pd => new PropertyWrapper(pd, this)).ToArray());
 
#Region "Grouping Info objects"
 
 
#End Region
#Region "Interfaces"
#End Region
 
 
'
'* Added the Generic comparer here, for ease of use on blog posting ;)
'
 
 
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Collections
Imports System.ComponentModel
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Namespace Subro.Controls
    <DefaultEvent("DisplayGroup")> _
    Public Class DataGridViewGrouper
        Inherits Component
        Implements IGrouper
        Public Sub New()
            AddHandler source.DataSourceChanged, AddressOf source_DataSourceChanged
            AddHandler source.GroupingChanged, AddressOf source_GroupingChanged
        End Sub
        Public Sub New(ByVal Grid As DataGridView)
            Me.New()
            Me.DataGridView = Grid
        End Sub
        Public Sub New(ByVal Container As IContainer)
            Me.New()
            Container.Add(Me)
        End Sub
        Private grid As DataGridView
        <DefaultValue(Nothing)> _
        Public Property DataGridView() As DataGridView
            Get
                Return grid
            End Get
            Set(ByVal value As DataGridView)
                If grid = value Then
                    Return
                End If
                If grid IsNot Nothing Then
                    RemoveHandler grid.Sorted, AddressOf grid_Sorted
                    RemoveHandler grid.RowPrePaint, AddressOf grid_RowPrePaint
                    RemoveHandler grid.RowPostPaint, AddressOf grid_RowPostPaint
                    RemoveHandler grid.CellBeginEdit, AddressOf grid_CellBeginEdit
                    RemoveHandler grid.CellDoubleClick, AddressOf grid_CellDoubleClick
                    RemoveHandler grid.CellClick, AddressOf grid_CellClick
                    RemoveHandler grid.MouseMove, AddressOf grid_MouseMove
                    RemoveHandler grid.SelectionChanged, AddressOf grid_SelectionChanged
                End If
                RemoveGrouping()
                selectedrows.Clear()
                grid = value
                If grid IsNot Nothing Then
                    AddHandler grid.Sorted, AddressOf grid_Sorted
                    AddHandler grid.RowPrePaint, AddressOf grid_RowPrePaint
                    AddHandler grid.RowPostPaint, AddressOf grid_RowPostPaint
                    AddHandler grid.CellBeginEdit, AddressOf grid_CellBeginEdit
                    AddHandler grid.CellDoubleClick, AddressOf grid_CellDoubleClick
                    AddHandler grid.CellClick, AddressOf grid_CellClick
                    AddHandler grid.MouseMove, AddressOf grid_MouseMove
                    AddHandler grid.SelectionChanged, AddressOf grid_SelectionChanged
                End If
            End Set
        End Property
        Private capturedcollapsebox As New Point(-1, -1)
        Private Sub grid_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
            If e.X < HeaderOffset AndAlso e.X >= HeaderOffset - collapseboxwidth Then
                Dim ht As DataGridView.HitTestInfo = grid.HitTest(e.X, e.Y)
                If IsGroupRow(ht.RowIndex) Then
                    Dim y = e.Y - ht.RowY
                    If y >= CollapseBox_Y_Offset AndAlso y <= CollapseBox_Y_Offset + collapseboxwidth Then
                        checkcollapsedfocused(ht.ColumnIndex, ht.RowIndex)
                        Exit Sub
                    End If
                End If
            End If
            checkcollapsedfocused(-1, -1)
        End Sub
        Private Sub InvalidateCapturedBox()
            If capturedcollapsebox.Y = -1 Then
                Exit Sub
            End If
            grid.InvalidateCell(capturedcollapsebox.X, capturedcollapsebox.Y)
        End Sub
        Private Sub checkcollapsedfocused(ByVal col As Integer, ByVal row As Integer)
            If capturedcollapsebox.X <> col OrElse capturedcollapsebox.Y <> row Then
                InvalidateCapturedBox()
                capturedcollapsebox = New Point(col, row)
                InvalidateCapturedBox()
            End If
        End Sub
        Private Sub grid_CellClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs)
            If e.RowIndex = -1 Then
                Exit Sub
            End If
            If e.RowIndex = capturedcollapsebox.Y Then
                Dim show As Boolean = IsCollapsed(e.RowIndex)
                CollapseExpand(e.RowIndex, show)
                If show Then
                    grid.CurrentCell = grid(1, e.RowIndex + 1)
                End If
            End If
        End Sub
        Private selectedrows As New List(Of Integer)()
        Private Sub grid_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs)
            invalidateselected()
            selectedrows.Clear()
            For Each c As DataGridViewCell In grid.SelectedCells
                If IsGroupRow(c.RowIndex) Then
                    selectedrows.Add(c.RowIndex)
                End If
            Next
            invalidateselected()
        End Sub
        Private Sub invalidateselected()
            If selectedrows.Count = 0 OrElse grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect Then
                Exit Sub
            End If
            For Each i As Integer In selectedrows
                grid.InvalidateRow(i)
            Next
        End Sub
        Private Function IsCollapsed(ByVal index As Integer) As Boolean
            If System.Threading.Interlocked.Increment(index) >= grid.Rows.Count Then
                Return False
            End If
            Return Not grid.Rows(index).Visible
        End Function
        Private Sub CollapseExpand(ByVal index As Integer, ByVal show As Boolean)
            grid.SuspendLayout()
            For Each row As DataGridViewRow In GetRows(index)
                row.Visible = show
            Next
            grid.ResumeLayout()
        End Sub
        Public Sub ExpandAll()
            CollapseExpandAll(True)
        End Sub
        Public Sub CollapseAll()
            CollapseExpandAll(False)
        End Sub
        Private Sub CollapseExpandAll(ByVal show As Boolean)
            If grid Is Nothing OrElse Not GridUsesGroupSource Then
                Exit Sub
            End If
            grid.SuspendLayout()
            source.SuspendBinding()
            Dim cnt As Integer = source.Count
            For i As Integer = 0 To cnt - 1
                If Not IsGroupRow(i) Then
                    grid.Rows(i).Visible = show
                End If
            Next
            grid.ResumeLayout()
            source.ResumeBinding()
        End Sub
        Private Sub grid_CellDoubleClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs)
            If IsGroupRow(e.RowIndex) Then
                CollapseExpand(e.RowIndex, True)
                grid.SuspendLayout()
                grid.CurrentCell = grid(1, e.RowIndex + 1)
                grid.Rows(e.RowIndex).Selected = False
                SelectGroup(e.RowIndex)
                grid.ResumeLayout()
            End If
        End Sub
        Private Function GetRows(ByVal index As Integer) As IEnumerable(Of DataGridViewRow)
            While Not IsGroupRow(System.Threading.Interlocked.Increment(index)) AndAlso index < source.Count
                
            End While
        End Function
        Private Sub SelectGroup(ByVal offset As Integer)
            For Each row As DataGridViewRow In GetRows(offset)
                row.Selected = True
            Next
        End Sub
        Public Function GetGroups() As IEnumerable(Of IGroupRow)
            Return source.GetGroups()
        End Function
        Public Function IsGroupRow(ByVal Index As Integer) As Boolean
            Return source.IsGroupRow(Index)
        End Function
        Private Sub source_DataSourceChanged(ByVal sender As Object, ByVal e As EventArgs)
            RaiseEvent PropertiesChanged(Me, e)
        End Sub
        Public Event PropertiesChanged As EventHandler
        Public Function GetProperties() As IEnumerable(Of PropertyDescriptor)
            For Each pd As PropertyDescriptor In source.GetItemProperties(Nothing)
                
            Next
        End Function
        Private Sub grid_CellBeginEdit(ByVal sender As Object, ByVal e As DataGridViewCellCancelEventArgs)
            If IsGroupRow(e.RowIndex) Then
                e.Cancel = True
            End If
        End Sub
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            DataGridView = Nothing
            source.Dispose()
            MyBase.Dispose(disposing)
        End Sub
        Private Sub grid_Sorted(ByVal sender As Object, ByVal e As EventArgs)
            Me.capturedcollapsebox = New Point(-1, -1)
            ResetGrouping()
        End Sub
        Private source As New GroupingSource()
        Public ReadOnly Property GroupingSource() As GroupingSource
            Get
                Return source
            End Get
        End Property
        Public Sub RemoveGrouping()
            If GridUsesGroupSource Then
                Try
                    grid.DataSource = source.DataSource
                    grid.DataMember = source.DataMember
                    source.RemoveGrouping()
                Catch
                End Try
            End If
        End Sub
        Private Sub source_GroupingChanged(ByVal sender As Object, ByVal e As EventArgs)
            OnGroupOnChanged()
        End Sub
        Public Event GroupingChanged As EventHandler
        Private Sub OnGroupOnChanged()
            RaiseEvent GroupingChanged(Me, EventArgs.Empty)
        End Sub
        Private ReadOnly Property GridUsesGroupSource() As Boolean
            Get
                Return grid IsNot Nothing AndAlso grid.DataSource = source
            End Get
        End Property
        Public Sub ResetGrouping()
            If Not GridUsesGroupSource Then
                Exit Sub
            End If
            source.ResetGroup()
        End Sub
        <DefaultValue(Nothing)> _
        Public Property GroupOn() As GroupingInfo
            Get
                Return source.GroupOn
            End Get
            Set(ByVal value As GroupingInfo)
                If GroupOn = value Then
                    Exit Sub
                End If
                If value Is Nothing Then
                    RemoveGrouping()
                Else
                    CheckSource().GroupOn = value
                End If
            End Set
        End Property
        Public ReadOnly Property IsGrouped() As Boolean
            Get
                Return source.GroupOn IsNot Nothing
            End Get
        End Property
        <DefaultValue(SortOrder.Ascending)> _
        Public Property SortOrder() As SortOrder
            Get
                Return source.GroupSortDirection
            End Get
            Set(ByVal value As SortOrder)
                source.GroupSortDirection = value
            End Set
        End Property
        Public Sub SetGroupOn(ByVal col As DataGridViewColumn)
            SetGroupOn(If(col Is Nothing, Nothing, col.DataPropertyName))
        End Sub
        Public Sub SetGroupOn(ByVal [Property] As PropertyDescriptor)
            CheckSource().SetGroupOn([Property])
        End Sub
        Public Sub SetGroupOn(ByVal gd As GroupingDelegate)
            CheckSource().SetGroupOn(gd)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal g As GroupingInfo, ByVal Letters As Integer)
            CheckSource().SetGroupOnStartLetters(g, Letters)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal [Property] As String, ByVal Letters As Integer)
            CheckSource().SetGroupOnStartLetters([Property], Letters)
        End Sub
        Public Sub SetGroupOn(ByVal Name As String)
            If String.IsNullOrEmpty(Name) Then
                RemoveGrouping()
            Else
                CheckSource().SetGroupOn(Name)
            End If
        End Sub
        Private Function CheckSource() As GroupingSource
            If grid Is Nothing Then
                Throw New Exception("No target datagridview set")
            End If
            If Not GridUsesGroupSource Then
                source.DataSource = grid.DataSource
                source.DataMember = grid.DataMember
                grid.DataSource = source
            End If
            Return source
        End Function
        Private Sub grid_RowPrePaint(ByVal sender As Object, ByVal e As DataGridViewRowPrePaintEventArgs)
            If IsGroupRow(e.RowIndex) Then
                e.Handled = True
                PaintGroupRow(e)
            End If
        End Sub
        Const collapseboxwidth As Integer = 10
        Const lineoffset As Integer = collapseboxwidth / 2
        Private ReadOnly Property HeaderOffset() As Integer
            Get
                If grid.RowHeadersVisible Then
                    Return grid.RowHeadersWidth - lineoffset
                End If
                Return lineoffset * 4
            End Get
        End Property
        Private linepen As Pen = Pens.SteelBlue
        Private ReadOnly Property DrawExpandCollapseLines() As Boolean
            Get
                Return grid.RowHeadersVisible
            End Get
        End Property
        Private Sub grid_RowPostPaint(ByVal sender As Object, ByVal e As DataGridViewRowPostPaintEventArgs)
            If Not DrawExpandCollapseLines OrElse e.RowIndex >= source.Count Then
                Exit Sub
            End If
            Dim [next] As Integer = e.RowIndex + 1
            Dim r As Integer = grid.RowHeadersWidth
            Dim x As Integer = HeaderOffset - lineoffset
            Dim y As Integer = e.RowBounds.Top + e.RowBounds.Height / 2
            e.Graphics.DrawLine(linepen, x, y, r, y)
            If [next] < source.Count AndAlso Not IsGroupRow([next]) Then
                y = e.RowBounds.Bottom
            End If
            e.Graphics.DrawLine(linepen, x, e.RowBounds.Top, x, y)
        End Sub
        Private showheader As Boolean = True
        <DefaultValue(True)> _
        Public Property ShowGroupName() As Boolean
            Get
                Return showheader
            End Get
            Set(ByVal value As Boolean)
                If showheader = value Then
                    Exit Sub
                End If
                showheader = value
                If grid IsNot Nothing Then
                    grid.Invalidate()
                End If
            End Set
        End Property
        Private m_showcount As Boolean = True
        <DefaultValue(True)> _
        Public Property ShowCount() As Boolean
            Get
                Return m_showcount
            End Get
            Set(ByVal value As Boolean)
                If m_showcount = value Then
                    Exit Sub
                End If
                m_showcount = value
                If grid IsNot Nothing Then
                    grid.Invalidate()
                End If
            End Set
        End Property
        Public Event DisplayGroup As EventHandler(Of GroupDisplayEventArgs)
        Private Function GetDisplayValues(ByVal pe As DataGridViewRowPrePaintEventArgs) As GroupDisplayEventArgs
            Dim row As IGroupRow = TryCast(source(pe.RowIndex), IGroupRow)
            Dim e As New GroupDisplayEventArgs(row, source.GroupOn)
            Dim selected As Boolean = selectedrows.Contains(pe.RowIndex)
            e.Selected = selected
            e.BackColor = If(selected, grid.DefaultCellStyle.SelectionBackColor, grid.DefaultCellStyle.BackColor)
            e.ForeColor = If(selected, grid.DefaultCellStyle.SelectionForeColor, grid.DefaultCellStyle.ForeColor)
            e.Font = pe.InheritedRowStyle.Font
            If m_showcount Then
                e.Summary = "(" & row.Count & ")"
            End If
            If showheader Then
                e.Header = source.GroupOn.ToString()
            End If
            e.GroupingInfo.SetDisplayValues(e)
            If e.Cancel Then
                Return Nothing
            End If
            If DisplayGroup IsNot Nothing Then
                DisplayGroup(Me, e)
                If e.Cancel Then
                    Return Nothing
                End If
            End If
            Return e
        End Function
        Private Sub PaintGroupRow(ByVal e As DataGridViewRowPrePaintEventArgs)
            Dim info = GetDisplayValues(e)
            If info Is Nothing Then
                Exit Sub
            End If
            Dim r = e.RowBounds
            r.X = 1
            r.Height -= 1
            Using b = New SolidBrush(info.BackColor)
                e.Graphics.FillRectangle(b, r)
            End Using
            e.Graphics.DrawLine(Pens.SteelBlue, r.Left, r.Bottom, r.Right, r.Bottom)
            If True Then
                Dim cer = GetCollapseBoxBounds(e.RowBounds.Y)
                If capturedcollapsebox.Y = e.RowIndex Then
                    e.Graphics.FillEllipse(Brushes.Yellow, cer)
                End If
                e.Graphics.DrawEllipse(linepen, cer)
                Dim collapsed As Boolean = IsCollapsed(e.RowIndex)
                Dim cx As Integer
                If DrawExpandCollapseLines AndAlso Not collapsed Then
                    cx = HeaderOffset - lineoffset
                    e.Graphics.DrawLine(linepen, cx, cer.Bottom, cx, r.Bottom)
                End If
                cer.Inflate(-2, -2)
                Dim cy = cer.Y + cer.Height / 2
                e.Graphics.DrawLine(linepen, cer.X, cy, cer.Right, cy)
                If collapsed Then
                    cx = cer.X + cer.Width / 2
                    e.Graphics.DrawLine(linepen, cx, cer.Top, cx, cer.Bottom)
                End If
            End If
            If True Then
                r.X = HeaderOffset + 1
                Using fb = New SolidBrush(info.ForeColor)
                    Dim sf = New StringFormat()
                    If info.Header IsNot Nothing Then
                        Dim size = e.Graphics.MeasureString(info.Header, info.Font)
                        e.Graphics.DrawString(info.Header, info.Font, fb, r, sf)
                        r.Offset(CInt(size.Width) + 5, 0)
                    End If
                    If info.DisplayValue IsNot Nothing Then
                        Using f = New Font(info.Font.FontFamily, info.Font.Size + 2, FontStyle.Bold)
                            Dim size = e.Graphics.MeasureString(info.DisplayValue, f)
                            e.Graphics.DrawString(info.DisplayValue, f, fb, r, sf)
                            r.Offset(CInt(size.Width) + 10, 0)
                        End Using
                    End If
                    If info.Summary IsNot Nothing Then
                        e.Graphics.DrawString(info.Summary, info.Font, fb, r, sf)
                    End If
                End Using
            End If
        End Sub
        Const CollapseBox_Y_Offset As Integer = 5
        Private Function GetCollapseBoxBounds(ByVal Y_Offset As Integer) As Rectangle
            Return New Rectangle(HeaderOffset - collapseboxwidth, Y_Offset + CollapseBox_Y_Offset, collapseboxwidth, collapseboxwidth)
        End Function
        Public ReadOnly Property CurrentRowIsGroupRow() As Boolean
            Get
                If grid Is Nothing OrElse grid.CurrentCell Is Nothing Then
                    Return False
                End If
                Return IsGroupRow(grid.CurrentCell.RowIndex)
            End Get
        End Property
    End Class
    Public Class GroupDisplayEventArgs
        Inherits CancelEventArgs
        Public ReadOnly Row As IGroupRow
        Public ReadOnly GroupingInfo As GroupingInfo
        Public Sub New(ByVal Row As IGroupRow, ByVal Info As GroupingInfo)
            Me.Row = Row
            Me.GroupingInfo = Info
        End Sub
        Public ReadOnly Property Value() As Object
            Get
                Return Row.Value
            End Get
        End Property
Private _DisplayValue As String
        Public Property DisplayValue() As String
            Get
                Return _DisplayValue
            End Get
            Set(ByVal value As String)
                _DisplayValue = value
            End Set
        End Property
Private _Header As String
        Public Property Header() As String
            Get
                Return _Header
            End Get
            Set(ByVal value As String)
                _Header = value
            End Set
        End Property
Private _Summary As String
        Public Property Summary() As String
            Get
                Return _Summary
            End Get
            Set(ByVal value As String)
                _Summary = value
            End Set
        End Property
Private _BackColor As Color
        Public Property BackColor() As Color
            Get
                Return _BackColor
            End Get
            Set(ByVal value As Color)
                _BackColor = value
            End Set
        End Property
Private _ForeColor As Color
        Public Property ForeColor() As Color
            Get
                Return _ForeColor
            End Get
            Set(ByVal value As Color)
                _ForeColor = value
            End Set
        End Property
Private _Font As Font
        Public Property Font() As Font
            Get
                Return _Font
            End Get
            Set(ByVal value As Font)
                _Font = value
            End Set
        End Property
Private _Selected As Boolean
        Public Property Selected() As Boolean
            Get
                Return _Selected
            End Get
            Friend Set(ByVal value As Boolean)
                _Selected = value
            End Set
        End Property
    End Class
    Public Interface IGroupRow
        ReadOnly Property Index() As Integer
        ReadOnly Property Value() As Object
        ReadOnly Property Count() As Integer
        ReadOnly Property Rows() As Object()
    End Interface
    <DefaultEvent("GroupingChanged")> _
    Public Class GroupingSource
        Inherits BindingSource
        Public Sub New()
        End Sub
        Public Sub New(ByVal DataSource As Object)
            Me.New()
            Me.DataSource = DataSource
        End Sub
        Public Sub New(ByVal DataSource As Object, ByVal GroupOn As String)
            Me.New(DataSource)
        End Sub
        Private m_groupon As GroupingInfo
        <DefaultValue(Nothing)> _
        Public Property GroupOn() As GroupingInfo
            Get
                Return m_groupon
            End Get
            Set(ByVal value As GroupingInfo)
                If m_groupon = value Then
                    Return
                End If
                RemoveGrouping(value Is Nothing)
                If value IsNot Nothing Then
                    If value.Equals(m_groupon) Then
                        Return
                    End If
                    m_groupon = value
                    OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
                    OnGroupingChanged()
                End If
            End Set
        End Property
        Public Sub RemoveGrouping()
            RemoveGrouping(True)
        End Sub
        Private Sub RemoveGrouping(ByVal callListChanged As Boolean)
            If m_groupon Is Nothing Then
                Exit Sub
            End If
            m_groupon = Nothing
            ResetGroup()
            If callListChanged Then
                OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
                OnGroupingChanged()
            End If
        End Sub
        Public Sub SetGroupOn(ByVal [Property] As String)
            SetGroupOn(GetProperty([Property]))
        End Sub
        Private Function GetProperty(ByVal Name As String) As PropertyDescriptor
            Dim pd = Me.GetItemProperties(Nothing)(Name)
            If pd Is Nothing Then
                Throw New Exception(Name & " is not a valid property")
            End If
            Return pd
        End Function
        Public Sub SetGroupOn(ByVal p As PropertyDescriptor)
            If p Is Nothing Then
                Throw New ArgumentNullException()
            End If
            If m_groupon Is Nothing OrElse Not m_groupon.IsProperty(p) Then
                GroupOn = New PropertyGrouper(p)
            End If
        End Sub
        Public Sub SetGroupOn(ByVal gd As GroupingDelegate)
            SetGroupOn(gd, Nothing)
        End Sub
        Public Sub SetGroupOn(ByVal gd As GroupingDelegate, ByVal descr As String)
            If gd Is Nothing Then
                Throw New ArgumentNullException()
            End If
            GroupOn = New DelegateGrouper(gd, descr)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal g As GroupingInfo, ByVal Letters As Integer)
            GroupOn = New StartLetterGrouper(g, Letters)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal [Property] As String, ByVal Letters As Integer)
            SetGroupOnStartLetters(GetProperty([Property]), Letters)
        End Sub
        Public Function IsGroupRow(ByVal Index As Integer) As Boolean
            If m_info Is Nothing Then
                Return False
            End If
            If Index < 0 OrElse Index >= Count Then
                Return False
            End If
            Return TypeOf m_info.Rows(Index) Is GroupRow
        End Function
        Private order As SortOrder = SortOrder.Ascending
        <DefaultValue(SortOrder.Ascending)> _
        Public Property GroupSortDirection() As SortOrder
            Get
                Return order
            End Get
            Set(ByVal value As SortOrder)
                If order = value Then
                    Return
                End If
                order = value
                ResetGroup()
            End Set
        End Property
        Private Class GroupRow
            Implements IGroupRow
Private _Index As Integer
            Public Property Index() As Integer
                Get
                    Return _Index
                End Get
                Set(ByVal value As Integer)
                    _Index = value
                End Set
            End Property
Private _Value As Object
            Public Property Value() As Object
                Get
                    Return _Value
                End Get
                Set(ByVal value As Object)
                    _Value = value
                End Set
            End Property
Private _Rows As Object()
            Public Property Rows() As Object()
                Get
                    Return _Rows
                End Get
                Set(ByVal value As Object())
                    _Rows = value
                End Set
            End Property
            Public ReadOnly Property Count() As Integer
                Get
                    Return Rows.Length
                End Get
            End Property
            Friend List As New List(Of Object)()
            Public Sub Finalize(ByVal Index As Integer)
                Me.Index = Index
                Rows = List.ToArray()
                List = Nothing
            End Sub
        End Class
        Public Function GetGroups() As IEnumerable(Of IGroupRow)
            For Each g As IGroupRow In Info.Groups.Values
                
            Next
        End Function
        Private Class GroupInfo
            Public ReadOnly Owner As GroupingSource
            Public Sub New(ByVal Owner As GroupingSource)
                Me.Owner = Owner
                [set]()
            End Sub
            Public ReadOnly Property TotalCount() As Integer
                Get
                    Return Rows.Count
                End Get
            End Property
            Public Rows As IList
            Public Groups As IDictionary(Of Object, GroupRow)
            Private Sub [set]()
                Groups = Nothing
                Dim gi As GroupingInfo = Owner.groupon
                If gi Is Nothing Then
                    Rows = Owner.List
                    Exit Sub
                End If
                If Owner.GroupSortDirection = SortOrder.None Then
                    Groups = New Dictionary(Of Object, GroupRow)()
                Else
                    Dim comparer As New GenericComparer(Of Object)()
                    comparer.Descending = Owner.GroupSortDirection = SortOrder.Descending
                    Groups = New SortedDictionary(Of Object, GroupRow)(comparer)
                End If
                For Each row As Object In Owner
                    Dim key As Object = gi.GetGroupValue(row)
                    Dim gr As GroupRow
                    If Not Groups.TryGetValue(key, gr) Then
                        gr = New GroupRow()
                        gr.Value = key
                        Groups.Add(key, gr)
                    End If
                    gr.List.Add(row)
                Next
                Dim i As Integer = 0
                Rows = New List(Of Object)(Groups.Count + Owner.BaseCount)
                For Each g As GroupRow In Groups.Values
                    g.Finalize(System.Math.Max(System.Threading.Interlocked.Increment(i),i - 1))
                    Rows.Add(g)
                    For Each row As Object In g.Rows
                        Rows.Add(row)
                    Next
                Next
            End Sub
        End Class
        Private m_info As GroupInfo
        Private ReadOnly Property Info() As GroupInfo
            Get
                If m_info Is Nothing Then
                    m_info = New GroupInfo(Me)
                    If bsource IsNot Nothing Then
                        SyncWithBSource()
                    End If
                End If
                Return m_info
            End Get
        End Property
        Private Sub OnGroupingChanged()
            RaiseEvent GroupingChanged(Me, EventArgs.Empty)
        End Sub
        Public Event GroupingChanged As EventHandler
        Public Sub ResetGroup()
            m_info = Nothing
        End Sub
        Private bsource As BindingSource
        Private Sub DisposeBindingSourceEvents()
            If bsource Is Nothing Then
                Exit Sub
            End If
            RemoveHandler bsource.CurrentChanged, AddressOf bsource_CurrentChanged
        End Sub
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            DisposeBindingSourceEvents()
            MyBase.Dispose(disposing)
        End Sub
        Protected Overloads Overrides Sub OnDataSourceChanged(ByVal e As EventArgs)
            ResetGroup()
            DisposeBindingSourceEvents()
            bsource = TryCast(DataSource, BindingSource)
            If bsource IsNot Nothing Then
                AddHandler bsource.CurrentChanged, AddressOf bsource_CurrentChanged
                If NeedSync Then
                    SyncWithBSource()
                End If
            End If
            MyBase.OnDataSourceChanged(e)
        End Sub
        Private suspendlistchange As Boolean
        Protected Overloads Overrides Sub OnListChanged(ByVal e As ListChangedEventArgs)
            If suspendlistchange Then
                Exit Sub
            End If
            Select Case e.ListChangedType
                Case ListChangedType.ItemChanged
                    If m_groupon IsNot Nothing AndAlso m_groupon.IsProperty(e.PropertyDescriptor) Then
                        ResetGroup()
                    End If
                    Exit Select
                Case ListChangedType.ItemAdded
                    If m_info IsNot Nothing Then
                        m_info.Rows.Add(List(e.NewIndex))
                    End If
                    Exit Select
                Case ListChangedType.ItemDeleted
                    ResetGroup()
                    Exit Select
                Case ListChangedType.Reset
                    ResetGroup()
                    Exit Select
                Case ListChangedType.PropertyDescriptorAdded, ListChangedType.PropertyDescriptorChanged, ListChangedType.PropertyDescriptorDeleted
                    props = Nothing
                    Exit Select
            End Select
            MyBase.OnListChanged(e)
        End Sub
        Public Overloads Overrides Function AddNew() As Object
            suspendlistchange = True
            Try
                Dim res = MyBase.AddNew()
                If m_info IsNot Nothing Then
                    m_info.Rows.Add(res)
                End If
                Return res
            Finally
                suspendlistchange = False
            End Try
        End Function
        Public Overloads Overrides Sub ApplySort(ByVal [property] As PropertyDescriptor, ByVal sort As ListSortDirection)
            If TypeOf [property] Is PropertyWrapper Then
                [property] = TryCast([property], PropertyWrapper).[Property]
            End If
            MyBase.ApplySort([property], sort)
        End Sub
        Public Overloads Overrides Sub ApplySort(ByVal sorts As ListSortDescriptionCollection)
            MyBase.ApplySort(sorts)
        End Sub
        Public Overloads Overrides Sub RemoveAt(ByVal index As Integer)
            If m_info Is Nothing OrElse m_groupon Is Nothing Then
                MyBase.RemoveAt(index)
            ElseIf Not IsGroupRow(index) Then
                Dim i = List.IndexOf(Me(index))
                suspendlistchange = True
                Try
                    m_info.Rows.RemoveAt(index)
                    List.RemoveAt(i)
                    MyBase.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
                Finally
                    suspendlistchange = False
                End Try
            End If
        End Sub
        Public Overloads Overrides Sub Remove(ByVal value As Object)
            If TypeOf value Is GroupRow Then
                Exit Sub
            End If
            Dim index As Integer = Me.IndexOf(value)
            If index <> -1 Then
                RemoveAt(index)
            End If
        End Sub
        Protected Overloads Overrides Sub OnCurrentChanged(ByVal e As EventArgs)
            MyBase.OnCurrentChanged(e)
            If NeedSync AndAlso Not (TypeOf Current Is GroupRow) Then
                bsource.Position = bsource.IndexOf(Current)
            End If
        End Sub
        Private Sub bsource_CurrentChanged(ByVal sender As Object, ByVal e As EventArgs)
            If NeedSync Then
                SyncWithBSource()
            End If
        End Sub
        Private ReadOnly Property NeedSync() As Boolean
            Get
                If bsource Is Nothing OrElse suspendlistchange Then
                    Return False
                End If
                If bsource.IsBindingSuspended Then
                    Return False
                End If
                Return Current <> bsource.Current
            End Get
        End Property
        Private Sub SyncWithBSource()
            Position = IndexOf(bsource.Current)
        End Sub
        Public Overloads Overrides Function IndexOf(ByVal value As Object) As Integer
            Return Info.Rows.IndexOf(value)
        End Function
        Public Class PropertyWrapper
            Inherits PropertyDescriptor
            Public ReadOnly [Property] As PropertyDescriptor
            Public ReadOnly Owner As GroupingSource
            Public Sub New(ByVal [Property] As PropertyDescriptor, ByVal Owner As GroupingSource)
                MyBase.New([Property])
                Me.[Property] = [Property]
                Me.Owner = Owner
            End Sub
            Public Overloads Overrides Function CanResetValue(ByVal component As Object) As Boolean
                Return [Property].CanResetValue(component)
            End Function
            Public Overloads Overrides ReadOnly Property ComponentType() As Type
                Get
                    Return [Property].ComponentType
                End Get
            End Property
            Public Overloads Overrides Function GetValue(ByVal component As Object) As Object
                If TypeOf component Is GroupRow Then
                    If Owner.groupon.IsProperty([Property]) Then
                        Return TryCast(component, GroupRow).Value
                    End If
                    Return Nothing
                End If
                Return [Property].GetValue(component)
            End Function
            Public Overloads Overrides ReadOnly Property IsReadOnly() As Boolean
                Get
                    Return [Property].IsReadOnly
                End Get
            End Property
            Public Overloads Overrides ReadOnly Property PropertyType() As Type
                Get
                    Return [Property].PropertyType
                End Get
            End Property
            Public Overloads Overrides Sub ResetValue(ByVal component As Object)
                [Property].ResetValue(component)
            End Sub
            Public Overloads Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
                [Property].SetValue(component, value)
            End Sub
            Public Overloads Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
                Return [Property].ShouldSerializeValue(component)
            End Function
        End Class
        Private props As PropertyDescriptorCollection
        Public Overloads Overrides Function GetItemProperties(ByVal listAccessors As PropertyDescriptor()) As PropertyDescriptorCollection
            If listAccessors Is Nothing Then
                If props Is Nothing Then
                    props = MyBase.GetItemProperties(Nothing)
                    Dim arr As PropertyDescriptor() = New PropertyDescriptor(props.Count - 1) {}
                    For i As Integer = 0 To props.Count - 1
                        arr(i) = New PropertyWrapper(props(i), Me)
                    Next
                    props = New PropertyDescriptorCollection(arr)
                End If
                Return props
            End If
            Return MyBase.GetItemProperties(listAccessors)
        End Function
        Public ReadOnly Property BaseCount() As Integer
            Get
                Return MyBase.Count
            End Get
        End Property
        Public Function GetBaseRow(ByVal Index As Integer) As Object
            Return List(Index)
        End Function
        Public Overloads Overrides ReadOnly Property Count() As Integer
            Get
                Return Info.TotalCount
            End Get
        End Property
        Public Overloads Overrides Default Property Item(ByVal index As Integer) As Object
            Get
                Return Info.Rows(index)
            End Get
            Set
                Info.Rows(index) = value
            End Set
        End Property
    End Class
    Public MustInherit Class GroupingInfo
        Public MustOverride Function GetGroupValue(ByVal Row As Object) As Object
        Public Overridable Function IsProperty(ByVal p As PropertyDescriptor) As Boolean
            Return False
        End Function
        Public Overridable Function IsProperty(ByVal Name As String) As Boolean
            Return Name = ToString()
        End Function
        Public Shared Widening Operator CType(ByVal p As PropertyDescriptor) As GroupingInfo
            Return New PropertyGrouper(p)
        End Operator
        Public Overridable Sub SetDisplayValues(ByVal e As GroupDisplayEventArgs)
            Dim o = e.Value
            e.DisplayValue = If(o Is Nothing, "<Null>", o.ToString())
        End Sub
    End Class
    Public Class PropertyGrouper
        Inherits GroupingInfo
        Public ReadOnly [Property] As PropertyDescriptor
        Public Sub New(ByVal [Property] As PropertyDescriptor)
            If [Property] Is Nothing Then
                Throw New ArgumentNullException()
            End If
            Me.[Property] = [Property]
        End Sub
        Public Overloads Overrides Function GetGroupValue(ByVal Row As Object) As Object
            Return [Property].GetValue(Row)
        End Function
        Public Overloads Overrides Function ToString() As String
            Return [Property].Name
        End Function
    End Class
    Public Delegate Function GroupingDelegate(ByVal Row As Object) As Object
    Public Class DelegateGrouper
        Inherits GroupingInfo
        Public ReadOnly Name As String
        Public ReadOnly GroupingDelegate As GroupingDelegate
        Public Sub New(ByVal [Delegate] As GroupingDelegate, ByVal Name As String)
            If [Delegate] Is Nothing Then
                Throw New ArgumentNullException()
            End If
            Me.Name = Name
            If Name Is Nothing Then
                Me.Name = [Delegate].ToString()
            End If
            Me.GroupingDelegate = [Delegate]
        End Sub
        Public Overloads Overrides Function ToString() As String
            Return Name
        End Function
        Public Overloads Overrides Function GetGroupValue(ByVal Row As Object) As Object
            Return GroupingDelegate(Row)
        End Function
    End Class
    Public Class StartLetterGrouper
        Inherits GroupingInfo
        Public ReadOnly Grouper As GroupingInfo
        Public ReadOnly Letters As Integer
        Public Sub New(ByVal Grouper As GroupingInfo)
            Me.New(Grouper, 1)
        End Sub
        Public Sub New(ByVal Grouper As GroupingInfo, ByVal Letters As Integer)
            If Grouper Is Nothing Then
                Throw New ArgumentNullException()
            End If
            Me.Grouper = Grouper
            Me.Letters = Letters
        End Sub
        Public Overloads Overrides Function ToString() As String
            Return Grouper.ToString()
        End Function
        Public Overloads Overrides Function IsProperty(ByVal p As PropertyDescriptor) As Boolean
            Return Grouper.IsProperty(p)
        End Function
        Public Overloads Overrides Function GetGroupValue(ByVal Row As Object) As Object
            Dim val = Grouper.GetGroupValue(Row)
            If val Is Nothing Then
                Return Nothing
            End If
            Dim s = val.ToString()
            If s.Length < Letters Then
                Return s
            End If
            Return s.Substring(0, Letters)
        End Function
    End Class
    Public Interface IDataGridViewGrouperOwner
        ReadOnly Property Grouper() As DataGridViewGrouper
    End Interface
    Public Interface IGrouper
        Sub SetGroupOn(ByVal col As String)
        Sub SetGroupOn(ByVal col As PropertyDescriptor)
        Sub RemoveGrouping()
Private _GroupOn As GroupingInfo
        Property GroupOn() As GroupingInfo
        Event PropertiesChanged As EventHandler
        Event GroupingChanged As EventHandler
        Function GetProperties() As IEnumerable(Of PropertyDescriptor)
    End Interface
End Namespace
Namespace Subro
    ''' <summary>
    ''' Comparer that tries to find the 'strongest' comparer for a type.
    ''' if the type implements a generic IComparable, that is used.
    ''' otherwise if it implements a normal IComparable, that is used.
    ''' If neither are implemented, the ToString versions are compared.
    ''' INullable structures are also supported.
    ''' This way, the DefaultComparer can compare any object types and can be used for sorting any source.
    ''' </summary>
    ''' <example>Array.Sort(YourArray,new GenericComparer());</example>
    Public Class GenericComparer
        Implements IComparer
        Public Sub New()
        End Sub
        Public Sub New(ByVal Type As Type)
            Me.Type = Type
        End Sub
        Private m_type As Type
        Public Property Type() As Type
            Get
                Return m_type
            End Get
            Set(ByVal value As Type)
                If value Is Nothing Then
                    Throw New ArgumentNullException()
                End If
                m_type = value
                comp = Nothing
            End Set
        End Property
        Private m_targettype As Type
        ''' <summary>
        ''' normally the same as the type, but can be set to a different type
        ''' </summary>
        Public Property TargetType() As Type
            Get
                If m_targettype Is Nothing Then
                    Return m_type
                End If
                Return m_targettype
            End Get
            Set(ByVal value As Type)
                If TargetType = value Then
                    Return
                End If
                m_targettype = value
                comp = Nothing
            End Set
        End Property
        
        
        Private comp As IComparer
        Private Function GetGenericComparer(ByVal From As Type, ByVal [To] As Type) As IComparer
            While [To] <> GetType(Object)
                If GetType(IComparable(Of )).MakeGenericType([To]).IsAssignableFrom(From) Then
                    Return DirectCast(Activator.CreateInstance(GetType(StrongCompare(Of , )).MakeGenericType(From, [To])), IComparer)
                End If
                [To] = [To].BaseType
            End While
            Return Nothing
        End Function
        Public Function GetComparer(ByVal From As Type, ByVal [To] As Type) As IComparer
            Dim gen = GetGenericComparer(From, [To])
            If gen IsNot Nothing Then
                Return gen
            ElseIf GetType(IComparable).IsAssignableFrom(m_type) Then
                Return DirectCast(Activator.CreateInstance(GetType(NonGenericCompare(Of )).MakeGenericType(m_type)), IComparer)
            ElseIf m_type.IsGenericType AndAlso GetType(Nullable(Of )) Is m_type.GetGenericTypeDefinition() Then
                Dim basetype = m_type.GetGenericArguments()(0)
                Return DirectCast(Activator.CreateInstance(GetType(NullableComparer(Of )).MakeGenericType(basetype), GetComparer(basetype, If([To] = From, basetype, [To]))), IComparer)
            End If
            Return New StringComparer()
        End Function
        Private Class NullableComparer(Of T As Structure)
            Implements IComparer
            Public ReadOnly BaseComparer As IComparer
            Public Sub New(ByVal BaseComparer As IComparer)
                Me.BaseComparer = BaseComparer
            End Sub
            Private Function getval(ByVal o As Object) As Object
                Return DirectCast(o, Nullable(Of T)).Value
            End Function
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return BaseComparer.Compare(getval(x), getval(y))
            End Function
        End Class
        Private Class StrongCompare(Of F As IComparable(Of T), T)
            Implements IComparer
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return DirectCast(x, F).CompareTo(DirectCast(y, T))
            End Function
        End Class
        Private Class NonGenericCompare(Of T As IComparable)
            Implements IComparer
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return DirectCast(x, T).CompareTo(y)
            End Function
        End Class
        Private Class StringComparer
            Implements IComparer
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return String.Compare(x.ToString(), y.ToString())
            End Function
        End Class
        Public Property Descending() As Boolean
            Get
                Return factor < 0
            End Get
            Set(ByVal value As Boolean)
                factor = If(value, -1, 1)
            End Set
        End Property
        Private factor As Integer = 1
        Private Function compare(ByVal x As Object, ByVal y As Object) As Integer
            If x = y Then
                Return 0
            End If
            If x Is Nothing Then
                Return -1
            End If
            If y Is Nothing Then
                Return 1
            End If
            If m_type Is Nothing Then
                Type = x.[GetType]()
            End If
            If comp Is Nothing Then
                comp = GetComparer(m_type, TargetType)
            End If
            Return comp.Compare(x, y)
        End Function
        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
            Return factor * compare(x, y)
        End Function
    End Class
    
    Public Class GenericComparer(Of T)
        Inherits GenericComparer
        Implements IComparer(Of T)
        Public Sub New()
            MyBase.New(GetType(T))
        End Sub
        Public Function Compare(ByVal a As T, ByVal b As T) As Integer
            Return MyBase.Compare(a, b)
        End Function
    End Class
    
    
    Public Class PropertyDescriptorComparer
        Inherits GenericComparer
        Public ReadOnly Prop As PropertyDescriptor
        Public Sub New(ByVal Prop As PropertyDescriptor)
            Me.New(Prop, True)
        End Sub
        Public Sub New(ByVal Prop As PropertyDescriptor, ByVal Descending As Boolean)
            MyBase.New(Prop.PropertyType)
            Me.Prop = Prop
            Me.Descending = Descending
        End Sub
    End Class
End Namespace

Open in new window

0
 
LVL 59

Assisted Solution

by:Kevin Cross
Kevin Cross earned 200 total points
ID: 24736843
Since VB.NET doesn't support the yeild (http://msdn.microsoft.com/en-us/library/9k7k7cf0(VS.80).aspx), I removed the yield return by changing to return and then did the conversion shown below.

''' <summary>
''' Add this component in runtime or designtime and assign a datagridview to it to enable grouping on that grid.
''' You can also add an
''' </summary>
 
 
 
#Region "Select /  Collapse/Expand"
 
 
''' <summary>
''' selected rows are kept seperately in order to invalidate the entire row
''' and not just one cell when the selection is changed
''' </summary>
 
 
#End Region
 
 
 
 
 
#Region "Painting"
 
 
 
''' <summary>
''' This event is fired when the group row has to be painted and the display values are requested
''' </summary>
'cancelled
'line under the group row
'collapse/expand symbol               
'group value
 
 
#End Region
 
 
 
 
 
'public List<GroupRow> Groups = new List<GroupRow>();
 
'var groups = Owner.Cast<object>().GroupBy<object, object>(o => gr.GetGroupValue(o));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
'
'props = new PropertyDescriptorCollection(
'base.GetItemProperties(null).Cast<PropertyDescriptor>()
'.Select(pd => new PropertyWrapper(pd, this)).ToArray());
 
#Region "Grouping Info objects"
 
 
#End Region
#Region "Interfaces"
#End Region
 
 
'
'* Added the Generic comparer here, for ease of use on blog posting ;)
'
 
 
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Collections
Imports System.ComponentModel
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Namespace Subro.Controls
    <DefaultEvent("DisplayGroup")> _
    Public Class DataGridViewGrouper
        Inherits Component
        Implements IGrouper
        Public Sub New()
            AddHandler source.DataSourceChanged, AddressOf source_DataSourceChanged
            AddHandler source.GroupingChanged, AddressOf source_GroupingChanged
        End Sub
        Public Sub New(ByVal Grid As DataGridView)
            Me.New()
            Me.DataGridView = Grid
        End Sub
        Public Sub New(ByVal Container As IContainer)
            Me.New()
            Container.Add(Me)
        End Sub
        Private grid As DataGridView
        <DefaultValue(Nothing)> _
        Public Property DataGridView() As DataGridView
            Get
                Return grid
            End Get
            Set(ByVal value As DataGridView)
                If grid = value Then
                    Return
                End If
                If grid IsNot Nothing Then
                    RemoveHandler grid.Sorted, AddressOf grid_Sorted
                    RemoveHandler grid.RowPrePaint, AddressOf grid_RowPrePaint
                    RemoveHandler grid.RowPostPaint, AddressOf grid_RowPostPaint
                    RemoveHandler grid.CellBeginEdit, AddressOf grid_CellBeginEdit
                    RemoveHandler grid.CellDoubleClick, AddressOf grid_CellDoubleClick
                    RemoveHandler grid.CellClick, AddressOf grid_CellClick
                    RemoveHandler grid.MouseMove, AddressOf grid_MouseMove
                    RemoveHandler grid.SelectionChanged, AddressOf grid_SelectionChanged
                End If
                RemoveGrouping()
                selectedrows.Clear()
                grid = value
                If grid IsNot Nothing Then
                    AddHandler grid.Sorted, AddressOf grid_Sorted
                    AddHandler grid.RowPrePaint, AddressOf grid_RowPrePaint
                    AddHandler grid.RowPostPaint, AddressOf grid_RowPostPaint
                    AddHandler grid.CellBeginEdit, AddressOf grid_CellBeginEdit
                    AddHandler grid.CellDoubleClick, AddressOf grid_CellDoubleClick
                    AddHandler grid.CellClick, AddressOf grid_CellClick
                    AddHandler grid.MouseMove, AddressOf grid_MouseMove
                    AddHandler grid.SelectionChanged, AddressOf grid_SelectionChanged
                End If
            End Set
        End Property
        Private capturedcollapsebox As New Point(-1, -1)
        Private Sub grid_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
            If e.X < HeaderOffset AndAlso e.X >= HeaderOffset - collapseboxwidth Then
                Dim ht As DataGridView.HitTestInfo = grid.HitTest(e.X, e.Y)
                If IsGroupRow(ht.RowIndex) Then
                    Dim y = e.Y - ht.RowY
                    If y >= CollapseBox_Y_Offset AndAlso y <= CollapseBox_Y_Offset + collapseboxwidth Then
                        checkcollapsedfocused(ht.ColumnIndex, ht.RowIndex)
                        Exit Sub
                    End If
                End If
            End If
            checkcollapsedfocused(-1, -1)
        End Sub
        Private Sub InvalidateCapturedBox()
            If capturedcollapsebox.Y = -1 Then
                Exit Sub
            End If
            grid.InvalidateCell(capturedcollapsebox.X, capturedcollapsebox.Y)
        End Sub
        Private Sub checkcollapsedfocused(ByVal col As Integer, ByVal row As Integer)
            If capturedcollapsebox.X <> col OrElse capturedcollapsebox.Y <> row Then
                InvalidateCapturedBox()
                capturedcollapsebox = New Point(col, row)
                InvalidateCapturedBox()
            End If
        End Sub
        Private Sub grid_CellClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs)
            If e.RowIndex = -1 Then
                Exit Sub
            End If
            If e.RowIndex = capturedcollapsebox.Y Then
                Dim show As Boolean = IsCollapsed(e.RowIndex)
                CollapseExpand(e.RowIndex, show)
                If show Then
                    grid.CurrentCell = grid(1, e.RowIndex + 1)
                End If
            End If
        End Sub
        Private selectedrows As New List(Of Integer)()
        Private Sub grid_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs)
            invalidateselected()
            selectedrows.Clear()
            For Each c As DataGridViewCell In grid.SelectedCells
                If IsGroupRow(c.RowIndex) Then
                    selectedrows.Add(c.RowIndex)
                End If
            Next
            invalidateselected()
        End Sub
        Private Sub invalidateselected()
            If selectedrows.Count = 0 OrElse grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect Then
                Exit Sub
            End If
            For Each i As Integer In selectedrows
                grid.InvalidateRow(i)
            Next
        End Sub
        Private Function IsCollapsed(ByVal index As Integer) As Boolean
            If System.Threading.Interlocked.Increment(index) >= grid.Rows.Count Then
                Return False
            End If
            Return Not grid.Rows(index).Visible
        End Function
        Private Sub CollapseExpand(ByVal index As Integer, ByVal show As Boolean)
            grid.SuspendLayout()
            For Each row As DataGridViewRow In GetRows(index)
                row.Visible = show
            Next
            grid.ResumeLayout()
        End Sub
        Public Sub ExpandAll()
            CollapseExpandAll(True)
        End Sub
        Public Sub CollapseAll()
            CollapseExpandAll(False)
        End Sub
        Private Sub CollapseExpandAll(ByVal show As Boolean)
            If grid Is Nothing OrElse Not GridUsesGroupSource Then
                Exit Sub
            End If
            grid.SuspendLayout()
            source.SuspendBinding()
            Dim cnt As Integer = source.Count
            For i As Integer = 0 To cnt - 1
                If Not IsGroupRow(i) Then
                    grid.Rows(i).Visible = show
                End If
            Next
            grid.ResumeLayout()
            source.ResumeBinding()
        End Sub
        Private Sub grid_CellDoubleClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs)
            If IsGroupRow(e.RowIndex) Then
                CollapseExpand(e.RowIndex, True)
                grid.SuspendLayout()
                grid.CurrentCell = grid(1, e.RowIndex + 1)
                grid.Rows(e.RowIndex).Selected = False
                SelectGroup(e.RowIndex)
                grid.ResumeLayout()
            End If
        End Sub
        Private Function GetRows(ByVal index As Integer) As IEnumerable(Of DataGridViewRow)
            While Not IsGroupRow(System.Threading.Interlocked.Increment(index)) AndAlso index < source.Count
                Return grid.Rows(index)
            End While
        End Function
        Private Sub SelectGroup(ByVal offset As Integer)
            For Each row As DataGridViewRow In GetRows(offset)
                row.Selected = True
            Next
        End Sub
        Public Function GetGroups() As IEnumerable(Of IGroupRow)
            Return source.GetGroups()
        End Function
        Public Function IsGroupRow(ByVal Index As Integer) As Boolean
            Return source.IsGroupRow(Index)
        End Function
        Private Sub source_DataSourceChanged(ByVal sender As Object, ByVal e As EventArgs)
            RaiseEvent PropertiesChanged(Me, e)
        End Sub
        Public Event PropertiesChanged As EventHandler
        Public Function GetProperties() As IEnumerable(Of PropertyDescriptor)
            For Each pd As PropertyDescriptor In source.GetItemProperties(Nothing)
                Return pd
            Next
        End Function
        Private Sub grid_CellBeginEdit(ByVal sender As Object, ByVal e As DataGridViewCellCancelEventArgs)
            If IsGroupRow(e.RowIndex) Then
                e.Cancel = True
            End If
        End Sub
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            DataGridView = Nothing
            source.Dispose()
            MyBase.Dispose(disposing)
        End Sub
        Private Sub grid_Sorted(ByVal sender As Object, ByVal e As EventArgs)
            Me.capturedcollapsebox = New Point(-1, -1)
            ResetGrouping()
        End Sub
        Private source As New GroupingSource()
        Public ReadOnly Property GroupingSource() As GroupingSource
            Get
                Return source
            End Get
        End Property
        Public Sub RemoveGrouping()
            If GridUsesGroupSource Then
                Try
                    grid.DataSource = source.DataSource
                    grid.DataMember = source.DataMember
                    source.RemoveGrouping()
                Catch
                End Try
            End If
        End Sub
        Private Sub source_GroupingChanged(ByVal sender As Object, ByVal e As EventArgs)
            OnGroupOnChanged()
        End Sub
        Public Event GroupingChanged As EventHandler
        Private Sub OnGroupOnChanged()
            RaiseEvent GroupingChanged(Me, EventArgs.Empty)
        End Sub
        Private ReadOnly Property GridUsesGroupSource() As Boolean
            Get
                Return grid IsNot Nothing AndAlso grid.DataSource = source
            End Get
        End Property
        Public Sub ResetGrouping()
            If Not GridUsesGroupSource Then
                Exit Sub
            End If
            source.ResetGroup()
        End Sub
        <DefaultValue(Nothing)> _
        Public Property GroupOn() As GroupingInfo
            Get
                Return source.GroupOn
            End Get
            Set(ByVal value As GroupingInfo)
                If GroupOn = value Then
                    Exit Sub
                End If
                If value Is Nothing Then
                    RemoveGrouping()
                Else
                    CheckSource().GroupOn = value
                End If
            End Set
        End Property
        Public ReadOnly Property IsGrouped() As Boolean
            Get
                Return source.GroupOn IsNot Nothing
            End Get
        End Property
        <DefaultValue(SortOrder.Ascending)> _
        Public Property SortOrder() As SortOrder
            Get
                Return source.GroupSortDirection
            End Get
            Set(ByVal value As SortOrder)
                source.GroupSortDirection = value
            End Set
        End Property
        Public Sub SetGroupOn(ByVal col As DataGridViewColumn)
            SetGroupOn(If(col Is Nothing, Nothing, col.DataPropertyName))
        End Sub
        Public Sub SetGroupOn(ByVal [Property] As PropertyDescriptor)
            CheckSource().SetGroupOn([Property])
        End Sub
        Public Sub SetGroupOn(ByVal gd As GroupingDelegate)
            CheckSource().SetGroupOn(gd)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal g As GroupingInfo, ByVal Letters As Integer)
            CheckSource().SetGroupOnStartLetters(g, Letters)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal [Property] As String, ByVal Letters As Integer)
            CheckSource().SetGroupOnStartLetters([Property], Letters)
        End Sub
        Public Sub SetGroupOn(ByVal Name As String)
            If String.IsNullOrEmpty(Name) Then
                RemoveGrouping()
            Else
                CheckSource().SetGroupOn(Name)
            End If
        End Sub
        Private Function CheckSource() As GroupingSource
            If grid Is Nothing Then
                Throw New Exception("No target datagridview set")
            End If
            If Not GridUsesGroupSource Then
                source.DataSource = grid.DataSource
                source.DataMember = grid.DataMember
                grid.DataSource = source
            End If
            Return source
        End Function
        Private Sub grid_RowPrePaint(ByVal sender As Object, ByVal e As DataGridViewRowPrePaintEventArgs)
            If IsGroupRow(e.RowIndex) Then
                e.Handled = True
                PaintGroupRow(e)
            End If
        End Sub
        Const collapseboxwidth As Integer = 10
        Const lineoffset As Integer = collapseboxwidth / 2
        Private ReadOnly Property HeaderOffset() As Integer
            Get
                If grid.RowHeadersVisible Then
                    Return grid.RowHeadersWidth - lineoffset
                End If
                Return lineoffset * 4
            End Get
        End Property
        Private linepen As Pen = Pens.SteelBlue
        Private ReadOnly Property DrawExpandCollapseLines() As Boolean
            Get
                Return grid.RowHeadersVisible
            End Get
        End Property
        Private Sub grid_RowPostPaint(ByVal sender As Object, ByVal e As DataGridViewRowPostPaintEventArgs)
            If Not DrawExpandCollapseLines OrElse e.RowIndex >= source.Count Then
                Exit Sub
            End If
            Dim [next] As Integer = e.RowIndex + 1
            Dim r As Integer = grid.RowHeadersWidth
            Dim x As Integer = HeaderOffset - lineoffset
            Dim y As Integer = e.RowBounds.Top + e.RowBounds.Height / 2
            e.Graphics.DrawLine(linepen, x, y, r, y)
            If [next] < source.Count AndAlso Not IsGroupRow([next]) Then
                y = e.RowBounds.Bottom
            End If
            e.Graphics.DrawLine(linepen, x, e.RowBounds.Top, x, y)
        End Sub
        Private showheader As Boolean = True
        <DefaultValue(True)> _
        Public Property ShowGroupName() As Boolean
            Get
                Return showheader
            End Get
            Set(ByVal value As Boolean)
                If showheader = value Then
                    Exit Sub
                End If
                showheader = value
                If grid IsNot Nothing Then
                    grid.Invalidate()
                End If
            End Set
        End Property
        Private m_showcount As Boolean = True
        <DefaultValue(True)> _
        Public Property ShowCount() As Boolean
            Get
                Return m_showcount
            End Get
            Set(ByVal value As Boolean)
                If m_showcount = value Then
                    Exit Sub
                End If
                m_showcount = value
                If grid IsNot Nothing Then
                    grid.Invalidate()
                End If
            End Set
        End Property
        Public Event DisplayGroup As EventHandler(Of GroupDisplayEventArgs)
        Private Function GetDisplayValues(ByVal pe As DataGridViewRowPrePaintEventArgs) As GroupDisplayEventArgs
            Dim row As IGroupRow = TryCast(source(pe.RowIndex), IGroupRow)
            Dim e As New GroupDisplayEventArgs(row, source.GroupOn)
            Dim selected As Boolean = selectedrows.Contains(pe.RowIndex)
            e.Selected = selected
            e.BackColor = If(selected, grid.DefaultCellStyle.SelectionBackColor, grid.DefaultCellStyle.BackColor)
            e.ForeColor = If(selected, grid.DefaultCellStyle.SelectionForeColor, grid.DefaultCellStyle.ForeColor)
            e.Font = pe.InheritedRowStyle.Font
            If m_showcount Then
                e.Summary = "(" & row.Count & ")"
            End If
            If showheader Then
                e.Header = source.GroupOn.ToString()
            End If
            e.GroupingInfo.SetDisplayValues(e)
            If e.Cancel Then
                Return Nothing
            End If
            If DisplayGroup IsNot Nothing Then
                DisplayGroup(Me, e)
                If e.Cancel Then
                    Return Nothing
                End If
            End If
            Return e
        End Function
        Private Sub PaintGroupRow(ByVal e As DataGridViewRowPrePaintEventArgs)
            Dim info = GetDisplayValues(e)
            If info Is Nothing Then
                Exit Sub
            End If
            Dim r = e.RowBounds
            r.X = 1
            r.Height -= 1
            Using b = New SolidBrush(info.BackColor)
                e.Graphics.FillRectangle(b, r)
            End Using
            e.Graphics.DrawLine(Pens.SteelBlue, r.Left, r.Bottom, r.Right, r.Bottom)
            If True Then
                Dim cer = GetCollapseBoxBounds(e.RowBounds.Y)
                If capturedcollapsebox.Y = e.RowIndex Then
                    e.Graphics.FillEllipse(Brushes.Yellow, cer)
                End If
                e.Graphics.DrawEllipse(linepen, cer)
                Dim collapsed As Boolean = IsCollapsed(e.RowIndex)
                Dim cx As Integer
                If DrawExpandCollapseLines AndAlso Not collapsed Then
                    cx = HeaderOffset - lineoffset
                    e.Graphics.DrawLine(linepen, cx, cer.Bottom, cx, r.Bottom)
                End If
                cer.Inflate(-2, -2)
                Dim cy = cer.Y + cer.Height / 2
                e.Graphics.DrawLine(linepen, cer.X, cy, cer.Right, cy)
                If collapsed Then
                    cx = cer.X + cer.Width / 2
                    e.Graphics.DrawLine(linepen, cx, cer.Top, cx, cer.Bottom)
                End If
            End If
            If True Then
                r.X = HeaderOffset + 1
                Using fb = New SolidBrush(info.ForeColor)
                    Dim sf = New StringFormat()
                    If info.Header IsNot Nothing Then
                        Dim size = e.Graphics.MeasureString(info.Header, info.Font)
                        e.Graphics.DrawString(info.Header, info.Font, fb, r, sf)
                        r.Offset(CInt(size.Width) + 5, 0)
                    End If
                    If info.DisplayValue IsNot Nothing Then
                        Using f = New Font(info.Font.FontFamily, info.Font.Size + 2, FontStyle.Bold)
                            Dim size = e.Graphics.MeasureString(info.DisplayValue, f)
                            e.Graphics.DrawString(info.DisplayValue, f, fb, r, sf)
                            r.Offset(CInt(size.Width) + 10, 0)
                        End Using
                    End If
                    If info.Summary IsNot Nothing Then
                        e.Graphics.DrawString(info.Summary, info.Font, fb, r, sf)
                    End If
                End Using
            End If
        End Sub
        Const CollapseBox_Y_Offset As Integer = 5
        Private Function GetCollapseBoxBounds(ByVal Y_Offset As Integer) As Rectangle
            Return New Rectangle(HeaderOffset - collapseboxwidth, Y_Offset + CollapseBox_Y_Offset, collapseboxwidth, collapseboxwidth)
        End Function
        Public ReadOnly Property CurrentRowIsGroupRow() As Boolean
            Get
                If grid Is Nothing OrElse grid.CurrentCell Is Nothing Then
                    Return False
                End If
                Return IsGroupRow(grid.CurrentCell.RowIndex)
            End Get
        End Property
    End Class
    Public Class GroupDisplayEventArgs
        Inherits CancelEventArgs
        Public ReadOnly Row As IGroupRow
        Public ReadOnly GroupingInfo As GroupingInfo
        Public Sub New(ByVal Row As IGroupRow, ByVal Info As GroupingInfo)
            Me.Row = Row
            Me.GroupingInfo = Info
        End Sub
        Public ReadOnly Property Value() As Object
            Get
                Return Row.Value
            End Get
        End Property
Private _DisplayValue As String
        Public Property DisplayValue() As String
            Get
                Return _DisplayValue
            End Get
            Set(ByVal value As String)
                _DisplayValue = value
            End Set
        End Property
Private _Header As String
        Public Property Header() As String
            Get
                Return _Header
            End Get
            Set(ByVal value As String)
                _Header = value
            End Set
        End Property
Private _Summary As String
        Public Property Summary() As String
            Get
                Return _Summary
            End Get
            Set(ByVal value As String)
                _Summary = value
            End Set
        End Property
Private _BackColor As Color
        Public Property BackColor() As Color
            Get
                Return _BackColor
            End Get
            Set(ByVal value As Color)
                _BackColor = value
            End Set
        End Property
Private _ForeColor As Color
        Public Property ForeColor() As Color
            Get
                Return _ForeColor
            End Get
            Set(ByVal value As Color)
                _ForeColor = value
            End Set
        End Property
Private _Font As Font
        Public Property Font() As Font
            Get
                Return _Font
            End Get
            Set(ByVal value As Font)
                _Font = value
            End Set
        End Property
Private _Selected As Boolean
        Public Property Selected() As Boolean
            Get
                Return _Selected
            End Get
            Friend Set(ByVal value As Boolean)
                _Selected = value
            End Set
        End Property
    End Class
    Public Interface IGroupRow
        ReadOnly Property Index() As Integer
        ReadOnly Property Value() As Object
        ReadOnly Property Count() As Integer
        ReadOnly Property Rows() As Object()
    End Interface
    <DefaultEvent("GroupingChanged")> _
    Public Class GroupingSource
        Inherits BindingSource
        Public Sub New()
        End Sub
        Public Sub New(ByVal DataSource As Object)
            Me.New()
            Me.DataSource = DataSource
        End Sub
        Public Sub New(ByVal DataSource As Object, ByVal GroupOn As String)
            Me.New(DataSource)
        End Sub
        Private m_groupon As GroupingInfo
        <DefaultValue(Nothing)> _
        Public Property GroupOn() As GroupingInfo
            Get
                Return m_groupon
            End Get
            Set(ByVal value As GroupingInfo)
                If m_groupon = value Then
                    Return
                End If
                RemoveGrouping(value Is Nothing)
                If value IsNot Nothing Then
                    If value.Equals(m_groupon) Then
                        Return
                    End If
                    m_groupon = value
                    OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
                    OnGroupingChanged()
                End If
            End Set
        End Property
        Public Sub RemoveGrouping()
            RemoveGrouping(True)
        End Sub
        Private Sub RemoveGrouping(ByVal callListChanged As Boolean)
            If m_groupon Is Nothing Then
                Exit Sub
            End If
            m_groupon = Nothing
            ResetGroup()
            If callListChanged Then
                OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
                OnGroupingChanged()
            End If
        End Sub
        Public Sub SetGroupOn(ByVal [Property] As String)
            SetGroupOn(GetProperty([Property]))
        End Sub
        Private Function GetProperty(ByVal Name As String) As PropertyDescriptor
            Dim pd = Me.GetItemProperties(Nothing)(Name)
            If pd Is Nothing Then
                Throw New Exception(Name & " is not a valid property")
            End If
            Return pd
        End Function
        Public Sub SetGroupOn(ByVal p As PropertyDescriptor)
            If p Is Nothing Then
                Throw New ArgumentNullException()
            End If
            If m_groupon Is Nothing OrElse Not m_groupon.IsProperty(p) Then
                GroupOn = New PropertyGrouper(p)
            End If
        End Sub
        Public Sub SetGroupOn(ByVal gd As GroupingDelegate)
            SetGroupOn(gd, Nothing)
        End Sub
        Public Sub SetGroupOn(ByVal gd As GroupingDelegate, ByVal descr As String)
            If gd Is Nothing Then
                Throw New ArgumentNullException()
            End If
            GroupOn = New DelegateGrouper(gd, descr)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal g As GroupingInfo, ByVal Letters As Integer)
            GroupOn = New StartLetterGrouper(g, Letters)
        End Sub
        Public Sub SetGroupOnStartLetters(ByVal [Property] As String, ByVal Letters As Integer)
            SetGroupOnStartLetters(GetProperty([Property]), Letters)
        End Sub
        Public Function IsGroupRow(ByVal Index As Integer) As Boolean
            If m_info Is Nothing Then
                Return False
            End If
            If Index < 0 OrElse Index >= Count Then
                Return False
            End If
            Return TypeOf m_info.Rows(Index) Is GroupRow
        End Function
        Private order As SortOrder = SortOrder.Ascending
        <DefaultValue(SortOrder.Ascending)> _
        Public Property GroupSortDirection() As SortOrder
            Get
                Return order
            End Get
            Set(ByVal value As SortOrder)
                If order = value Then
                    Return
                End If
                order = value
                ResetGroup()
            End Set
        End Property
        Private Class GroupRow
            Implements IGroupRow
Private _Index As Integer
            Public Property Index() As Integer
                Get
                    Return _Index
                End Get
                Set(ByVal value As Integer)
                    _Index = value
                End Set
            End Property
Private _Value As Object
            Public Property Value() As Object
                Get
                    Return _Value
                End Get
                Set(ByVal value As Object)
                    _Value = value
                End Set
            End Property
Private _Rows As Object()
            Public Property Rows() As Object()
                Get
                    Return _Rows
                End Get
                Set(ByVal value As Object())
                    _Rows = value
                End Set
            End Property
            Public ReadOnly Property Count() As Integer
                Get
                    Return Rows.Length
                End Get
            End Property
            Friend List As New List(Of Object)()
            Public Sub Finalize(ByVal Index As Integer)
                Me.Index = Index
                Rows = List.ToArray()
                List = Nothing
            End Sub
        End Class
        Public Function GetGroups() As IEnumerable(Of IGroupRow)
            For Each g As IGroupRow In Info.Groups.Values
                Return g
            Next
        End Function
        Private Class GroupInfo
            Public ReadOnly Owner As GroupingSource
            Public Sub New(ByVal Owner As GroupingSource)
                Me.Owner = Owner
                [set]()
            End Sub
            Public ReadOnly Property TotalCount() As Integer
                Get
                    Return Rows.Count
                End Get
            End Property
            Public Rows As IList
            Public Groups As IDictionary(Of Object, GroupRow)
            Private Sub [set]()
                Groups = Nothing
                Dim gi As GroupingInfo = Owner.groupon
                If gi Is Nothing Then
                    Rows = Owner.List
                    Exit Sub
                End If
                If Owner.GroupSortDirection = SortOrder.None Then
                    Groups = New Dictionary(Of Object, GroupRow)()
                Else
                    Dim comparer As New GenericComparer(Of Object)()
                    comparer.Descending = Owner.GroupSortDirection = SortOrder.Descending
                    Groups = New SortedDictionary(Of Object, GroupRow)(comparer)
                End If
                For Each row As Object In Owner
                    Dim key As Object = gi.GetGroupValue(row)
                    Dim gr As GroupRow
                    If Not Groups.TryGetValue(key, gr) Then
                        gr = New GroupRow()
                        gr.Value = key
                        Groups.Add(key, gr)
                    End If
                    gr.List.Add(row)
                Next
                Dim i As Integer = 0
                Rows = New List(Of Object)(Groups.Count + Owner.BaseCount)
                For Each g As GroupRow In Groups.Values
                    g.Finalize(System.Math.Max(System.Threading.Interlocked.Increment(i),i - 1))
                    Rows.Add(g)
                    For Each row As Object In g.Rows
                        Rows.Add(row)
                    Next
                Next
            End Sub
        End Class
        Private m_info As GroupInfo
        Private ReadOnly Property Info() As GroupInfo
            Get
                If m_info Is Nothing Then
                    m_info = New GroupInfo(Me)
                    If bsource IsNot Nothing Then
                        SyncWithBSource()
                    End If
                End If
                Return m_info
            End Get
        End Property
        Private Sub OnGroupingChanged()
            RaiseEvent GroupingChanged(Me, EventArgs.Empty)
        End Sub
        Public Event GroupingChanged As EventHandler
        Public Sub ResetGroup()
            m_info = Nothing
        End Sub
        Private bsource As BindingSource
        Private Sub DisposeBindingSourceEvents()
            If bsource Is Nothing Then
                Exit Sub
            End If
            RemoveHandler bsource.CurrentChanged, AddressOf bsource_CurrentChanged
        End Sub
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            DisposeBindingSourceEvents()
            MyBase.Dispose(disposing)
        End Sub
        Protected Overloads Overrides Sub OnDataSourceChanged(ByVal e As EventArgs)
            ResetGroup()
            DisposeBindingSourceEvents()
            bsource = TryCast(DataSource, BindingSource)
            If bsource IsNot Nothing Then
                AddHandler bsource.CurrentChanged, AddressOf bsource_CurrentChanged
                If NeedSync Then
                    SyncWithBSource()
                End If
            End If
            MyBase.OnDataSourceChanged(e)
        End Sub
        Private suspendlistchange As Boolean
        Protected Overloads Overrides Sub OnListChanged(ByVal e As ListChangedEventArgs)
            If suspendlistchange Then
                Exit Sub
            End If
            Select Case e.ListChangedType
                Case ListChangedType.ItemChanged
                    If m_groupon IsNot Nothing AndAlso m_groupon.IsProperty(e.PropertyDescriptor) Then
                        ResetGroup()
                    End If
                    Exit Select
                Case ListChangedType.ItemAdded
                    If m_info IsNot Nothing Then
                        m_info.Rows.Add(List(e.NewIndex))
                    End If
                    Exit Select
                Case ListChangedType.ItemDeleted
                    ResetGroup()
                    Exit Select
                Case ListChangedType.Reset
                    ResetGroup()
                    Exit Select
                Case ListChangedType.PropertyDescriptorAdded, ListChangedType.PropertyDescriptorChanged, ListChangedType.PropertyDescriptorDeleted
                    props = Nothing
                    Exit Select
            End Select
            MyBase.OnListChanged(e)
        End Sub
        Public Overloads Overrides Function AddNew() As Object
            suspendlistchange = True
            Try
                Dim res = MyBase.AddNew()
                If m_info IsNot Nothing Then
                    m_info.Rows.Add(res)
                End If
                Return res
            Finally
                suspendlistchange = False
            End Try
        End Function
        Public Overloads Overrides Sub ApplySort(ByVal [property] As PropertyDescriptor, ByVal sort As ListSortDirection)
            If TypeOf [property] Is PropertyWrapper Then
                [property] = TryCast([property], PropertyWrapper).[Property]
            End If
            MyBase.ApplySort([property], sort)
        End Sub
        Public Overloads Overrides Sub ApplySort(ByVal sorts As ListSortDescriptionCollection)
            MyBase.ApplySort(sorts)
        End Sub
        Public Overloads Overrides Sub RemoveAt(ByVal index As Integer)
            If m_info Is Nothing OrElse m_groupon Is Nothing Then
                MyBase.RemoveAt(index)
            ElseIf Not IsGroupRow(index) Then
                Dim i = List.IndexOf(Me(index))
                suspendlistchange = True
                Try
                    m_info.Rows.RemoveAt(index)
                    List.RemoveAt(i)
                    MyBase.OnListChanged(New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
                Finally
                    suspendlistchange = False
                End Try
            End If
        End Sub
        Public Overloads Overrides Sub Remove(ByVal value As Object)
            If TypeOf value Is GroupRow Then
                Exit Sub
            End If
            Dim index As Integer = Me.IndexOf(value)
            If index <> -1 Then
                RemoveAt(index)
            End If
        End Sub
        Protected Overloads Overrides Sub OnCurrentChanged(ByVal e As EventArgs)
            MyBase.OnCurrentChanged(e)
            If NeedSync AndAlso Not (TypeOf Current Is GroupRow) Then
                bsource.Position = bsource.IndexOf(Current)
            End If
        End Sub
        Private Sub bsource_CurrentChanged(ByVal sender As Object, ByVal e As EventArgs)
            If NeedSync Then
                SyncWithBSource()
            End If
        End Sub
        Private ReadOnly Property NeedSync() As Boolean
            Get
                If bsource Is Nothing OrElse suspendlistchange Then
                    Return False
                End If
                If bsource.IsBindingSuspended Then
                    Return False
                End If
                Return Current <> bsource.Current
            End Get
        End Property
        Private Sub SyncWithBSource()
            Position = IndexOf(bsource.Current)
        End Sub
        Public Overloads Overrides Function IndexOf(ByVal value As Object) As Integer
            Return Info.Rows.IndexOf(value)
        End Function
        Public Class PropertyWrapper
            Inherits PropertyDescriptor
            Public ReadOnly [Property] As PropertyDescriptor
            Public ReadOnly Owner As GroupingSource
            Public Sub New(ByVal [Property] As PropertyDescriptor, ByVal Owner As GroupingSource)
                MyBase.New([Property])
                Me.[Property] = [Property]
                Me.Owner = Owner
            End Sub
            Public Overloads Overrides Function CanResetValue(ByVal component As Object) As Boolean
                Return [Property].CanResetValue(component)
            End Function
            Public Overloads Overrides ReadOnly Property ComponentType() As Type
                Get
                    Return [Property].ComponentType
                End Get
            End Property
            Public Overloads Overrides Function GetValue(ByVal component As Object) As Object
                If TypeOf component Is GroupRow Then
                    If Owner.groupon.IsProperty([Property]) Then
                        Return TryCast(component, GroupRow).Value
                    End If
                    Return Nothing
                End If
                Return [Property].GetValue(component)
            End Function
            Public Overloads Overrides ReadOnly Property IsReadOnly() As Boolean
                Get
                    Return [Property].IsReadOnly
                End Get
            End Property
            Public Overloads Overrides ReadOnly Property PropertyType() As Type
                Get
                    Return [Property].PropertyType
                End Get
            End Property
            Public Overloads Overrides Sub ResetValue(ByVal component As Object)
                [Property].ResetValue(component)
            End Sub
            Public Overloads Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
                [Property].SetValue(component, value)
            End Sub
            Public Overloads Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
                Return [Property].ShouldSerializeValue(component)
            End Function
        End Class
        Private props As PropertyDescriptorCollection
        Public Overloads Overrides Function GetItemProperties(ByVal listAccessors As PropertyDescriptor()) As PropertyDescriptorCollection
            If listAccessors Is Nothing Then
                If props Is Nothing Then
                    props = MyBase.GetItemProperties(Nothing)
                    Dim arr As PropertyDescriptor() = New PropertyDescriptor(props.Count - 1) {}
                    For i As Integer = 0 To props.Count - 1
                        arr(i) = New PropertyWrapper(props(i), Me)
                    Next
                    props = New PropertyDescriptorCollection(arr)
                End If
                Return props
            End If
            Return MyBase.GetItemProperties(listAccessors)
        End Function
        Public ReadOnly Property BaseCount() As Integer
            Get
                Return MyBase.Count
            End Get
        End Property
        Public Function GetBaseRow(ByVal Index As Integer) As Object
            Return List(Index)
        End Function
        Public Overloads Overrides ReadOnly Property Count() As Integer
            Get
                Return Info.TotalCount
            End Get
        End Property
        Public Overloads Overrides Default Property Item(ByVal index As Integer) As Object
            Get
                Return Info.Rows(index)
            End Get
            Set
                Info.Rows(index) = value
            End Set
        End Property
    End Class
    Public MustInherit Class GroupingInfo
        Public MustOverride Function GetGroupValue(ByVal Row As Object) As Object
        Public Overridable Function IsProperty(ByVal p As PropertyDescriptor) As Boolean
            Return False
        End Function
        Public Overridable Function IsProperty(ByVal Name As String) As Boolean
            Return Name = ToString()
        End Function
        Public Shared Widening Operator CType(ByVal p As PropertyDescriptor) As GroupingInfo
            Return New PropertyGrouper(p)
        End Operator
        Public Overridable Sub SetDisplayValues(ByVal e As GroupDisplayEventArgs)
            Dim o = e.Value
            e.DisplayValue = If(o Is Nothing, "<Null>", o.ToString())
        End Sub
    End Class
    Public Class PropertyGrouper
        Inherits GroupingInfo
        Public ReadOnly [Property] As PropertyDescriptor
        Public Sub New(ByVal [Property] As PropertyDescriptor)
            If [Property] Is Nothing Then
                Throw New ArgumentNullException()
            End If
            Me.[Property] = [Property]
        End Sub
        Public Overloads Overrides Function GetGroupValue(ByVal Row As Object) As Object
            Return [Property].GetValue(Row)
        End Function
        Public Overloads Overrides Function ToString() As String
            Return [Property].Name
        End Function
    End Class
    Public Delegate Function GroupingDelegate(ByVal Row As Object) As Object
    Public Class DelegateGrouper
        Inherits GroupingInfo
        Public ReadOnly Name As String
        Public ReadOnly GroupingDelegate As GroupingDelegate
        Public Sub New(ByVal [Delegate] As GroupingDelegate, ByVal Name As String)
            If [Delegate] Is Nothing Then
                Throw New ArgumentNullException()
            End If
            Me.Name = Name
            If Name Is Nothing Then
                Me.Name = [Delegate].ToString()
            End If
            Me.GroupingDelegate = [Delegate]
        End Sub
        Public Overloads Overrides Function ToString() As String
            Return Name
        End Function
        Public Overloads Overrides Function GetGroupValue(ByVal Row As Object) As Object
            Return GroupingDelegate(Row)
        End Function
    End Class
    Public Class StartLetterGrouper
        Inherits GroupingInfo
        Public ReadOnly Grouper As GroupingInfo
        Public ReadOnly Letters As Integer
        Public Sub New(ByVal Grouper As GroupingInfo)
            Me.New(Grouper, 1)
        End Sub
        Public Sub New(ByVal Grouper As GroupingInfo, ByVal Letters As Integer)
            If Grouper Is Nothing Then
                Throw New ArgumentNullException()
            End If
            Me.Grouper = Grouper
            Me.Letters = Letters
        End Sub
        Public Overloads Overrides Function ToString() As String
            Return Grouper.ToString()
        End Function
        Public Overloads Overrides Function IsProperty(ByVal p As PropertyDescriptor) As Boolean
            Return Grouper.IsProperty(p)
        End Function
        Public Overloads Overrides Function GetGroupValue(ByVal Row As Object) As Object
            Dim val = Grouper.GetGroupValue(Row)
            If val Is Nothing Then
                Return Nothing
            End If
            Dim s = val.ToString()
            If s.Length < Letters Then
                Return s
            End If
            Return s.Substring(0, Letters)
        End Function
    End Class
    Public Interface IDataGridViewGrouperOwner
        ReadOnly Property Grouper() As DataGridViewGrouper
    End Interface
    Public Interface IGrouper
        Sub SetGroupOn(ByVal col As String)
        Sub SetGroupOn(ByVal col As PropertyDescriptor)
        Sub RemoveGrouping()
Private _GroupOn As GroupingInfo
        Property GroupOn() As GroupingInfo
        Event PropertiesChanged As EventHandler
        Event GroupingChanged As EventHandler
        Function GetProperties() As IEnumerable(Of PropertyDescriptor)
    End Interface
End Namespace
Namespace Subro
    ''' <summary>
    ''' Comparer that tries to find the 'strongest' comparer for a type.
    ''' if the type implements a generic IComparable, that is used.
    ''' otherwise if it implements a normal IComparable, that is used.
    ''' If neither are implemented, the ToString versions are compared.
    ''' INullable structures are also supported.
    ''' This way, the DefaultComparer can compare any object types and can be used for sorting any source.
    ''' </summary>
    ''' <example>Array.Sort(YourArray,new GenericComparer());</example>
    Public Class GenericComparer
        Implements IComparer
        Public Sub New()
        End Sub
        Public Sub New(ByVal Type As Type)
            Me.Type = Type
        End Sub
        Private m_type As Type
        Public Property Type() As Type
            Get
                Return m_type
            End Get
            Set(ByVal value As Type)
                If value Is Nothing Then
                    Throw New ArgumentNullException()
                End If
                m_type = value
                comp = Nothing
            End Set
        End Property
        Private m_targettype As Type
        ''' <summary>
        ''' normally the same as the type, but can be set to a different type
        ''' </summary>
        Public Property TargetType() As Type
            Get
                If m_targettype Is Nothing Then
                    Return m_type
                End If
                Return m_targettype
            End Get
            Set(ByVal value As Type)
                If TargetType = value Then
                    Return
                End If
                m_targettype = value
                comp = Nothing
            End Set
        End Property
        
        
        Private comp As IComparer
        Private Function GetGenericComparer(ByVal From As Type, ByVal [To] As Type) As IComparer
            While [To] <> GetType(Object)
                If GetType(IComparable(Of )).MakeGenericType([To]).IsAssignableFrom(From) Then
                    Return DirectCast(Activator.CreateInstance(GetType(StrongCompare(Of , )).MakeGenericType(From, [To])), IComparer)
                End If
                [To] = [To].BaseType
            End While
            Return Nothing
        End Function
        Public Function GetComparer(ByVal From As Type, ByVal [To] As Type) As IComparer
            Dim gen = GetGenericComparer(From, [To])
            If gen IsNot Nothing Then
                Return gen
            ElseIf GetType(IComparable).IsAssignableFrom(m_type) Then
                Return DirectCast(Activator.CreateInstance(GetType(NonGenericCompare(Of )).MakeGenericType(m_type)), IComparer)
            ElseIf m_type.IsGenericType AndAlso GetType(Nullable(Of )) Is m_type.GetGenericTypeDefinition() Then
                Dim basetype = m_type.GetGenericArguments()(0)
                Return DirectCast(Activator.CreateInstance(GetType(NullableComparer(Of )).MakeGenericType(basetype), GetComparer(basetype, If([To] = From, basetype, [To]))), IComparer)
            End If
            Return New StringComparer()
        End Function
        Private Class NullableComparer(Of T As Structure)
            Implements IComparer
            Public ReadOnly BaseComparer As IComparer
            Public Sub New(ByVal BaseComparer As IComparer)
                Me.BaseComparer = BaseComparer
            End Sub
            Private Function getval(ByVal o As Object) As Object
                Return DirectCast(o, Nullable(Of T)).Value
            End Function
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return BaseComparer.Compare(getval(x), getval(y))
            End Function
        End Class
        Private Class StrongCompare(Of F As IComparable(Of T), T)
            Implements IComparer
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return DirectCast(x, F).CompareTo(DirectCast(y, T))
            End Function
        End Class
        Private Class NonGenericCompare(Of T As IComparable)
            Implements IComparer
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return DirectCast(x, T).CompareTo(y)
            End Function
        End Class
        Private Class StringComparer
            Implements IComparer
            Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
                Return String.Compare(x.ToString(), y.ToString())
            End Function
        End Class
        Public Property Descending() As Boolean
            Get
                Return factor < 0
            End Get
            Set(ByVal value As Boolean)
                factor = If(value, -1, 1)
            End Set
        End Property
        Private factor As Integer = 1
        Private Function compare(ByVal x As Object, ByVal y As Object) As Integer
            If x = y Then
                Return 0
            End If
            If x Is Nothing Then
                Return -1
            End If
            If y Is Nothing Then
                Return 1
            End If
            If m_type Is Nothing Then
                Type = x.[GetType]()
            End If
            If comp Is Nothing Then
                comp = GetComparer(m_type, TargetType)
            End If
            Return comp.Compare(x, y)
        End Function
        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
            Return factor * compare(x, y)
        End Function
    End Class
    
    Public Class GenericComparer(Of T)
        Inherits GenericComparer
        Implements IComparer(Of T)
        Public Sub New()
            MyBase.New(GetType(T))
        End Sub
        Public Function Compare(ByVal a As T, ByVal b As T) As Integer
            Return MyBase.Compare(a, b)
        End Function
    End Class
    
    
    Public Class PropertyDescriptorComparer
        Inherits GenericComparer
        Public ReadOnly Prop As PropertyDescriptor
        Public Sub New(ByVal Prop As PropertyDescriptor)
            Me.New(Prop, True)
        End Sub
        Public Sub New(ByVal Prop As PropertyDescriptor, ByVal Descending As Boolean)
            MyBase.New(Prop.PropertyType)
            Me.Prop = Prop
            Me.Descending = Descending
        End Sub
    End Class
End Namespace

Open in new window

0
 
LVL 33

Assisted Solution

by:Todd Gerbert
Todd Gerbert earned 200 total points
ID: 24737062
Looks to me like yield is a short-hand way of writing an enumerator, so that you can use For Each on a custom object.  That is, if an object contains an array of the days of the week (Dim s() As String = {"Monday", "Tuesday", etc}), the yield could be used to return each day in succession inside a For Each loop.  If you just change it to "return" then 1) It probably won't compile, and 2) it'll return "Monday" and then exit the sub, without ever returning the rest of the days.

I'm not intimately familiar with VB, so I'm not sure if there's an equivelant.  You might need to implement Current, MoveNext and Reset members of IEnumerator manually.
0
Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

 
LVL 59

Expert Comment

by:Kevin Cross
ID: 24737127
tgerbert, very good point.  That is what the yield appeared to do to me as well.  I was focused on showing how to get the code to initially convert, then figured can update from there.  I don't know of an equivalent to yield shorthand in VB.NET so will probably have to write it out manually as you stated.
0
 
LVL 33

Assisted Solution

by:Todd Gerbert
Todd Gerbert earned 200 total points
ID: 24737271
Here's a simple example of an object that supports a For Each loop, in VB and C# (the two are equivelant).

To use it in C#:
Week wk = new Week();
foreach(string day in wk)
    MessageBox.Show(day);

To use in VB:
Dim wk As New Week()
For Each day As String In wk
    MessageBox.Show(day)
Next
C#:
=======================================
using System;
using System.Collections;
using System.Text;
 
namespace CSharpWinFormsApp
{
	public class Week: IEnumerable
	{
		private string[] daysOfWeek = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
 
		public IEnumerator GetEnumerator()
		{
			for (int i = 0; i < daysOfWeek.Length; i++)
				yield return daysOfWeek[i];
		}
	}
}
 
 
VB:
=============================================
Imports System.Collections
 
Public Class Week
	Implements IEnumerable
 
 
	Shared daysOfWeek() As String = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
 
	Private Class WeekEnumerator
		Implements IEnumerator
		Private i As Integer = -1
		Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
			Get
				Return daysOfWeek(i)
			End Get
		End Property
 
		Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
			If i = 6 Then
				Return False
			Else
				i = i + 1
				Return True
			End If
 
		End Function
 
		Public Sub Reset() Implements System.Collections.IEnumerator.Reset
			i = 0
		End Sub
	End Class
 
	Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
		Return New WeekEnumerator()
	End Function
End Class

Open in new window

0
 
LVL 5

Expert Comment

by:vivekpv10
ID: 24743407
0
 
LVL 83

Assisted Solution

by:CodeCruiser
CodeCruiser earned 100 total points
ID: 24761696
Here is a sample showing how to implement ienumerable in vb.net as it does not support iterators.

http://support.microsoft.com/kb/322025
0

Featured Post

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question