4

私は、rotationプロパティを介してボタンテキストの回転を可能にするカスタムwinformsボタンコントロールを作成しようとしています。私はほとんどそれを機能させていますが、それは非常に厄介であり、これを行うための適切な方法を知りたいです。

特に現在、テキストの再描画は奇妙な動作をしています。コントロールを画面の外に移動してからゆっくりとテキストに戻すと、テキストがめちゃくちゃになるか(半分しか描画されないなど)、マウスを合わせるまで完全に消えます。明らかに私は何か間違ったことをしているが、何を理解することができない。

ボタンコントロールから継承し、OnPaintメソッドをオーバーライドしています。

コードは次のとおりです。

public class RotateButton : Button
{
    private string text;
    private bool painting = false;

    public enum RotationType { None, Right, Flip, Left}

    [DefaultValue(RotationType.None), Category("Appearance"), Description("Rotates Button Text")]
    public RotationType Rotation { get; set; }

    public override string Text
    {
        get
        {
            if (!painting)
                return text;
            else
                return "";
        }
        set
        {
            text = value;
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        painting = true;

        base.OnPaint(e);

        StringFormat format = new StringFormat();
        Int32 lNum = (Int32)Math.Log((Double)this.TextAlign, 2);
        format.LineAlignment = (StringAlignment)(lNum / 4);
        format.Alignment = (StringAlignment)(lNum % 4);

        int padding = 2;

        SizeF txt = e.Graphics.MeasureString(Text, this.Font);
        SizeF sz = e.Graphics.VisibleClipBounds.Size;

        if (Rotation == RotationType.Right)
        {
            //90 degrees
            e.Graphics.TranslateTransform(sz.Width, 0);
            e.Graphics.RotateTransform(90);
            e.Graphics.DrawString(text, this.Font, Brushes.Black, new RectangleF(padding, padding, sz.Height - padding, sz.Width - padding), format);
            e.Graphics.ResetTransform();
        }
        else if (Rotation == RotationType.Flip)
        {
            //180 degrees
            e.Graphics.TranslateTransform(sz.Width, sz.Height);
            e.Graphics.RotateTransform(180);
            e.Graphics.DrawString(text, this.Font, Brushes.Black, new RectangleF(padding, padding, sz.Width - padding, sz.Height - padding), format);
            e.Graphics.ResetTransform();
        }
        else if (Rotation == RotationType.Left)
        {
            //270 degrees
            e.Graphics.TranslateTransform(0, sz.Height);
            e.Graphics.RotateTransform(270);
            e.Graphics.DrawString(text, this.Font, Brushes.Black, new RectangleF(padding, padding, sz.Height - padding, sz.Width - padding), format);
            e.Graphics.ResetTransform();
        }
        else
        {
            //0 = 360 degrees
            e.Graphics.TranslateTransform(0, 0);
            e.Graphics.RotateTransform(0);
            e.Graphics.DrawString(text, this.Font, Brushes.Black, new RectangleF(padding, padding, sz.Width - padding, sz.Height - padding), format);
            e.Graphics.ResetTransform();
        }

        painting = false;
    }
}

だから私の主な質問は、テキストの再描画の問題をどのように修正できるかということです。

さらに、上記のコードについて他にもいくつか質問/コメントがあります。

  1. 最初は、テキストが2回表示されていました。1回はデフォルトの場所に、もう1回は回転した場所に表示されていました。これは、base.OnPaintメソッドが呼び出されたときにテキストが最初に描画されるためだと思います。この場合、テキストが最初に描画されないようにするにはどうすればよいですか?

    私の解決策は、ブール値を使用して呼び出す前に、テキスト文字列をオーバーライドしてクリアすることbase.OnPaintです。これは、私が特に満足している解決策ではありません。

  2. 最後にPaintEventArgsをで破棄する必要がありe.disposeますか?PaintEventArgsオブジェクトがどのように処理されているのかわかりません。

前もって感謝します!

追伸 初めての投稿・質問ですので、エチケットやルールをうっかり無視してしまった場合は、あらかじめお詫び申し上げます。

4

2 に答える 2

2
  1. VisibleClipBoundsは、再描画が必要な領域を返します。たとえば、ボタンの半分を再描画する必要がある場合(ボタンの半分を覆う上部のフォームが閉じている場合)、VisibleClipBoundsはその領域のみを返します。したがって、中央揃えのテキストをペイントするためにそれを使用することはできません。SizeF sz = new SizeF(Width、Height); 再塗装の問題に対処する必要があります。

  2. ボタンは所有者の描画をサポートしていません。あなたのやり方は問題ないようです。

  3. 原則として、作成していないオブジェクトを破棄しないでください。使い捨てのイベント引数は、それらを作成したロジックによって破棄されます(最初はOn ...と呼ばれます)ので、PaintEventArgsの破棄について心配する必要はありません。

StackOverflowへようこそ:)

于 2012-09-03T09:40:36.203 に答える
1

まず、をに置き換え、すべての場合で同一であった2行をコードから移動することで、if... else...コードを少しリファクタリングしました。switch... case...しかし、それはそれをより読みやすくするためだけのものです。

    protected override void OnPaint(PaintEventArgs e)
    {
        painting = true;

        base.OnPaint(e);

        StringFormat format = new StringFormat();
        Int32 lNum = (Int32)Math.Log((Double)this.TextAlign, 2);
        format.LineAlignment = (StringAlignment)(lNum / 4);
        format.Alignment = (StringAlignment)(lNum % 4);

        int padding = 2;

        SizeF txt = e.Graphics.MeasureString(Text, this.Font);
        SizeF sz = e.Graphics.VisibleClipBounds.Size;
        switch (Rotation)
        {
            case RotationType.Right:  //90 degrees
                {
                    e.Graphics.TranslateTransform(sz.Width, 0);
                    e.Graphics.RotateTransform(90);
                    break;
                }
            case RotationType.Flip: //180 degrees
                {
                    e.Graphics.TranslateTransform(sz.Width, sz.Height);
                    e.Graphics.RotateTransform(180);
                    break;
                }
            case RotationType.Left: //270 degrees
                {
                    e.Graphics.TranslateTransform(0, sz.Height);
                    e.Graphics.RotateTransform(270);
                    break;
                }
            default: //0 = 360 degrees
                {
                    e.Graphics.TranslateTransform(0, 0);
                    e.Graphics.RotateTransform(0);
                    break;
                }
        }

        e.Graphics.DrawString(text, this.Font, Brushes.Black, new RectangleF(padding, padding, sz.Height - padding, sz.Width - padding), format);
        e.Graphics.ResetTransform();

        painting = false;
    }

RotateButton主な質問について:を作成し、回転タイプをに設定して、これをテストしましたRight。あなたが説明した行動を確認できます。OnPaint休憩後にプログラムを再開するたびに、フォームがフォーカスを取り戻し、新しいPaintイベントがトリガーされるため、デバッグは困難です。最後に、メソッドの最後に2行を追加して、この動作の原因を突き止めました。

System.Diagnostics.Debug.WriteLine(sz.Width.ToString());
System.Diagnostics.Debug.WriteLine(sz.Height.ToString());

これにより、幅と高さの値がVisualStudioの出力ウィンドウに書き込まれます。そこで、コントロールを画面に戻すと、値が1に設定されていることがわかりましたsz.Width。したがって、テキストはコントロールに描画されますが、長方形が小さすぎるため、表示されません。つまり、を使用することはできませんe.Graphics.VisibleClipBounds.Size。サイズを自分で計算する必要があります(を使用する場合は、サンプルコードのようにではなく、パラメータとしてMeasureString渡すように注意してください)。textText

追加の質問について:

  1. 私はあなたの解決策は大丈夫だと思います。textを呼び出す前に空の文字列に設定し、base.OnPaint()後で正しい値を復元することを検討してください。
  2. いいえ、絶対にありません。PaintEventArgsオブジェクトはメソッドの外部で作成されます。OnPaint破棄(必要な場合)はそこで処理する必要があります。オブジェクトの「作成者」だけが、オブジェクトを適切に破棄する方法とタイミングを知っています。(後でイベントを発生させたコードで必要になるかどうかはわかりませんPaint)。
于 2012-09-03T11:22:08.027 に答える