开发者

AnimateWindow API transparency problem with RichTextBox

I'm using the AnimateWindow API to show or hide a Form with a slide animation. The problem is that if the Form contains a RichTextBox control, it doesn't display this control correctly. It's transparent and doesn't show any text.

After the animation is complete, double clicking somewhere in the control will reveal lines of text.

I've created a full sample that anyone can use to compile and test themselves. There's no better way to debug this, unless you already know the answer.

There's 2 buttons in the main form, one to show another form and the other to hide that same form. I've added both the RichTextBox as a simple TextBox. As you'll see, the problem only happens on the RichTextBox.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1 {
    static class Program {
        public const int AW_ACTIVATE = 0x00020000;
        public const int AW_HIDE = 0x00010000;
        public const int AW_HOR_NEGATIVE = 0x00000002;
        public const int AW_HOR_POSITIVE = 0x00000001;
        public const int AW_SLIDE = 0x00040000;

        [DllImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool AnimateWindow(IntPtr hWnd, int time, int awFlags);

        [STAThread]
        public static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

    public class Form1 : Form {
        public Form form;

        public Form1() {
            InitializeComponent();
            form = new Form2();
            form.Show();
            form.Hide();
        }

        private void button1_Click(object sender, EventArgs e) {
            form.Location = new Point(Location.X, Location.Y + form.Height + 100);
            Program.AnimateWindow(form.Handle, 1000, Program.AW_SLIDE | Program.AW_HOR_NEGATIVE | Program.AW_ACTIVATE);
            form.Show();
        }

        private void button2_Click(object sender, EventArgs e) {
            Program.AnimateWindow(form.Handle, 1000, Program.AW_HIDE | Program.AW_HOR_POSITIVE);
            form.Hide();
        }

        #region Windows Form Designer generated code

        private void InitializeComponent() {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button1.Location = new System.Drawing.Point(11, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(133, 114);
            this.button1.TabIndex = 0;
            this.button1.Text = "SHOW";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button2.Location = new System.Drawing.Point(150, 12);
            this.button2.Name = "button2";开发者_如何学编程
            this.button2.Size = new System.Drawing.Size(133, 114);
            this.button2.TabIndex = 1;
            this.button2.Text = "HIDE";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(294, 138);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "Form1";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "Form1";
            this.ResumeLayout(false);
        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
    }

    public class Form2 : Form {
        public Form2() {
            InitializeComponent();
        }

        #region Windows Form Designer generated code

        private void InitializeComponent() {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form2));
            this.richTextBox1 = new System.Windows.Forms.RichTextBox();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Top;
            this.richTextBox1.Location = new System.Drawing.Point(0, 0);
            this.richTextBox1.Name = "richTextBox1";
            this.richTextBox1.Size = new System.Drawing.Size(240, 50);
            this.richTextBox1.TabIndex = 0;
            this.richTextBox1.Text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.";
            this.textBox1.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.textBox1.Location = new System.Drawing.Point(0, 57);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBox1.Size = new System.Drawing.Size(240, 50);
            this.textBox1.TabIndex = 1;
            this.textBox1.Text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.";
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(240, 107);
            this.ControlBox = false;
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.richTextBox1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.Name = "Form2";
            this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
            this.Text = "Form2";
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        #endregion

        private System.Windows.Forms.RichTextBox richTextBox1;
        private System.Windows.Forms.TextBox textBox1;
    }
}

NOTE: I'm starting a bounty on this so if you're going to answer, please answer with a solution that works and not something for me to try and see if it works. Why? Because if people upvote your answer and doesn't fix anything , it will still be marked as accepted which it didn't help. Thanks for understanding.


Please see this code... this is the most bizarre bug I've ever seen... the only difference is that I had to trap the VisibleChanged Event for the RichTextBox, and to set the Capture property, update it, then switch off the Capture, my thinking there was to send a message to the control to force a double-click event but discovered that is not required... I also had to override the OnActivated event for the form2 itself in order to get the event trapped AFTER the animation is done and to force a refresh...strange bizarre bug....

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Text;

namespace WindowsFormsApplication1
{
    static class Program
    {
        public const int AW_ACTIVATE = 0x00020000;
        public const int AW_HIDE = 0x00010000;
        public const int AW_HOR_NEGATIVE = 0x00000002;
        public const int AW_HOR_POSITIVE = 0x00000001;
        public const int AW_SLIDE = 0x00040000;

        [DllImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool AnimateWindow(IntPtr hWnd, int time, int awFlags);

        [STAThread]
        public static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

    public class Form1 : Form
    {
        public Form form;

        public Form1()
        {
            InitializeComponent();
            form = new Form2();
            form.Show();
            form.Hide();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            form.Location = new Point(Location.X, Location.Y + form.Height + 100);
            Program.AnimateWindow(form.Handle, 1000, Program.AW_SLIDE | Program.AW_HOR_NEGATIVE | Program.AW_ACTIVATE);
            form.Show();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Program.AnimateWindow(form.Handle, 1000, Program.AW_HIDE | Program.AW_HOR_POSITIVE);
            form.Hide();
        }

        #region Windows Form Designer generated code

        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button1.Location = new System.Drawing.Point(11, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(133, 114);
            this.button1.TabIndex = 0;
            this.button1.Text = "SHOW";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button2.Location = new System.Drawing.Point(150, 12);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(133, 114);
            this.button2.TabIndex = 1;
            this.button2.Text = "HIDE";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(294, 138);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "Form1";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "Form1";
            this.ResumeLayout(false);
        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
    }

    public class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
            this.richTextBox1.VisibleChanged += new EventHandler(richTextBox1_VisibleChanged);
        }

        void richTextBox1_VisibleChanged(object sender, EventArgs e)
        {
            if (this.richTextBox1.Visible)
            {
                System.Diagnostics.Debug.WriteLine("Visible!");
                this.richTextBox1.Capture = true;
                this.richTextBox1.Update();
                this.richTextBox1.Capture = false;
                this.richTextBox1.Refresh();
            }
            else System.Diagnostics.Debug.WriteLine("InVisible!");
        }

        #region Windows Form Designer generated code

        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form2));
            this.richTextBox1 = new System.Windows.Forms.RichTextBox();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Top;
            this.richTextBox1.Location = new System.Drawing.Point(0, 0);
            this.richTextBox1.Name = "richTextBox1";
            this.richTextBox1.Size = new System.Drawing.Size(240, 50);
            this.richTextBox1.TabIndex = 0;
            this.richTextBox1.Text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.";
            this.textBox1.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.textBox1.Location = new System.Drawing.Point(0, 57);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBox1.Size = new System.Drawing.Size(240, 50);
            this.textBox1.TabIndex = 1;
            this.textBox1.Text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.";
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(240, 107);
            this.ControlBox = false;
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.richTextBox1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.Name = "Form2";
            this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
            this.Text = "Form2";
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        #endregion

        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            System.Diagnostics.Debug.WriteLine("OnActivated");
            this.Refresh();
        }

        private System.Windows.Forms.RichTextBox richTextBox1;
        private System.Windows.Forms.TextBox textBox1;
    }
}

