- PR -

コントロール描画の残像

投稿者投稿内容
NEX
会議室デビュー日: 2008/04/23
投稿数: 5
投稿日時: 2008-04-23 18:45
初めて質問させて頂きます。

VS2005 C# (.net framework 2.0) で現在 Windows アプリを作成しているのですが、
フォームの内のレイアウトを変更する際に残像が表示されてしまい困っています。

レイアウトは以下の 3 種類があります。

■ レイアウト A

┌───────────┐
│           │
│           │
│           │
│     @     │
│           │
│           │
│           │
└───────────┘

■ レイアウト B

┌───┬───────┐
│   │       │
│ @ │       │
│   │       │
├───┤   A   │
│   │       │
│ B │       │
│   │       │
└───┴───────┘

■ レイアウト C

┌───┬───────┐
│   │       │
│ @ │   A   │
│   │       │
├───┼───────┤
│   │       │
│ B │   C   │
│   │       │
└───┴───────┘

各レイアウト間はユーザ操作 ( クリック等 ) により、以下のように遷移します。

  ┌──┐
  │  ↓
A─→B  C
  ↑  │
  └──┘

・レイアウトは Panel, UserControl を継承したクラス、 Splitter で作成しています
・各小分けになった番号がふられた部分には DataGridView 、 Label 、 TextBox 、 PictureBox 、 TabControl など複数の子コントロールが載っています
・各小分けになった番号がふられた部分は DockStyle を指定しておりフォームのリサイズに影響を受けます
・各小分けになった番号がふられた部分は境界になっている Splitter の移動によってリサイズの影響を受けます

例えばレイアウト A から B に変更する場合、レイアウト B のAにレイアウト A の@の残像が表示されてしまいます。

コントロールの描画イメージを表示されないところで作成しておき、その処理が完了したタイミングで
画面に表示させたいのですが、 Windows の Control を継承したコントロールを使用して
そのようなことができるものなのでしょうか ?

※ DoubleBuffered プロパティが設定可能なものについては true に設定しましたが、残像が表示されてしまいました。
( DoubleBuffered プロパティが protected なものについてはサブクラスを作成して true に設定しました )

もし、そのような方法があるならば、教えて頂けますでしょうか ?

よろしくお願いします。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2008-04-24 07:46
デザイナが自動生成しているコードを見てください。レイアウト変更中は描画を止めたり、配置が終わったことを通知したりしています。それと同じことをすればいいと思います。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2008-04-24 08:48
引用:

NEXさんの書き込み (2008-04-23 18:45) より:
※ DoubleBuffered プロパティが設定可能なものについては true に設定しましたが、残像が表示されてしまいました。
( DoubleBuffered プロパティが protected なものについてはサブクラスを作成して true に設定しました )



よくある勘違いですが、DoubleBufferedは子コントロールまで制御しません。
自分の描画だけですので、今回は意味がありません。
重くしてるだけですので、やめたほうがいいでしょう。

引用:

NEXさんの書き込み (2008-04-23 18:45) より:
コントロールの描画イメージを表示されないところで作成しておき、その処理が完了したタイミングで
画面に表示させたいのですが、 Windows の Control を継承したコントロールを使用して
そのようなことができるものなのでしょうか ?



WS_EX_COMPOSITEDというのがありませす。
しかし、適用できるケースが限られています。

引用:

例えばレイアウト A から B に変更する場合、レイアウト B のAにレイアウト A の@の残像が表示されてしまいます。



残像とはなんですか?
表示が更新されずに残ってしまうということですか?
それとも一瞬残ってしまっうということですか?
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2008-04-24 10:46
引用:

Jittaさんの書き込み (2008-04-24 07:46) より:

デザイナが自動生成しているコードを見てください。レイアウト変更中は描画を止めたり、配置が終わったことを通知したりしています。それと同じことをすればいいと思います。


おそらくですが、レイアウト ロジックを止めても描画関係の再描画漏れは改善されないと思います。

引用:

NEXさんの書き込み (2008-04-23 18:45) より:

コントロールの描画イメージを表示されないところで作成しておき、その処理が完了したタイミングで画面に表示させたいのですが、 Windows の Control を継承したコントロールを使用してそのようなことができるものなのでしょうか ?


今回問題となっているのはこれから '表示すべき' Control の描画ではなくて、'消えるべき' Control の描画ではないのでしょうか? 問題解決の手法を見誤っておられる気がします。

安直ですが、レイアウト B の (2) に残ってしまったレイアウト A の (1) の領域のみ強制的に再描画すれば解決するのではと思います。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
NEX
会議室デビュー日: 2008/04/23
投稿数: 5
投稿日時: 2008-04-24 16:24
返答ありがとうございます。

私の説明がわかりにくかったと思いますので、以下の場所に簡単なサンプルを作成して置きました。
速い PC ではわかりにくいと思います。

http://cid-5bee01eee94b636f.skydrive.live.com/self.aspx/%e5%85%ac%e9%96%8b/LayoutTest1.zip

サンプルではメニューをクリックするたびにレアイアウトが A → B → C と変わります。

例えばレイアウト A から B に変わるときに以下のキャプチャ画像のように

http://cid-5bee01eee94b636f.skydrive.live.com/self.aspx/%e5%85%ac%e9%96%8b/capture.png

一瞬見えるのをなんとかしたいということです。

キャプチャ画像をとった PC のスペックは

OS : Windows Vista Ultimate
CPU : Pentium M 1.4 GHz
Memory : 512 MB
エクスペリエンスインデックス : 1.0

※ノート PC です。

すいませんがお知恵をお貸しください、よろしくお願いします。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2008-04-24 18:09
引用:

NEXさんの書き込み (2008-04-24 16:24) より:
返答ありがとうございます。

私の説明がわかりにくかったと思いますので、以下の場所に簡単なサンプルを作成して置きました。
速い PC ではわかりにくいと思います。



たしかにやってみればすぐわかるでしょうが、
私はネットからダウンロードした署名なしファイルを
自由に実行できる環境にはいません。
また、おそらくほとんどの人がそうでしょう。

きちんと文章で説明したほうがよいと思います。
もしくは、exeを信頼できる証明書で署名したらよいでしょう。

引用:

一瞬見えるのをなんとかしたいということです。



切り替え時に一瞬見えるだけですね?
では、どのようなコード、仕組みでレイアウトを切り替えていますか?

#いままでの経験上、
#きちんと説明があれば私はその問題を解決できると思います。
NEX
会議室デビュー日: 2008/04/23
投稿数: 5
投稿日時: 2008-04-24 21:19
れい様、回答ありがとうございます。

配慮が足らず申し訳ありませんでした。

引用:--------------------------------------------------------------------------------
切り替え時に一瞬見えるだけですね?
-------------------------------------------------------------------------------------

はい、その通りです。

引用:--------------------------------------------------------------------------------
では、どのようなコード、仕組みでレイアウトを切り替えていますか?
-------------------------------------------------------------------------------------

サンプルではメニューをクリックする度に以下のメソッドで Form 内のレイアウトを変更しています。

=======================================================================
private void SetLayout(LayoutType type)
{
  SuspendLayout();

  baseLeftPanel.Controls.Clear();
  baseRightPanel.Controls.Clear();
  basePanel.Controls.Clear();

  switch (type)
  {
    case LayoutType.A:
      parts1Panel.Dock = DockStyle.Fill;
      baseLeftPanel.Controls.Add(parts1Panel);

      baseLeftPanel.Dock = DockStyle.Fill;
      basePanel.Controls.Add(baseLeftPanel);
      break;
    case LayoutType.B:
      parts2Panel.Dock = DockStyle.Fill;
      baseRightPanel.Controls.Add(parts2Panel);

      parts3Panel.Dock = DockStyle.Fill;
      baseLeftPanel.Controls.Add(parts3Panel);

      baseLeftVerticalSplitter.Dock = DockStyle.Top;
      baseLeftPanel.Controls.Add(baseLeftVerticalSplitter);

      parts1Panel.Dock = DockStyle.Top;
      baseLeftPanel.Controls.Add(parts1Panel);

      baseRightPanel.Dock = DockStyle.Fill;
      basePanel.Controls.Add(baseRightPanel);

      baseHorizontalSplitter.Dock = DockStyle.Left;
      basePanel.Controls.Add(baseHorizontalSplitter);

      baseLeftPanel.Dock = DockStyle.Left;
      basePanel.Controls.Add(baseLeftPanel);
      break;
    case LayoutType.C:
      parts4Panel.Dock = DockStyle.Fill;
      baseRightPanel.Controls.Add(parts4Panel);

      baseRightVerticalSplitter.Dock = DockStyle.Top;
      baseRightPanel.Controls.Add(baseRightVerticalSplitter);

      parts2Panel.Dock = DockStyle.Top;
      baseRightPanel.Controls.Add(parts2Panel);

      parts3Panel.Dock = DockStyle.Fill;
      baseLeftPanel.Controls.Add(parts3Panel);

      baseLeftVerticalSplitter.Dock = DockStyle.Top;
      baseLeftPanel.Controls.Add(baseLeftVerticalSplitter);

      parts1Panel.Dock = DockStyle.Top;
      baseLeftPanel.Controls.Add(parts1Panel);

      baseRightPanel.Dock = DockStyle.Fill;
      basePanel.Controls.Add(baseRightPanel);

      baseHorizontalSplitter.Dock = DockStyle.Left;
      basePanel.Controls.Add(baseHorizontalSplitter);

      baseLeftPanel.Dock = DockStyle.Left;
      basePanel.Controls.Add(baseLeftPanel);
      break;
    default:
      break;
  }

  ResumeLayout();
}
=======================================================================

よろしくお願いします。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2008-04-25 09:38
引用:

NEXさんの書き込み (2008-04-24 21:19) より:
サンプルではメニューをクリックする度に以下のメソッドで Form 内のレイアウトを変更しています。



まず、描画やコントロールのレイアウトの仕組みがどうなっているのか、
考えましょう。

また、使うコントロールのメソッドやプロパティは、
軽くでいいので目を通したほうがいいでしょう。

それなりに考えて作られているので、
たいていの場合、やりたいことに適した方法があります。

今回の場合、
まずSusupend/ResumeLayoutの使い方を間違っています。

これらは「子コントロールのレイアウトロジックの停止/再開」です。
描画の停止でもありませんし、
孫まで停止するものでもありません。
コンテナコントロールごとにSuspendする必要があります。

何回LayoutEventが起きているのか見てみるとよいと思います。
おそらく、本来1回でいいところ、2〜3回起きているはずです。

また、無駄にDoubleBufferedを設定してはいけません。
メモリを大量に消費しますので、
なるべくfalseに設定します。

無駄なレイアウトをやめ、
パフォーマンスが改善されないか
試すとよいと思います。

コード:
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private int layouttype = 0;
        private void aToolStripMenuItem_Click(object sender, EventArgs e) {
            layouttype = (layouttype + 1) % 3;

            splitContainer1.SuspendLayout();
            splitContainer2.SuspendLayout();
            splitContainer3.SuspendLayout();
            switch (layouttype) {
                case 0:
                    splitContainer1.Panel1Collapsed = false;
                    splitContainer1.Panel2Collapsed = true;
                    splitContainer2.Panel1Collapsed = false;
                    splitContainer2.Panel2Collapsed = true;
                    break;
                case 1:
                    splitContainer1.Panel1Collapsed = false;
                    splitContainer1.Panel2Collapsed = false;
                    splitContainer2.Panel1Collapsed = false;
                    splitContainer2.Panel2Collapsed = false;
                    splitContainer3.Panel1Collapsed = false;
                    splitContainer3.Panel2Collapsed = true;
                    break;
                case 2:
                    splitContainer1.Panel1Collapsed = false;
                    splitContainer1.Panel2Collapsed = false;
                    splitContainer2.Panel1Collapsed = false;
                    splitContainer2.Panel2Collapsed = false;
                    splitContainer3.Panel1Collapsed = false;
                    splitContainer3.Panel2Collapsed = false;
                    break;
            }
            splitContainer3.ResumeLayout();
            splitContainer2.ResumeLayout();
            splitContainer1.ResumeLayout();
        }
    }



私の環境では、これでチラツキはほとんど気になりません。
さらにチラツキを抑えたいなら、
WS_EX_COMPOSITEDを追加する手があります。
ですが、これはTabPageやListView、ComboBoxなどで
描画に問題が発生する場合があります。
また、メモリも消費します。

コード:
        private const Int32 WS_EX_COMPOSITED = 0x2000000;
        protected override CreateParams CreateParams {
            get {
                CreateParams p = base.CreateParams;
                p.ExStyle |= WS_EX_COMPOSITED;
                return p;
            }
        }



コントロールの数を減らし、
適切な方法でレイアウト・描画を行えば
それほど問題にならないはずです。

スキルアップ/キャリアアップ(JOB@IT)