6

Controlプロパティをサポートする派生クラスを作成しようとしていOpcacityます。
このコントロールは、テキストと画像の両方をホストでき、それらをフェードアウトおよびフェードインできます。
私のコードは次のとおりです。

internal class FadeControl : Control
{
    private int opacity = 100;

    public FadeControl()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    }

    public int Opacity
    {
        get
        {
            return opacity;
        }
        set
        {
            if (value > 100) opacity = 100;
            else if (value < 1) opacity = 1;
            else opacity = value;

            if (Parent != null)
                Parent.Invalidate(Bounds, true);
        }
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle = cp.ExStyle | 0x20;
            return cp;
        }
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        //do nothing
    }

    protected override void OnMove(EventArgs e)
    {
        RecreateHandle();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (Graphics g = e.Graphics)
        {
            Rectangle bounds = new Rectangle(0, 0, Width - 1, Height - 1);
            int alpha = (opacity * 255) / 100;

            using (Brush bckColor = new SolidBrush(Color.FromArgb(alpha, BackColor)))
            {
                if (BackColor != Color.Transparent)
                    g.FillRectangle(bckColor, bounds);
            }

            ColorMatrix colorMatrix = new ColorMatrix();
            colorMatrix.Matrix33 = (float)alpha / 255;
            ImageAttributes imageAttr = new ImageAttributes();
            imageAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

            if (BackgroundImage != null)
                g.DrawImage(BackgroundImage, bounds, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr);

            if (Text != string.Empty)
            {
                using (Brush txtBrush = new SolidBrush(Color.FromArgb(alpha, ForeColor)))
                {
                    g.DrawString(Text, Font, txtBrush, 5, 5);
                }
            }
        }
    }

    protected override void OnBackColorChanged(EventArgs e)
    {
        if (Parent != null)
            Parent.Invalidate(Bounds, true);

        base.OnBackColorChanged(e);
    }

    protected override void OnParentBackColorChanged(EventArgs e)
    {
        Invalidate();

        base.OnParentBackColorChanged(e);
    }
}

タイマーが設定されているフォームにコントロールを配置しました。
タイマーは、コントロールの不透明度を0から100に設定し、元に戻します。
私が解決しようとしている問題は、不透明度を変更しているときにコントロールがちらつくことです。
コントロールをに設定するControlStyles.DoubleBufferと、フォーム上でコントロールが非表示になります。

どんなアドバイスでも大歓迎です。

4

1 に答える 1

0

WS_EX_TRANSPARENT透明な背景にダブル バッファと (0x20) の両方を使用できませんでした。そこで、親コントロールのコンテンツをコピーして透明な背景を実装し、ちらつきを防ぐためにダブル バッファーを使用することにしました。

以下は、テスト済みで機能する最終的なソース コードです。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

internal class FadeControl : Control
{
    private int opacity = 100;
    private Bitmap backgroundBuffer;
    private bool skipPaint;

    public FadeControl()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.DoubleBuffer |
                 ControlStyles.AllPaintingInWmPaint |
                 ControlStyles.UserPaint, true);
    }

    public int Opacity
    {
        get
        {
            return opacity;
        }
        set
        {
            if (value > 100) opacity = 100;
            else if (value < 1) opacity = 1;
            else opacity = value;

            if (Parent != null)
                Parent.Invalidate(Bounds, true);
        }
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        //do nothig
    }

    protected override void OnMove(EventArgs e)
    {
        RecreateHandle();
    }

    private void CreateBackgroundBuffer(Control parent)
    {
        int offsetX;
        int offsetY;
        GetOffsets(out offsetX, out offsetY, parent);
        backgroundBuffer = new Bitmap(Width + offsetX, Height + offsetY);
    }

    protected override void OnResize(EventArgs e)
    {
        var parent = Parent;
        if (parent != null)
        {
            CreateBackgroundBuffer(parent);
        }
        base.OnResize(e);
    }

    private void GetOffsets(out int offsetX, out int offsetY, Control parent)
    {
        var parentPosition = parent.PointToScreen(Point.Empty);
        offsetY = Top + parentPosition.Y - parent.Top;
        offsetX = Left + parentPosition.X - parent.Left;
    }

    private void UpdateBackgroundBuffer(int offsetX, int offsetY, Control parent)
    {
        if (backgroundBuffer == null)
        {
            CreateBackgroundBuffer(parent);
        }
        Rectangle parentBounds = new Rectangle(0, 0, Width + offsetX, Height + offsetY);
        skipPaint = true;
        parent.DrawToBitmap(backgroundBuffer, parentBounds);
        skipPaint = false;
    }

    private void DrawBackground(Graphics graphics, Rectangle bounds)
    {
        int offsetX;
        int offsetY;
        var parent = Parent;
        GetOffsets(out offsetX, out offsetY, parent);
        UpdateBackgroundBuffer(offsetX, offsetY, parent);
        graphics.DrawImage(backgroundBuffer, bounds, offsetX, offsetY, Width, Height, GraphicsUnit.Pixel);
    }

    private void Draw(Graphics graphics)
    {
        Rectangle bounds = new Rectangle(0, 0, Width, Height);
        DrawBackground(graphics, bounds);

        int alpha = (opacity * 255) / 100;

        using (Brush bckColor = new SolidBrush(Color.FromArgb(alpha, BackColor)))
        {
            if (BackColor != Color.Transparent)
            {
                graphics.FillRectangle(bckColor, bounds);
            }
        }

        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.Matrix33 = (float)alpha / 255;
        ImageAttributes imageAttr = new ImageAttributes();
        imageAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

        if (BackgroundImage != null)
        {
            graphics.DrawImage(BackgroundImage, bounds, 0, 0, Width, Height, GraphicsUnit.Pixel, imageAttr);
        }

        if (Text != string.Empty)
        {
            using (Brush txtBrush = new SolidBrush(Color.FromArgb(alpha, ForeColor)))
            {
                graphics.DrawString(Text, Font, txtBrush, 5, 5);
            }
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (!skipPaint)
        {
            Graphics graphics = e.Graphics;
            Draw(graphics);
        }
    }

    protected override void OnBackColorChanged(EventArgs e)
    {
        if (Parent != null)
        {
            Parent.Invalidate(Bounds, true);
        }
        base.OnBackColorChanged(e);
    }

    protected override void OnParentBackColorChanged(EventArgs e)
    {
        Invalidate();
        base.OnParentBackColorChanged(e);
    }
}

CreateParamsメソッドが存在しなくなったことに注意してください。また、コンストラクターを変更しました。

このフィールドは、無限再帰を行わずskipPaintに親に自分自身をビットマップに描画するように指示できるようにするために、いつペイントしないかを知ることです。OnPaint

backgroundBufferダブルバッファリングを実装するのではなく、コントロールをレンダリングせずに親のコンテンツのコピーを保持することです。ペイントごとに更新されますが、より効率的な解決策があることは知っています...* それでも、このアプローチはシンプルに保たれ、同じコンテナーにこれらのコントロールが多すぎる場合を除き、ボトルネックにはなりません。

*: 親が無効になるたびに更新する方がよいでしょう。さらに、同じ親のすべての FadeControls 間で共有しました。

于 2013-03-27T09:26:46.517 に答える