Uitbreiden van een Winform Datagrid

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.

Commentaar van anderen:
bags op 9-7-2010 om 11:35
Whatever you choose a few famous Loewe handbags reproductions, Louis Louis Vuitton handbags wallet and reproduction. You can choose a recently published Marc Jacob handbags reproductions, replica Juicy Corture handbags style or being introduced several months or years ago. Manyreplica Louis Vuitton handbags never lost replica Marc Jacob handbagstheir appeal to a replica Chanel to travel around the world, the designer's reproductions, and strive to make thesereplica Hermes hobbies can give you YSL replica. The release date are clearly marked for each replica Piaget, handbags, as well as a detailed description about the copy of Mulberry replica handbags, what it is made of. Panerai watches attached lets you see what you can see Tudor watches what you will get.Alain Silberstein watches -- sometimes hard to distinguish - not now! Fashion and choose Paul Smith replica handbags -- not exactly sure Replica handbags what Versace replica meet
replica handbags op 16-7-2010 om 9:54
Actually, jimmy choo replicathere are too many brands like Designer HandbagsLouis Vuittion, Balenciaga, Gucci, versace replica handbags, Miu Miu, Fendi, Versace,swiss watches Kooba and so on, how designer watchescould you find the right style forGlashutte watches you? My suggestion is to considerreplica handbags quality over quantity and burberry handbagskeep in mind that a high qualityChloe handbags designer bag will go with youPatek Philippe for sale for years as a bottega veneta handbagsfaithful friendRado for sale.As mentioned above, choosing the rightRolex for sale designer handbags is something Glashutte for saleof art, which reflects your sense of stylejimmy choo replica handbags, taste, and even more, it’s signal of your level of swiss watchessophistication. Breitling replicaPretty girls will always have differentLouis Vuitton replica handbags designer handbags to fit theirChloe handbags needs for differentPatek Philippe for sale occasions. They believe high authentic fendi handbagsquality designer handbagsFranck Muller for sale are good investments.
fasfaf fasfafaf op 28-7-2010 om 4:54
Tiffany jewelry manufacturers directory,cheap jewelry - lots of registered importers and exporters. Cheap tiffany jewelry manufacturers, Tiffany & co jewelry suppliers, tiffany jewelry wholesales, Tiffany Watches,exporters, sellers, traders and gold tiffany jewelry Distributors from China and around the world at www.goldtiffanyjewelry.com Tiffany jewelry exporters, Tiffany Accessories, Tiffany Bangles, Tiffany Bracelet, Discont Tiffany Bracelet, Discount Tiffany Cufflinks, Fashion Tiffany Earring, Tiffany Necklaces , Discount Tiffany Necklaces, Tiffany Rings, Tiffany Watches
ghdivstyler op 7-8-2010 om 9:06
http://www.google.com.hk/search?q=%22Geef+feedback%22+site:www.sdn.nl&hl=zh-CN&newwindow=1&safe=strict&ei=sARdTMy5BYu4uQOQ8pyaDA&start=240&sa=N
fake watch op 9-8-2010 om 9:56
Hello,FIFA World Cup South Africa we offer Omega watches With Best Quality,at watch shop, you can buy fake watch Our gaqoniag fake rolex watches are unique in the trade for the semblance to the real thing. buy fake watchesguaranteed! Replica Watches Store provides the best fake breitling watches to our customers at the lowest prices. This best fake chanel watches is known for Men and Ladies, as replica tag heuer Formula,Monaco, fake rolex are the best solution if you cannot afford to purchase so expensive original Rolex and rolex fake watches Now please come in!Such a surprise wait for you. gucci watches gucci ladies watches Chanel Watches gucci mens watches Fendi Watches The 18th Golden Rooster Hundred Flowers Film Festival rolex Submariner appeared replica watches in rolex day date replica an replica rolex watches almost record low number of stars rolex air king in the replica tag heuer embarrassing bmw watches situation rolex milgauss in the evening of rolex oyster watch the replica breitling watches 17th over. Film Festival has fake breitling watches come replica watches to an end, untreated illnesses and new diseases have added. In selecting the host city, the mens omega watches film rolex watches festival omega watches sale organized replica breitling by fake cartier watch the replica breitling watches meaning of star attitude towards domestic replica corum watches film replica tag heuer festival and chopard watches replica many other d&g women watches aspects of the problems left corum watches over fake rolex watches need to think deeply of domestic film. blancpain watches esprit watches raymond weil watches sinn watches
ChristianLouboutin op 17-8-2010 om 4:11
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Wedding Shoes, Louboutin Shoes, Christian Louboutin Discount copies of the mirror, so the angel of the practical activities of customers who activities. Christian Louboutin Evening, Manolo Blahnik Shoes, Christian, Louboutin, Christian Louboutin Sale, Louboutin Sale, Cheap Christian Louboutin Can you from your shoes, you give yourself into chargeless travel and bear your lifetime. Christian Louboutin Boots, Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Wedges, Christian Louboutin Sandals Do you agree to accept and hear the cases angled heel shoes forward or abstract, tear, buttons, and destruction, and the ability to reduce greenhouse gas emissions, it is exactly like daydreaming and shoes. Yves Saint Laurent Shoes, Christian Louboutin Boots, Manolo Blahnik Shoes, Yves Saint Laurent Boots, Miu Miu Shoes, Christian Dior Shoes this is an extra brand and shoes. However, you will never accept the amorous Louboutin christians through brand. Christian Louboutin Flats, Christian, Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress You can visit in your configuration of the life of their shoes were forgiven a brace, air brief you accept defeat them later anniversary, they are careful bag dust, shoe box that you have to go.
NHN op 24-8-2010 om 4:28
LRH20100824

