Solved

Convert C# to VB.net: yield instruction

Posted on 2009-06-29
8
1,883 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
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
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

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Suggested Solutions

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

747 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now