

お世話になります。

C#にて、配置したListBoxをオーナードローさせることで、簡単なアニメーションを  
させようとしています。現在選択中のセルの中にのみアニメーションをさせればと  
やっていますが、TimerでRefreshをさせるたびにListBoxがちらついてしまいます。

当然と言えば当然なのですが、そのあとDoubleBufferedをTrueにしてみたり  
OnPaintBackgroundを空でオーバーライドしてみたりやってみたのですが、  
どうしてもちらつきが出てしまいます。

ちらつきをせずにアニメーションをさせる方法はありますでしょうか。

下記がそのコードです。

[code]

    public class AnimeList : ListBox
    {
        private Timer timer = new Timer();
    
        private int iXpos = 0;
    
        public TunesList()
        {
            this.ScrollAlwaysVisible = true;
            this.Font = new Font("メイリオ", 8, FontStyle.Regular);
            this.ItemHeight = 20;
            this.DrawMode = DrawMode.OwnerDrawFixed;
            this.DrawItem += new DrawItemEventHandler(this.listBox1_DrawItem);
            this.DoubleBuffered = true;
    
            this.timer.Interval = 100;
            this.timer.Tick += new System.EventHandler(this.timer1_Tick);
            this.timer.Enabled = true;
        }
        /// <summary>
        ///     セルの描画
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (this.DesignMode)
            {
                return;
            }
            //ListBoxが空のときにListBoxが選択されるとe.Indexが-1になる
            if (e.Index < 0)
            {
                return;
            }
    
            //描画する文字列の取得
            string s = ((ListBox)sender).Items[e.Index].ToString();
    
            e.DrawBackground();
    
    
            Brush b = null;
    
            //  選択されていないセル
            if ((e.State & DrawItemState.Selected) != DrawItemState.Selected)
            {
                e.Graphics.FillRectangle(Brushes.Black, e.Bounds);
                b = new SolidBrush(Color.White);
                //文字列の描画
                e.Graphics.DrawString(s, this.Font, b, e.Bounds.X + 4, e.Bounds.Y);
            }
    
            //  選択中のセル
            else
            {
                e.Graphics.FillRectangle(Brushes.White, e.Bounds);
                b = new SolidBrush(Color.Black);
                //文字列の描画
                e.Graphics.DrawString(s, this.Font, b, e.Bounds.X + 4 + this.iXpos, e.Bounds.Y);
            }
    
            //後始末
            b.Dispose();
    
            //フォーカスを示す四角形を描画
            e.DrawFocusRectangle();
    
    
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            this.iXpos -= 2;
            if(this.iXpos < -128)
            {
                this.iXpos = 0;
            }
            this.Refresh();
        }
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            // 何もしない
        }
    }
    
[/code]

以上、よろしくお願いいたします。


CC BY-SA 3.0
2016-11-29T08:35:52.540
30767
2017-03-09T04:17:04.167
2016-11-29T11:17:36.167
8000
9374
question
c# winforms
オーナードローを使ってアニメーションさせると表示がちらついてしまう


とりあえず元の方針に沿ってちらつきを軽減するためには更新範囲と描画回数を押さえてください。

  1. 背景の描画に`OnPaintBackground`を使用する。

    * `OnPaintBackground`のオーバーライドを削除
    * `e.Graphics.FillRectangle(Brushes.Black, e.Bounds);`を削除
  2. 更新範囲を指定する。

`this.Refresh()`ではなく`Invalidate(Rectangle)`を使用して更新範囲を明示する。

[code]

    private int[] _previousSelection = { };
    
    private void timer1_Tick(object sender, EventArgs e)
    {
        this.iXpos -= 2;
        if (this.iXpos < -128)
        {
            this.iXpos = 0;
        }
    
        var selection = SelectedIndices.Cast<int>().ToArray();
    
        // 選択行と直前まで選択されていた行を再描画する
        foreach (var i in selection.Union(_previousSelection))
        {
            Invalidate(new Rectangle(0, i * ItemHeight, Width, ItemHeight));
        }
        _previousSelection = selection;
    }
    
[/code]

しかし上記の修正は「ちらつく範囲を局限する」ものです。`ListBox`の`Graphics`に描画する方針であれば多少のちらつきは発生すると思います。

根本的に解決するためには`ListBox`を捨ててWPF相互運用を行うか、Windowsフォームコントロールを組み合わせて同じようなデザインを作成し、`Location`を変化させて同様のアニメーションを行うのが良いと思います。


CC BY-SA 3.0
2016-11-30T06:44:12.620
30788
2016-11-30T06:44:12.620
5750
30767
answer