There are many men to choose Cheap Lacoste Shoes for his fame and service which is famous with the world people.Countries' leaders like to wear Locaste Shoes.On 4th of August, Western media quoted the Lebanese and Arab Mens Lacoste Shoes TV stations reported that Iran - President Mahmoud Ahmadinejad's team that day was in Iran bomb attacks, several people were injured, Mahmoud Ahmadinejad, I survived.The leader wears the style shoe of 2010 Lacoste Shoes.

nike air max 90 op 24-8-2010 om 10:01
chinese antique shop replica supplier nike shox r4 puma shoes
Air Max Shoes op 24-8-2010 om 11:07
yx20100824

A journey of a thousand miles begins with a single step. Every dreamer, want a pair of suitable Air Max Shoes, a perfect fit with yourself to any where you want to go. Belong to you, or comfortable, or personality, or alternative, or enchanting, or recreational or publicity. Different Nike Free Trainer 7.0.3 in different mood, this summer, which one would you choose?
Mickey's tattoo, wear with pink Cheap Nike Air Max 90, my mood I advocate, this summer i am the most lovely girl!
Wear the most popular of Nike Air Max LTD and walk in the summer on the street. The girl Love shopping is the beautiful scenery.
Shuttlecock fly in the foot, mood also with the fly, holiday was coming, explicitly enjoy relaxing vacation! With Air Max LeBron VII to travel!

Nike Free shoes always being popular in youth, older, every type consumers, her Nike Air Max 95, Nike Air Max 360, etc, are stylish, high quality, durable, hot sale online, www.airmaxsite.com, every style, cute, romantic, cool, personality, practical, fashionable etc, you can find your style at here.

