Control Rendering in Print Document

Often times we want to display a control, rendered on a Windows form, in a C1PrintDocument. However, being that there is no direct method to achieve this, we can follow the approach mentioned in this blog. The idea is to capture the control's state as an image object and then render it the same in a Print Document. The implementation, though, is something that makes this worth reading. We will use C1Flexgrid control with a bound image column and then render it on a C1PrintDocument.

Step 1: Display Bound Images in C1FlexGrid

Step One is carried out by binding C1FlexGrid with a datasource. For simplicity we are using Category table of C1Nwind database. //Binding C1FlexGrid categoriesTableAdapter.Connection = new OleDbConnection(GetConnectionString()); categoriesTableAdapter.Fill(this.c1NWindDataSet.Categories); _flex.DataSource = c1NWindDataSet.Tables[0]; The GetConnectionString() method here would return the path for the C1Nwind.mdb :

//Returns the connection string for the datasource  
static string GetConnectionString()  
    string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal)   @"\\ComponentOne Samples\\Common";  
    string conn = @"provider=microsoft.jet.oledb.4.0;data source={0}\\c1nwind.mdb;";  
    return string.Format(conn, path);  

To view images rendered within a flexgrid column, it is necessary to make some changes. By default, the visibility of the image column is set as false. Set the visibility of Picture column to True. Furthermore, we need to set the custom cell drawing, which is done by setting the DrawMode property of the grid to OwnerDraw and subscribing the OwnerDrawCell event. Finally, making all the preceding revisions would require the following code which we have added in the Form_Load event :

private void Form1_Load(object sender, EventArgs e)  
    //Binding C1FlexGrid  
    categoriesTableAdapter.Connection = new OleDbConnection(GetConnectionString());  
    _flex.DataSource = c1NWindDataSet.Tables[0];  

    //Displaying Picture Column and Attaching a OwnerDrawCell Event to _flex  
    _flex.Cols["Picture"].Visible = true;  
    _flex.DrawMode = C1.Win.C1FlexGrid.DrawModeEnum.OwnerDraw;  
    \_flex.OwnerDrawCell  =new C1.Win.C1FlexGrid.OwnerDrawCellEventHandler(\_flex_OwnerDrawCell);  

    // initialize styles  
    \_flex.Styles.Normal.Margins.Left = \_flex.Styles.Normal.Margins.Right = 0;  
    _flex.Styles.Normal.TextAlign = C1.Win.C1FlexGrid.TextAlignEnum.LeftTop;  
    _flex.Styles.Fixed.TextAlign = C1.Win.C1FlexGrid.TextAlignEnum.LeftCenter;  

    // make rows taller to show images  
    int hei = _flex.Rows.DefaultSize;  
    _flex.Rows.DefaultSize = hei * 4;  
    _flex.Rows[0].Height = hei;  

Within the OwnerDrawCell event we parse the image column of the grid by converting each cell into an individual image object :

//Handles Custom Cell Drawing to display bound images  
private void \_flex\_OwnerDrawCell(object sender, C1.Win.C1FlexGrid.OwnerDrawCellEventArgs e)  
    // "Picture" is an image stored in a blob (byte[])  
    if (_flex.Cols[e.Col].Name == "Picture")  
        // try loading from mdb  
        e.Image = LoadImage(_flex[e.Row, e.Col] as byte[]);  

        // if we got an image, blank text  
        if (e.Image != null) e.Text = null;  

The LoadImage() method is a subroutine to convert the Byte[] array to Bitmap images :

// load bitmap image stored in blob DB field  
// this assumes the image was stored as an OLE "package"  
// (this is the format used by Access)  
static Image LoadImage(byte[] picData)  
    // make sure this is an embedded object  
    const int bmData = 78;  
    if (picData == null || picData.Length < bmData   2) return null;  
    if (picData[0] != 0x15 || picData[1] != 0x1c) return null;  

    // we only handle bitmaps for now  
    if (picData[bmData] != 'B' || picData[bmData   1] != 'M') return null;  

    // load the picture  
    Image img = null;  
        MemoryStream ms = new MemoryStream(picData, bmData, picData.Length - bmData);  
        img = Image.FromStream(ms);  
    catch { }  

    // return what we got  
    return img;  

At the end of step one, we have a bound Flexgrid with an image column.

Step 2: Capture an image of C1FlexGrid

Now we will capture the image of C1FlexGrid using a GetControlImage() method. This is a simple method that uses the ClientRectangle of the control and the DrawToBitmap() method of Control class to convert it into a bitmap image with a zoom-like speed.

// Method to convert C1FlexGrid into an image  
static Image GetControlImage(Control ctl, float zoom)  
    // get image  
    Rectangle rc = ctl.ClientRectangle;  
    Bitmap bmp = new Bitmap(rc.Width, rc.Height);  
    ctl.DrawToBitmap(bmp, rc);  

    // apply zoom  
    if (zoom != 1)  
        Size newSize = Size.Round(new SizeF(bmp.Width * zoom, bmp.Height * zoom));  
        bmp = new Bitmap(bmp, newSize);  

    // return image  
    return bmp;  

We will use this method when opening a form containing C1PrintPreviewControl :

PreviewForm frm = new PreviewForm(GetControlImage(_flex, 1));  

Step 3: Render Captured Image in C1PrintDocument

The last step is to render the captured image in a C1PrintDocunment. We will add 2 objects to the document -- RenderText and RenderImage. RenderText renders text on the PrintDocument whereas RenderImage renders the captured C1FlexGrid image. We also define the default unit and page margins for the document in this step. The code should look as follows :

private void PreviewForm_Load(object sender, EventArgs e)  
    //PrintDocument for C1PrintPreviewControl  
    C1PrintDocument doc = new C1PrintDocument();  
    doc.DefaultUnit = UnitTypeEnum.Inch;  
    doc.PageLayout.PageSettings.TopMargin = 0.5;  
    doc.PageLayout.PageSettings.LeftMargin = 0.5;  
    doc.PageLayout.PageSettings.RightMargin = 0.5;  
    doc.PageLayout.PageSettings.BottomMargin = 0.5;  

    //Renders text  
    RenderText rt = new RenderText();  
    rt.Text = "C1FlexGrid Image Rendering on C1PrintDocument";  

    //Renders C1FlexGrid control image  
    RenderImage ri = new RenderImage();  
    ri.Image = img;  
    ri.Width = doc.PageLayout.PageSettings.Width.Value-1;  

    c1PrintPreviewControl1.Document = doc;  

When we run the application, C1FlexGrid is displayed on the form. Click on the Render To PrintDocument button and the flexgrid will appear on the C1PrintPreviewControl. Download Sample


GrapeCity Developer Tools
comments powered by Disqus