Uitbreiden van een Winform DataGrid
Wil je data kunnen bewerken in een DataGrid, dan kom je de beperkingen van een DataGrid tegen. Er zijn maar twee typen kolommen: de TextBox kolom en de CheckBox kolom. Ook kun je de data in een DataGrid niet standaard printen en de kolommen verbergen of verplaatsen. Een DataGrid kan wel op deze punten uitgebreid worden, maar de vraag is hoe dit te realiseren? Er zijn veel varianten voorhanden, maar deze zijn niet flexibel. De componenten die je kunt kopen zijn niet afgeleid van een DataGrid control, waardoor ze vaak niet de flexibiliteit bieden die een DataGrid je kan bieden.
Uitbreiden van de kolomtypen van een DataGrid

Fig. 1: Uitgebreide DataGrid met diverse kolomtypen
De eerste uitbreiding die wordt toegelicht, is het maken van een eigen type kolom. Een kolom in een DataGrid bestaat uit een kolom cellen met specifieke kolomeigenschappen. Dit kunnen uiterlijke kenmerken zijn zoals het datatype, opmaak en formaat, maar ook het bewerken van de data via een control kan per kolom verschillen. Een eigen DataGrid-kolom wordt afgeleid van de class DataGridColumnStyle. Deze DataGridColumnStyle kan aan een DataGridTableStyle toegevoegd worden. Een DataGridTableStyle bevat naast de collectie van kolommen ook opmaakgegevens voor de totale DataGrid. De DataGridTableStyle wordt weer toegepast op een DataGrid voor een specifieke DataMember. Een DataMember wordt alleen gebruikt bij het koppelen van een DataSet. De DataTable-naam is dan de DataMember en de DataSet is de DataSource.
De DataGridColumnStyle-class is een abstracte class, waar de diverse benodigde methoden via een override geïmplementeerd kunnen worden. Er zijn basisfuncties voor het bewerken van data in een kolomcel gedefinieerd, zoals “Edit”, “Abort” en “Commit”. In de Edit-methode wordt een editable control in de cel geplaatst en zichtbaar gemaakt. In de Abort-functie wordt het editen gestaakt en wordt de editable control verborgen. In de Commit-functie, wordt de actuele waarde uit het control opgehaald en in de DataRowColumn gezet. Daarna wordt de control verborgen en het editen gestaakt.
Wanneer een kolom niet editable hoeft te zijn, dien je deze drie functies nog wel te implementeren. In dat geval is de body van deze functies echter leeg. Aan de functie Commit wordt dan de standaardreturn waarde true toegekend.
override protected void Edit(
CurrencyManager source, int rowNum, Rectangle bounds,
bool readOnly, string instantText, bool cellIsVisible)
{
// Get the current value for the combobox
if ( ! this.startedEditing )
{
this.setControlValue(
this.GetColumnValueAtRow( source, rowNum ) );
// show the edit control in the position
// of the current cell
this.control.Bounds = bounds;
this.control.Show();
this.control.Focus();
// flag that the control is prepared for editing
// the current cell
this.prepared = true;
}
}
override protected void Abort( int rowNum )
{
// reset the column state
this.startedEditing = false;
this.prepared = false;
// hide the hosted control
this.control.Hide();
}
override protected bool Commit(
CurrencyManager source, int rowNum )
{
bool result = true;
if ( this.prepared )
{
if ( this.startedEditing )
{
// retrieve current cell value from the control
object value;
result = this.getControlValue( out value );
// and store it in the data source
if ( result ) this.SetColumnValueAtRow(
source, rowNum, value );
// reset column status
this.startedEditing = false;
}
this.prepared = false;
this.control.Hide();
}
return result;
}
Listing 1: De basisfuncties voor een editable DataGridColumnType
De volgende stap in het maken van een eigen kolom is het bepalen van de grootte van de cel. Hiervoor zijn de volgende functies beschikbaar:
| Functie |
Omschrijving |
| GetMinimumHeight |
Minimale hoogte kolom |
| GetPreferredHeight |
Standaard hoogte kolom |
| GetPreferredSize |
Standaard grootte cel |
De kolomhoogte wordt mede bepaald door de aan het DataGridTableStyle toegekende kolomtypen. Het kolomtype met de grootste standaardhoogte bepaalt de hoogte van de DataGridRow.
Soms heeft een DataGrid-kolom gebeurtenissen nodig om te kunnen functioneren
Soms heeft een DataGrid-kolom gebeurtenissen nodig om te kunnen functioneren. Neem bijvoorbeeld een button-kolom. Gebeurtenissen worden verwezenlijkt door de implementatie van events. De DataGridColumnType class beschikt zelf niet altijd over de benodigde gebeurtenissen, maar de DataGrid heeft deze voorziening wel. Een DataGrid gebeurtenis is bijvoorbeeld het bewegen en klikken van de muis. Deze gebeurtenissen kunnen daarom pas in een DataGridColumnType gebruikt worden als de kolom daadwerkelijk aan een DataGrid wordt toegekend. Indien dit het geval is wordt de SetDataGridInColumn-functie aangeroepen. Deze functie kan ook gebruikt worden om te onthouden aan welk DataGrid de DataGridTableStyle gekoppeld is en of de DataGridTableStyle aan een DataGrid gekoppeld is.
protected override void SetDataGridInColumn(
DataGrid value )
{
base.SetDataGridInColumn( value );
AddHandlers( value );
}
Listing 2: Methode voor het opvangen van de koppeling van een DataGrid aan een DataGridColumnstyle
Het belangrijkste onderdeel van een GridColumnStyle is de Paint-methode
Het belangrijkste onderdeel van een GridColumnStyle is de Paint-methode. De Paint-methode tekent de cel van een DataGrid. Deze methode komt in vier smaken, en degene met een parameter meer wordt aangeroepen door de Paint-methode met een parameter minder. De Paint-methode met de meeste parameters bevat de feitelijke implementatie voor het tekenen van de cel.
protected override void Paint(
Graphics g, Rectangle bounds, CurrencyManager source,
int rowNum, Brush backBrush, Brush foreBrush,
bool alignToRight )
{
DataGrid dg = GridControl;
object value = this.GetColumnValueAtRow(
source, rowNum );
string text= FormatCellValue(value);
RectangleF borderRectangle = bounds;
StringFormat formatStr = FormatText();
Font tFont = this.DataGridTableStyle.DataGrid.Font;
CellPaintEvent(
rowNum,ref value,bounds,ref backBrush,
ref foreBrush,ref tFont );
g.FillRectangle( backBrush, bounds );
bounds.Offset(0, _intBorderWidth);
bounds.Height -= _intBorderWidth;
if (rowNum==_rowNum && GetKeyState(VK_SPACE) < 0)
{
_buttonState=ButtonState.Pushed;
ControlPaint.DrawButton(
g, bounds, ButtonState.Pushed);
OnClick(this,EventArgs.Empty);
this.Abort(_rowNum);
_buttonState=ButtonState.Normal;
}
if (dg.CurrentCell.ColumnNumber==Column &&
Control.MouseButtons == MouseButtons.Left &&
rowNum==_rowNum)
{
ControlPaint.DrawButton(
g, bounds, ButtonState.Pushed);
}
else
ControlPaint.DrawButton(
g, bounds, ButtonState.Normal);
SizeF sizef = g.MeasureString(
text, this.DataGridTableStyle.DataGrid.Font,
bounds.Width, formatStr);
borderRectangle.Y += Math.Abs(
(borderRectangle.Height - sizef.Height) / 2);
if (rowNum==_rowNum &&
dg.CurrentCell.ColumnNumber==Column )
{
Rectangle focusbounds = bounds;
focusbounds.Width = focusbounds.Width - 4;
focusbounds.X = focusbounds.X + 2;
focusbounds.Y =
Convert.ToInt32(borderRectangle.Y) - 2;
focusbounds.Height =
Convert.ToInt32(
this.DataGridTableStyle.DataGrid.Font.Size) + 8;
ControlPaint.DrawFocusRectangle(
g, focusbounds, Color.Blue, Color.Gray);
}
g.DrawString(
text, tFont, foreBrush, borderRectangle, formatStr);
formatStr.Dispose();
}
Listing 3: Het paint event van een ButtonGridColumnStyle
Nu de basiskennis aanwezig is voor het maken van een eigen kolomtype, is het simpel om een stapje verder te gaan. Veel functionaliteit die je gebruikt, geldt voor elk type kolom. Dus is het een goed idee om een abstracte afgeleide klasse van DataGridColumnStyle te ontwikkelen, waarbij het editen en de grootte van de kolom afgehandeld kunnen worden. Daarnaast kun je een aantal hulpfuncties opnemen zoals het format voor de tekst. Het format van de tekst bepaalt de plaats waar de tekst binnen een cel komt te staan. Deze functie wordt bijna altijd in de Paint methode aangeroepen.
protected StringFormat FormatText()
{
StringFormat formatStr = new StringFormat();
if (_bRightToLeft)
{
formatStr.FormatFlags =
StringFormatFlags.DirectionRightToLeft;
}
switch (this.Alignment)
{
case HorizontalAlignment.Left:
{
formatStr.Alignment = StringAlignment.Near;
break;
}
case HorizontalAlignment.Right:
{
formatStr.Alignment = StringAlignment.Far;
break;
}
case HorizontalAlignment.Center:
{
formatStr.Alignment = StringAlignment.Center;
break;
}
}
formatStr.FormatFlags |= StringFormatFlags.NoWrap;
return formatStr;
}
Listing 4: Het formatteren van tekst t.b.v. de DrawString-functie in het Paint event
De control voor het editen van data kan iedere willekeurige control zijn. Microsoft heeft een textbox- en een checkbox-control geïmplementeerd, maar daar hoeft het niet bij te blijven. Elke control heeft zijn eigen afhandelingmechanisme om toetsaanslagen te verwerken. Echter, deze toetsen kunnen in strijd zijn met de toetsen die gebruikt worden voor de werking van de DataGrid. Het gevolg van een toets die zowel door een control als door een DataGrid gebruikt wordt, is, dat deze toets twee keer wordt uitgevoerd. Daarom dient er een afgeleide van een bestaande control worden gemaakt, waarbij het toetsverwerkingsmechanisme, waar nodig, wordt herschreven.
In de volgende methoden kunnen toetsen herprogrammeerd worden om de afhandeling van toetsaanslagen te corrigeren.
| Methode |
Omschrijving |
| ProcessKeyMessage |
Blokkeren van de toetsfunctie als de control van de DataGrid in edit-mode is. Voorkomt het dubbel uitvoeren van een toets. |
| ProcessCmdKey |
Herprogrammeren van (functie)toetsen van de control, zodat dit beter aansluit op de werking van de DataGrid |
Een DateTimePicker control heeft de mogelijkheid om met de pijltjestoetsen tussen dag, maand en jaar te switchen. De DataGrid gebruikt deze toeten echter om tussen cellen te navigeren. Als een DateTimePicker control in een kolom gehost wordt, leidt het uitvoeren van een pijltoets tot het onverwachte resultaat van 2 pijltjes toetsen. In de ProcessKeyMessage kan deze situatie gecorrigeerd worden door het processen van de toets uit te schakelen. Dit betekent overigens niet dat de pijltjestoets binnen het control niet meer gebruikt wordt.
protected override bool ProcessKeyMessage(ref Message m)
{
if (this.Visible)
switch ((Keys) m.WParam.ToInt32())
{
case Keys.PageDown:
case Keys.PageUp:
return false;
case Keys.Home:
case Keys.End:
case Keys.Tab:
return false;
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
return false;
}
return base.ProcessKeyMessage (ref m);
}
Listing 5: Het afvangen van toetsen, die zowel voor de DataGrid als door een DataGridControl gebruikt worden.
Tenslotte dient de afgeleide control aan te geven of er in de DataGrid een edit-bewerking plaatsvindt. Het icon in de RowHeader van de DataGrid verandert daardoor van een pijl in een potlood. Het is daarom aan te bevelen de container van de control - dat is de DataGrid - in de constructor van de control mee te nemen. Daarmee kan de control invloed uitoefenen op de DataGrid waar dit nodig is zoals in de EditingNotificationService van de DataGrid-control.
override protected void OnTextChanged( EventArgs e )
{
((IDataGridColumnStyleEditingNotificationService)
this.host).ColumnStartedEditing( this );
base.OnTextChanged( e );
}
Listing 6: Het DataGrid op de hoogte brengen dat je een waarde aan het editen bent
Uitbreiden van het DataGrid control zelf
Een van de problemen die een DataGrid met zich meebrengt, is het bepalen van de breedte van de kolommen. Standaard wordt dit niet lekker afgehandeld. Het is daarom aan te raden om hier een standaard functie voor te schrijven. Je kunt hier zelfs een methode voor schrijven die ervoor zorgt dat de laatste kolom de lege achtergrond van een DataGrid opvult. Niet alle kolommen hoeven dezelfde breedte te hebben. Daarom is het verstandig om afhankelijk van het type de breedte van de kolom te bepalen.
public void DataGridSizeColumns()
{
for(int i = 0; i < _dt.Columns.Count; i++)
{
DataColumn dataColumn = _dt.Columns[i];
if (dataColumn.ColumnMapping != MappingType.Hidden)
{
int maxSize = this.DetermineColumnWidth(dataColumn) + 2;
_dg.TableStyles[_dt.TableName].
GridColumnStyles[dataColumn.ColumnName].Width = maxSize;
}
}
}
Listing 7: Het bepalen van de kolombreedte van de diverse DataGridColumTypes op basis van een DataColumn.
Per type kolom wordt de breedte bepaald. Datakolommen die een MappingType met de waarde Hidden hebben, worden niet getoond en hebben daarom een breedte van 0. Kolombreedtes worden toegekend aan een DataGridColumnStyle.
Standaard wordt het bepalen van de breedte van een kolom niet lekker afgehandeld
Per kolom kan het datatype en de DataGridColumnStyle verschillen. Soms is het nodig om in een DataTable extra informatie over de formattering van de kolom aan te geven, zoals bij een Currency-veld. In .Net wordt dit type geconverteerd naar het datatype decimal. Currency-data wordt echter afgebeeld met een geldsymbool zoals het €-teken. In het berekenen van de breedte van de kolom wordt dit feit meegenomen. Deze kolombreedte kan aan een maximum breedte gelimiteerd worden. Dit kan de Caption van de ColumnHeader zijn.
case "Decimal":
if (dataColumn.ExtendedProperties.ContainsKey(
"Currency"))
size = g.MeasureString(
CurrentCulture.NumberFormat.CurrencySymbol.
ToString() + "99.999.999.999,99",_dg.Font);
else
size =
g.MeasureString("99.999.999.999,99",_dg.Font);
if(size.Width > maxSize)
maxSize = (int)size.Width;
break;
Listing 8: Het bepalen van de kolombreedte
Een andere handige functie is om op basis van het DataColumnType het juiste kolomtype van een DataGrid te gebruiken. Het type hangt af van het datatype van de aangeboden datakolom van een datatabel. Het kan evengoed geïmplementeerd worden voor een array, mits er een iterator geïmplementeerd wordt voor een datakolom-definitie.
Het uitvragen van een datatype kan als volgt gedaan worden:
DataTable.Columns[AColumnNameOrNumber].DataType.Name
Als het datatype bekend is, kan de juiste DataGridColumnType geselecteerd worden en de eigenschappen van deze kolom kunnen gezet worden. De meeste eigenschappen worden overgenomen van een DataColumn. De kolom wordt uiteindelijk toegevoegd aan een DataGridTableStyle. Deze DataGridTableStyle wordt op zijn beurt weer toegekend aan een DataGrid. Zonder zo’n DataGridTableStyle wordt data in een DataGrid volgens de standaarddefinities getoond.
Er kunnen diverse DataColumn attributen in een DataGridColumnStyle overgenomen worden waaronder een Caption. Standaard doet een DataGridColumnStyle niks met dit Caption-attribuut van een DataColumn.
case "Boolean":
case "Bool":
DataGridBooleanColumn dgbc =
new DataGridBooleanColumn();
dgbc.MappingName = _dt.Columns[intI].ColumnName;
dgbc.AllowNull = _dt.Columns[intI].AllowDBNull;
dgbc.Alignment= HorizontalAlignment.Center;
dgbc.HeaderText = _dt.Columns[intI].Caption;
dgbc.ReadOnly = _dt.Columns[intI].ReadOnly;
dgbc.NullText ="";
_dgts.GridColumnStyles.Add(dgbc);
dgbc.Width =
this.DetermineColumnWidth(_dt.Columns[intI]);
bDone=true;
break;
case "Byte[]":
if (_AutomaticImageColumn==true)
{
DataGridImageColumn dgic =
new DataGridImageColumn();
dgic.MappingName = _dt.Columns[intI].ColumnName;
dgic.HeaderText = _dt.Columns[intI].Caption;
dgic.ReadOnly = true;
dgic.ImageSizeType =
DataGridImageColumn.SizeType.Nomal;
dgic.ImageSourceType =
DataGridImageColumn.SourceType.Database;
_dgts.GridColumnStyles.Add(dgic);
bDone=true;
}
break;
Listing 10: Op basis van het DataType van een DataColumn wordt het juiste kolomtype gebruikt.
Soms kan het handig zijn om in het DataGrid geen huidige cel te hebben. Je kunt dit niet doen via een standaardmethode, maar de SendMessage-methode van een DataGrid biedt uitkomst.
public void SetNoCurrentCell()
{
//click on top left corner of the grid
SendMessage(this.Handle, WM_LBUTTONDOWN, 0, 0);
SendMessage(this.Handle, WM_LBUTTONUP, 0, 0);
}
Listing 11: Het deselecteren van een selectie in het DataGrid
Het opvragen van een rij in een DataGrid is een lastig karwei. Aan het nummer van de rij valt dit niet op te maken. Bij een sortering in het DataGrid lopen de nummering van de DataGrid-rij en die van de DataSource-rij niet synchroon. De rij kan alleen opgevraagd worden via de iterator van de BindingContext. Indien als DataSource een DataTable wordt gebruikt, wordt er een DataRowView-object gebruikt. Deze DataRowView kan de eigenlijke DataRow retourneren, die de uiteindelijke huidige rij teruggeeft.
[Description("Retrieves the currentrow")]
[Browsable(false)]
public DataRow CurrentRow
{
get
{
try
{
DataRowView r =
(DataRowView)this.BindingContext[
DataSource,DataMember].Current;
return r.Row;
}
catch
{
return null;
}
}
set {}
}
Listing 12: Het opvragen van de huidige DataRow in een DataGrid.
Het zetten van de rijhoogte is geen eigenschap van een DataGrid. De breedte mag dan gezet worden, op de rijhoogte kan geen invloed worden uitgeoefend. Er moet zelfs reflectie op de assembly toegepast worden om de rijhoogte te kunnen zetten. De reden is dat de RowHeight eigenschap van een DataGrid niet als public gedefinieerd is.
public void SetGridRowHeight(int nRow, int cy)
{
MethodInfo mi = this.GetType().BaseType.GetMethod(
"get_DataGridRows",
BindingFlags.FlattenHierarchy |
BindingFlags.IgnoreCase |
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Static);
Array dgra = (Array)mi.Invoke(this,null);
ArrayList rowObjects = new ArrayList();
foreach (object dgrr in dgra)
{
if (dgrr.ToString().EndsWith(
"DataGridRelationshipRow")==true)
rowObjects.Add(dgrr);
}
PropertyInfo pi =
rowObjects[nRow].GetType().GetProperty("Height");
pi.SetValue(rowObjects[nRow], cy, null);
}
Listing 13: Het zetten van de rijhoogte in een DataGrid
Als je een DataGridTableStyle gebruikt in een DataGrid, dan zal opvallen dat een aantal opmaakeigenschappen van de DataGrid niet meer werken. Deze opmaak wordt namelijk overgenomen door de DataGridTableStyle. Om de eigenschappen van de DataGrid te laten werken moeten diverse eigenschappen overgezet worden.
private void FormatTableStyle()
{
DataGridTableStyle style =
this.CurrentDatagridTableStyle;
if (ForeColor != Color.Empty)
style.ForeColor = ForeColor;
style.BackColor = BackColor;
style.AlternatingBackColor = AlternatingBackColor;
style.AllowSorting = AllowSorting;
style.ColumnHeadersVisible = ColumnHeadersVisible;
style.GridLineColor = GridLineColor;
style.GridLineStyle = GridLineStyle;
style.HeaderBackColor = HeaderBackColor;
style.HeaderFont = HeaderFont;
style.HeaderForeColor = HeaderForeColor;
style.LinkColor = LinkColor;
style.ReadOnly = ReadOnly;
style.RowHeadersVisible = RowHeadersVisible;
style.RowHeaderWidth = RowHeaderWidth;
}
Listing 14: Het corrigeren van de opmaak van een DataGrid na gebruik van een DataGridTableStyle.
Printen van een DataGrid
Er zijn diverse functies geschreven om een DataGrid te printen, maar geen van de functies voldeed aan mijn verwachtingen. Bovendien bleken ze na het uitbreiden van het DataGrid met eigen kolomtypes niet meer te werken. Tijd dus om zelf een functie te schrijven.
Het printen wordt aangeroepen via een context-menu op de titelbar van een DataGrid. Eerst wordt een DataGridPrinter gestart, waaraan de control en de data van de control worden meegegeven.
private void PrintPreview(object sender, EventArgs e)
{
DataGridPrinter p =
new DataGridPrinter(this,this.DataSource);
p.PrintPreview();
}
private void Print(object sender, EventArgs e)
{
DataGridPrinter p =
new DataGridPrinter(this,this.DataSource);
p.Print();
}
Listing 15: Aanroepen van de DataGridPrinter Class
In de constructor van de DataGridPrinter worden waarden van parameters overgenomen en wordt er een event gekoppeld aan een PrintDocument-variabele. De PrintPage-methode doet niks anders dan het tekenen van rijen. De pagina begint met het tekenen van de Caption, dan een RowHeader en vervolgens de datarijen. Bij het printen van een pagina moet je zelf bijhouden waar je bent. Als een pagina vol is en er is nog wat te tekenen, dan wordt de eigenschap PrintPageEventArgs.HasMorePages op true gezet.
public DataGridPrinter(Grid grid, object source)
{
dataGridPrintDocument = new PrintDocument();
Host = grid;
if (source !=null)
DataSource=source;
dataGridPrintDocument.PrintPage +=
new PrintPageEventHandler(PrintPage);
}
public void PrintPage(
object sender, PrintPageEventArgs e)
{
DrawRows(ref e);
}
public bool DrawRows(ref PrintPageEventArgs e)
{
try
{
//Caption on each page
if (grid.CaptionVisible == true)
DrawCaption(e.Graphics);
//Teken de DataColumn Caption
int HeaderHeight= DrawRowHeader(e.Graphics);
int pageTop=TopMargin()+captionHeight+HeaderHeight;
//Haal de juiste DataGridStyle op
DataGridTableStyle t;
if (grid.DataMember == "")
t=grid.TableStyles[0];
else
//Er is een datamember geslecteerd
t=grid.TableStyles[grid.DataMember];
// draw the rows of the table
while (_CurRow < dataGridTable.Rows.Count)
{
int RowHeight = grid.GetGridRowHeight(_CurRow);
//Bereken de DataGridrijhoogte
if (pageTop+RowHeight>
e.PageBounds.Height-this.pageMarginTop)
{
e.HasMorePages=true;
return true;
}
//Teken rij
int printed=DrawRow(e.Graphics,t,_CurRow,pageTop);
//overgebleven witte ruimte op de pagina
pageTop+= printed;
_rowTop+= printed;
rowCount++; //Volgende paginarij
_CurRow++; //Volgende DataGridRij
}
return false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
return false;
}
}
Listing 16: De printfunctionaliteit voor het afdrukken van een DataGrid
Het tekenen van een rij kost nog best veel werk. Een rij bestaat uit een set cellen. Deze cellen worden op een bepaalde hoogte van de pagina getekend. Voordurend moet dan ook de offset van een rij worden bijgehouden. De grootte van de totale rij dient eerst berekend te worden. Vervolgens dient per kolomtype de breedte te worden gemeten. Aangezien een DataGridColumType een eigenschap breedte kent wordt deze overgenomen. Dit betekent wel dat bij overschrijding van de som van kolombreedten van een pagina, een aantal kolommen niet getekend kan worden. Dat kan deels opgelost worden door de pagina op landscape-formaat af te drukken.
Het tekenen van een rij kost nog best veel werk
De truc van het tekenen van de kolom zelf zit in het feit dat de DataGridColumType een methode Paint heeft. Normaliter wordt het tekenen door de DataGrid zelf afgehandeld, maar in dit geval printen we niet de control maar de inhoud van de control. Het aanroepen van deze Paint methode is niet zo makkelijk als het lijkt. Feit is dat de Paint-methode een protected member is. We kunnen deze methode dus alleen in dezelfde assembly aanroepen. En feit is ook dat de Paint-methode van de specifieke DataGridColumnstyle uitgevoerd dient te worden. Er is een slimme en eenvoudige oplossing voorhanden: de methode wordt aangeroepen door gebruik te maken van reflectie.
MethodInfo mi2 =
t.GridColumnStyles[Column].GetType().
BaseType.GetMethod(
"Paint",b, null, CallingConventions.Standard,
new Type[]
{typeof(Graphics),typeof(Rectangle),
typeof(CurrencyManager),typeof(int)},
null);
mi2.Invoke(t.GridColumnStyles[j],
new object[]{g,bounds,source,row}) ;
Listing 17: Het aanroepen van de Paint-methode gebeurt met reflectie
Het daadwerkelijk printen wordt via een aparte methode aangeroepen. Deze methode roept de printdialoog in het leven, stelt het te printen document in en toont de dialoog in de ‘owner’ van de applicatie.
public void Print()
{
_CurRow=0;
PrintDialog pdlg = new PrintDialog();
pdlg.Document = dataGridPrintDocument;
pdlg.ShowDialog(this);
}
Listing 18: Het activeren van de printdialoog
En verder
Al deze bevindingen en nog vele andere uitbreidingen heb ik een C#-controlproject samengevat. Deze source kun je vinden op www.sdn.nl.