Chanel Handbags op 24-8-2010 om 11:07
zn2010-08-24 weclome to the designer handbags outlet.our shop is sell all kind bags ,all of them are famous brand,they are Cheap Coach Handbags, burberry bags, Louis Vuitton bags , chole bags, Dolce Gabbana handbags, ed hardy bags,Designer Purses,fendi handbags, jimmy choo bags ,juicy couture bags, juicy couture handbags, Gucci Handbags,prada bags ,coach handbags. also,we are prove that all of them are good and beautiful,with many colour and different size.if you like our goods,you can pay them by paypal, credit cards, western union and we will ship to you within 48 by DHL ,EMS and UPS shipping ,just to buy what Paul Smith Bags you like,and let you to feel the different experience of you life.come on,choose our handbags online,choose the succeed. Perfectly imitated, of high quality, are our Paul Smith Clothing.From the beginning, ReplicasHandbag has a good concept of Chanel Handbags and was clear what exactly attract their clients's attention.
zz zz op 26-8-2010 om 5:25
Balenciaga is known for providing some of the latest and stylish designer bags as replica Juicy Couture handbags of this renowned name are considered as some of the most preferred merchandises in the fashion world. These innovative designer handbags on sale are a unique creation of Cristobal Balenciaga are available in an assortment of styles such as the First, City, Work, Weekender, Purse, Box, Twiggy, etc. By keeping in mind the quality and durability, professional and expert craftsmen of Balenciaga make replica Luella handbags carefully without compromising with quality. Balenciaga celine handbags without any doubt are designed to boast a blend of fashion and quality. They add spice to your personality and make you the centre of attraction in parties, big events and other places. These replica Jimmy Choo handbags are fashionable and also have enough space to hold everything that you need for your day. There are a number of celebrities often seen with a cheap Christian Dior handbags wallets and purses. Moreover, Balenciaga Gucci replica and other items are a great addition to any outfit whether it is casual or dressy. It is a fact that women who carry a Versace handbags of Balenciaga are centre of attraction in any party, public place or any kind of event. However, the popularity of these hermes replica has often led to pirating. On the other hand, due to their high cost, there are numerous women who can only dream of owning one of these beautiful masterpieces. Balenciaga armani replica handbags are the great addition to any outfit that can be purchased easily. In other words, one can buy many replica Armani handbags in the cost of one replica Bottega Veneta handbags of Balenciaga. Balenciaga discount designer bags allow you to add your wardrobe without spending a lot of money. Another reason behind the popularity of Balenciaga real designer handbags is that with continuous changing in the fashion, cartier handbags wallets and purses become obsolete soon and no one likes to spend a lot of money. Balenciaga replica Prada handbags are also made of quality, lightweight, durable and unique leather that offer a tempting, shiny and above all very unique look. Whether you want light blue hobo, grey hobo, dark purple hobo, mix skin nails Juicy Couture replica or motorcyclecartier replica, Balenciaga replica Bally handbags are available in all styles, sizes and design. Only you have to select the designer bags according to your choice and design and place your order online. When it comes to place an order for Balenciaga replica Anya Hindmarch handbags, you can place your order easily. There are numerous online stores offering you replica Lancel handbags at affordable prices with attractive discounts.
jordanshoes op 1-9-2010 om 9:57
Here jordan shoes is a cheap jordan shoes bit of louis vuitton shoes for men history nike air yeezy on nike air max 2010 the e currency coogi hoodies jordans for women cheap gucci belts for men gucci sneakers for women exchange cheap timberland boots for men system. gucci mens shoes The e-currency cheap red monkey jeans cheap creative recreation shoes gucci caps for men exchange program red monkey jeans started gucci hats a true religion jeans for women few years bape shoes gucci boots for men ago, and on sale uggs when cheap jordans for sale it first nike shox for men came ed hardy shirts for men out it nike free was women gucci shoes a really louis vuitton caps hot business ed hardy hoodies men and people cheap gucci shoes alife shoes where making cheap af1 shoes mens nike shox shoes a lot coogi shoes of money off of it. People then decided to cash in on other getting into the opportunity buy creating e books, and other types of training courses to help those in the business.
replica Rolex op 1-9-2010 om 11:23
igner handbags not e jerseY cheap igner handbags not e pittsbuRgh steelers 34 rashard mendenhall white jersey-3341
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar