1

I am trying to implement a "Fillable Form" in which editable text fields appear over top of an image of a pre-preprinted form for a dot matrix printer. (using c# and Windows Forms and targeting .Net 2.0) My first idea was to use the image as the Windows Form background, but it looked horrible when scrolling and also did not scroll properly with the content.

My next attempt was to create a fixed-size window with a panel that overflows the bounds of the window (for scrolling purposes.) I added a PictureBox to the panel, and added my textboxes on top of it. This works fine, except that TextBoxes do not support transparency, so I tried several methods to make the TextBoxes transparent. One approach was to use an odd background color and a transparency key. Another, described in the following links, was to create a derived class that allows transparency:

Transparency for windows forms textbox

TextBox with a Transparent Background

Neither method works, because as I have come to find out, "transparency" in Windows Forms just means that the background of the window is painted onto the control background. Since the PictureBox is positioned between the Window background and the TextBox, it gives the appearance that the TextBox is not transparent, but simply has a background color equal to the background color of the Window. With the transparency key approach, the entire application becomes transparent so that you can see Visual Studio in the background, which is not what I want. So now I am trying to implement a class that derives from TextBox and overrides either OnPaint or OnPaintBackground to paint the appropriate part of the PictureBox image onto the control background to give the illusion of transparency as described in the following link:

How to create a transparent control which works when on top of other controls?

First of all, I can't get it working (I have tried various things, and either get a completely black control, or just a standard label background), and second of all, I get intermittent ArgumentExceptions from the DrawToBitmap method that have the cryptic message "Additional information: targetBounds." Based on the following link from MSDN, I believe that this is because the bitmap is too large - in either event it seems inefficient to capture the whole form image here because I really just want a tiny piece of it.

https://msdn.microsoft.com/en-us/library/system.windows.forms.control.drawtobitmap(v=vs.100).aspx

Here is my latest attempt. Can somebody please help me with the OnPaintBackground implementation or suggest a different approach? Thanks in advance!

public partial class TransparentTextbox : TextBox
{
    public TransparentTextbox()
    {
        InitializeComponent();
        SetStyle(ControlStyles.OptimizedDoubleBuffer |
                    ControlStyles.AllPaintingInWmPaint |
                    ControlStyles.ResizeRedraw |
                    ControlStyles.UserPaint, true);
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        //base.OnPaintBackground(e); // not sure whether I need this
        if (Parent != null)
        {
            foreach (Control c in Parent.Controls)
            {
                if (c.GetType() == typeof(PictureBox))
                {
                    PictureBox formImg = (PictureBox)c;
                    Bitmap bitmap = new Bitmap(formImg.Width, formImg.Height);
                    formImg.DrawToBitmap(bitmap, formImg.Bounds);
                    e.Graphics.DrawImage(bitmap, -Left, -Top);

                    break;
                }
            }

            Debug.WriteLine(Name + " didn't find the PictureBox.");
        }
    }
}

NOTE: This has been tagged as a duplicate, but I referenced the "duplicate question" in my original post, and explained why it was not working. That solution only works if the TextBox sits directly over the Window - if another control (such as my Panel and PictureBox) sit between the window and the TextBox, then .Net draws the Window background onto the TextBox background, effectively making its background look gray, not transparent.

Community
  • 1
  • 1
Dave Smash
  • 2,941
  • 1
  • 18
  • 38
  • 1
    I don't think this is possible with Windows Forms standard TextBox. If you're open to using a 3rd party kit (I can recommend), Infragistics offer a solution to your problem using a background image, and origin. http://www.infragistics.com/community/forums/t/1324.aspx – Jon Jul 14 '16 at 19:40
  • 1
    Take a look at this article: [Alpha Blended (Transparent Capable) TextBox and RichTextBox](http://www.codeproject.com/Articles/13197/Alpha-Blended-Transparent-Capable-TextBox-and-Rich) or this one: [AlphaBlendTextBox - A transparent/translucent textbox for .NET](http://www.codeproject.com/Articles/4390/AlphaBlendTextBox-A-transparent-translucent-textbo) – Reza Aghaei Jul 14 '16 at 19:43
  • This was a limitation in Windows Forms that some 3rd party components can almost fix. But it will add a ton of weight and references to a simple Winforms project. Is switching to WPF out of the question? Something like this would be very easy to accomdate. – Kevin B Burns Jul 14 '16 at 20:00
  • @Reza - this looks promising - the sample application is basically doing what I am trying to do. Just including the source and changing my TransparentTextbox to one of his controls is giving me similar results to what I am getting with my own class, but I am going to dig a little deeper - there must be something I am missing if his works. – Dave Smash Jul 14 '16 at 20:05
  • @KevinBBurns - I am trying to make a class library that I can share between a newer WPF application and an older WinForms application... I'm pretty sure I can get a WPF solution working, but one way or another the main place I need to use this is in an existing WinForms app. – Dave Smash Jul 14 '16 at 20:07
  • I'm not sur that putting some control over a picturebox is good way. Could you give more detail over the issue of your first attempt (forms backgroundimage). This sound for me as a nice approach to display the "old paper form image". You could also have a look to [How to develop a readonly transparent Textbox control in Windows Forms](https://blogs.msdn.microsoft.com/nandal/2008/07/15/how-to-develop-a-readonly-transparent-textbox-control-in-windows-forms/). RichTextBox looks to be more usefull for this kind of stuff. – Marco Guignard Jul 14 '16 at 21:23
  • @MarcoGuignard - When I set the form backgroundimage, it would look fine initially, but when you scrolled, the text would scroll but the background image would stay still (causing everything to lose alignment). I also tried setting the panel background to the image, but when you scroll, you would get severe black horizontal lines all over the image and it would never look right again. When you scroll the PictureBox using the panel, it looks flawless (although that idea is proving to have its own share of problems.) – Dave Smash Jul 14 '16 at 21:58
  • Possible duplicate of [Transparency for windows forms textbox](http://stackoverflow.com/questions/16050249/transparency-for-windows-forms-textbox) –  Jul 15 '16 at 04:33
  • @ElementalPete Have you tried to change the **BackgroundImageLayout** to **None**. This should fix this point. – Marco Guignard Jul 15 '16 at 07:35
  • @MarcoGuignard - I am trying it now, and getting the same result - the textboxes scroll, but the background image stays still, causing things to go out of alignment. If I can get it working with the background image rather than the textbox, I think it would lead to a much better solution, though. – Dave Smash Jul 15 '16 at 15:20
  • @ElementalPete Strange I have tried a test project with one gridview half out of a form to make scroll bar appear and when I go up and down The background image doesn't flick. I would try it with a more real paper form next monday. – Marco Guignard Jul 15 '16 at 16:55

1 Answers1

-1

I think I have finally gotten to the bottom of this. I added a Bitmap variable to my class, and when I instantiate the textboxes, I am setting it to contain just the portion of the form image that sits behind the control. Then I overload OnPaintBackground to display the Bitmap, and I overload OnPaint to manually draw the text string. Here is the updated version of my TransparentTextbox class:

public partial class TransparentTextbox : TextBox
{
    public Bitmap BgBitmap { get; set; }

    public TransparentTextbox()
    {
        InitializeComponent();
        SetStyle(ControlStyles.OptimizedDoubleBuffer |
                    ControlStyles.AllPaintingInWmPaint |
                    ControlStyles.ResizeRedraw |
                    ControlStyles.UserPaint, true);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, new PointF(0.0F, 0.0F));
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        e.Graphics.DrawImage(BgBitmap, 0, 0);
    }
}

... and here is the relevant part of how I instantiate:

Bitmap bgImage = (Bitmap)Bitmap.FromStream(Document.FormImage);

PictureBox pb = new PictureBox();
pb.Image = bgImage;
pb.Size = pb.Image.Size;
pb.Top = 0;
pb.Left = 0;
panel1.Controls.Add(pb);

foreach (FormField field in Document.FormFields)
{
    TransparentTextbox tb = new TransparentTextbox();   
    tb.Width = (int)Math.Ceiling(field.MaxLineWidth * 96.0);
    tb.Height = 22;
    tb.Font = new Font("Courier", 12);
    tb.BorderStyle = BorderStyle.None;
    tb.Text = "Super Neat!";
    tb.TextChanged += tb_TextChanged;
    tb.Left = (int)Math.Ceiling(field.XValue * 96.0);
    tb.Top = (int)Math.Ceiling(field.YValue * 96.0);
    tb.Visible = true;

    Bitmap b = new Bitmap(tb.Width, tb.Height);
    using (Graphics g = Graphics.FromImage(b))
    {
        g.DrawImage(bgImage, new Rectangle(0, 0, b.Width, b.Height), tb.Bounds, GraphicsUnit.Pixel);
        tb.BgBitmap = b;
    }
    panel1.Controls.Add(tb);
}

I still need to work on how the text looks when I highlight it, and other things like that, but I feel like I am on the right track. +1 to Reza Aghaei and Mangist for commenting with other viable solutions!

Dave Smash
  • 2,941
  • 1
  • 18
  • 38