The code works nonetheless - a very obscure thing - the funny thing is within the VisibleChanged handler, I was expecting the debug output to say "InVisible" when clicking on the 'Hide' button but it did not. I did try using WM_PRINT and WM_PRINTCLIENT message which failed with the rich-text-box, just don't ask me why setting the capture on the rich-text-box itself and forcing a refresh solved it... Here's the quote from MSDN on this:

The window procedures for the window and its child windows should handle any WM_PRINT or WM_PRINTCLIENT messages. Dialog boxes, controls, and common controls already handle WM_PRINTCLIENT. The default window procedure already handles WM_PRINT. If a child window is displayed partially clipped, when it is animated it will have holes where it is clipped.

I do not know if it's worth the trouble in pointing this out to Microsoft and say "There's a bug either in the AnimateWindow or in the RichTextBox Control with .NET", either way, it is difficult to ascertain, since after the animation is done, the text-box appears ok, but not the rich-text-box, yet explicitly using the Capture and the Update when the capture is on, forced a refresh... it swings both ways on the bug itself - could be in both spots... Very unusual and bizarre anyway.. hope the code is of use to you.


The RichTextBox does not process WM_PRINT messages, which is I believe what AnimateWindow uses behind the scenes, so I don't see any way of doing the animation with the text visible in your rich text box (in case you want richtext box to display text during the animation).

When the animation is complete you can call richTextBox1.Refresh (in your button1_Click via a helper method in the Form2 class) for the box to repaint - the control with the text will be shown alright without any double-clicking.


Does WPF not contain the slide animations you need?

APIs like AnimateWindow are ... not given a lot of attention at Microsoft - usually in favor of a 'better' .NET / WPF method. Using native / not managed APIs to animate windows quickly lands you in a world of hurt :- Microsoft do not support flicker control animations - or alpha effects - in native controls and windows.

The native AnimateWindow tends to fall over if the target window has aero glass. any non-client elements at all. Any child controls. any transparency (window regions, layered windows with alpha or masking).

It really just doesn't work that well at all.


The only way to work around it would be to do what AnimateWindow does. AnimateWindow is not so much an API call, but a convenience wrapper around functionality that can "easilly" be implemented by the user.

All that AnimateWindow does it, well, reposition the animated window on a timer. Depending on the animation it will use clip regions, or add the WS_EX_LAYERED style (to do the alpha blends) for the duration of the animation. This is why AnimateWindow fails so spectacularly when given windows already using those effects.

Performing the animation yourself, you can try to tweak the parameters to do it in a way more compatible with the rich edits painting requirements.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