10

Windows 7 のプログレス バーのバグと思われるものに遭遇しました。バグを示すために、ボタンとプログレス バーを備えた WinForm アプリケーションを作成しました。ボタンの「オンクリック」ハンドルには、次のコードがあります。

private void buttonGo_Click(object sender, EventArgs e)
{
  this.progressBar.Minimum = 0;
  this.progressBar.Maximum = 100;

  this.buttonGo.Text = "Busy";
  this.buttonGo.Update();

  for (int i = 0; i <= 100; ++i)
  {
    this.progressBar.Value = i;
    this.Update();

    System.Threading.Thread.Sleep(10);
  }

  this.buttonGo.Text = "Ready";
}

予想される動作は、プログレス バーが 100% に進み、ボタン テキストが「準備完了」に変わることです。ただし、このコードを Windows 7 で開発しているときに、プログレス バーが約 75% まで上昇し、ボタンのテキストが「準備完了」に変わることに気付きました。コードが同期的であると仮定すると、これは起こらないはずです!

さらにテストを行ったところ、Windows Server 2003 でまったく同じコードを実行すると、期待どおりの結果が得られることがわかりました。さらに、Windows 7 で Aero 以外のテーマを選択すると、期待どおりの結果が得られます。

私の考えでは、これはバグのようです。多くの場合、長い操作に複雑なコードが含まれる場合、進行状況バーを正確にすることは非常に困難ですが、私の特定のケースでは非常に簡単でした。

他の誰かがこの動作に気づきましたか? 誰かが回避策を見つけましたか?

4

7 に答える 7

19

プログレスバーのアニメーションに関係しています。プログレス バーが 0% で 100% に設定されている場合、そこにジャンプすることはありませんが、プログレス バーがスムーズにいっぱいになるようにアニメーション化します。これが遅すぎると、プログレス バーのアニメーションが終了する前に完了してしまいます。したがって、すでに 80、90、100% に設定していても、アニメーションはまだ遅れています。

これをオフにする方法は見つかりませんでしたが、回避策があります。プログレスバーをインクリメントした場合にのみ、アニメーションが実行されます。後方に移動すると、すぐにその位置にジャンプします。したがって、進行状況バーを x% (x != 100) にしたい場合は、それを x+1 に移動してから x に移動します。100% にしたい場合は、100、99、100% に移動します。(または、使用する値が何であれ、アイデアが得られます。)これは、表示されないほど高速に動作し、以前の Windows バージョンでもこのコードを残すことができます (ただし、私はしません)。

于 2010-03-28T00:10:20.110 に答える
4

私も同じ問題を抱えていました。Fozi のヒントは私を助けてくれました。新しい値を設定する前に、値 + 1 を設定しました。これを 100% に対しても機能させるには、前に最大値を増やす必要があります。以下は私にとってはうまくいきました。

if (NewValue < progressBar.Maximum)
{
  progressBar.Value = NewValue + 1;
  progressBar.Value--;
}
else
{
  progressBar.Maximum++;
  progressBar.Value = progressBar.Maximum;
  progressBar.Value--;
  progressBar.Maximum--;
}
于 2011-03-07T15:56:44.273 に答える
3

元の問題は、進行状況バーのタイミングと Win7 (または Aero) のアニメーション メカニズムに関連していると思います。

この Sub は、進行状況バー (pBar) を含むフォームにあります。

バーの .Maximum を変更し、.Value を 10 に固定して 1 ~ 99 の完了率を維持します。バーの .Minimum は設計時に 0 に設定されます。

これで問題は解決しました。

Public Sub UpdateStatusPC(ByVal pc As Integer)

    Try

        If pc < 0 Then
            pBar.Maximum = 100
            pBar.Value = 0
        ElseIf pc > 100 Then
            pBar.Maximum = 100
            pBar.Value = 100
        ElseIf pc = 0 Then
            pBar.Maximum = 10
            pBar.Value = 0
        Else
            pBar.Value = 10
            pBar.Maximum = 10 / CDbl(pc / 100.0)
        End If

        pBar.Update()

    Catch ex As Exception

        MsgBox("UpdateStatusPC: " & ex.Message)

    End Try

End Sub
于 2010-03-27T23:50:13.873 に答える
2

同じ問題に直面している Delphi ユーザーへ: 以下は ProgressBarFix と呼ばれるユニットで、プログレス バー コードの変更を心配することなく、問題に自動的にパッチを適用するために使用できます。 '回避策を自動的に取得します:

unit ProgressBarFix;
(* The standard progress bar fails under Windows theming -- it fails to animate
   all the way to the right side. C.f.,
   http://stackoverflow.com/questions/2217688/windows-7-aero-theme-progress-bar-bug

   To work around the problem, include ProgressBarFix in the interface section's
   "uses" clause *after* ComCtrls (this replaces the TProgressBar definition in
   ConCtrls with the one here, effectively allowing the control defined on the
   form to be replaced with the patch version.

   c.f., http://www.deltics.co.nz/blog/?p=222and http://melander.dk/articles/splitter *)

interface
uses ComCtrls ;

type TProgressBar = class(ComCtrls.TProgressBar)
private
    procedure SetPosition(Value: Integer);
    function GetPosition: Integer;
published
    property Position: Integer read GetPosition write SetPosition default 0;
end ;

implementation

{ TProgressBar }

function TProgressBar.GetPosition: Integer;
begin
    result := inherited Position
end;

procedure TProgressBar.SetPosition(Value: Integer);
begin
    if Value=inherited Position then
        exit ;
    if value<Max then begin
        inherited Position := value+1 ;
        inherited Position := value
    end else begin
        Max := Max+1 ;
        inherited Position := Max ;
        inherited Position := value ;
        Max := Max-1
    end            
end;

end.
于 2012-12-03T20:44:29.443 に答える
1

「パフォーマンス オプション」の視覚効果オプション「ウィンドウ内のコントロールと要素をアニメーション化する」を無効にします。その後、プログレスバーはアニメーション化されなくなります。

于 2010-08-22T18:06:47.573 に答える
0

VistaとWindows7のプログレスバーで同様の問題が発生しました。

私の場合の主な問題は、UIスレッドのブロックでした。(サンプルで行うように)。

Windowsは、メッセージキュー内の新しいメッセージに応答しないアプリケーションを好みません。1つのメッセージに多くの時間を費やすと、Windowsはアプリケーションを「応答なし」としてマークします。Vista / Win7では、Windowsはアプリケーションウィンドウの更新を停止することも決定します。

回避策として、実際の作業をバックグラウンドワーカーに任せるか、たまに電話をかけることができますApplication.DoEvents()。プログレスバーウィンドウがモーダルであることを確認する必要があります。そうでない場合、DoEvents()により、バックグラウンド処理の途中で新しいコマンドの実行が開始される可能性があります。

それがぎこちなく感じる場合、より適切な方法は、BackgroundWorkerスレッドでバックグラウンド作業を行うことです。進行状況バーを更新するためにUIスレッドにイベントを送信するためのサポートが付属しています。

于 2010-02-07T19:33:06.950 に答える
0

(2015 年 9 月) D6 から XE8 にジャンプしました。数々の問題を抱える。この TProgressBar のものを含めます。しばらくテーブルに置いた。今夜、この (Erik Knowles) の修正に出くわしました。素晴らしい。ただし、最初に実行したシナリオの最大値は 9,770,880 でした。そして、それ (Erik Knowles の「元の」修正) は、このプロセスにかかった時間を本当に追加しました (ProgressBar の余分な実際の更新がすべて含まれています)。

そこで、私は彼のクラスを拡張して、ProgressBar が実際に自分自身を再描画する回数を減らしました。ただし、「元の」最大値が MIN_TO_REWORK_PCTS より大きい場合のみ (ここでは 5000 に落ち着きました)。

もしそうなら、ProgressBar はそれ自身を HUNDO 回だけ更新します (ここで私は 100 から始めて、ほぼ 100 に落ち着いたため、「HUNDO」という名前が付けられました)。

Max 値の奇抜さも考慮しました。

if Abs(FOriginalMax - value) <= 1 then
  pct := HUNDO

これをオリジナルの 9.8m Max に対してテストしました。そして、このスタンドアロン テスト アプリを使用すると、次のようになります。

:
uses
  :
  ProgressBarFix;

const
  PROGRESS_PTS = 500001;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    PB: TProgressBar;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  x: integer;
begin
PB.Min := 0;
PB.Max := PROGRESS_PTS;
PB.Position := 0;

for x := 1 to PROGRESS_PTS do
  begin
  //let's do something
  //
  Label1.Caption := Format('%d of %d',[x,PROGRESS_PTS]);
  Update;

  PB.Position := x;
  end;

PB.Position := 0;
end;

end.

PROGRESS_PTS 値: 10 100 1,000 10,000 100,000 1,000,000

これらのすべての値に対してスムーズで「正確」であり、実際に何も遅くすることはありません。

テストでは、コンパイラ ディレクティブ DEF_USE_MY_PROGRESS_BAR を切り替えて、両方の方法をテストすることができました (この TProgressBar の置換と元の TProgressBar の置換)。

Application.ProcessMessages への呼び出しのコメントを外したい場合があることに注意してください。

これが(私の「強化された」)ProgressBarFixソースです:

unit ProgressBarFix;

interface

uses
  Vcl.ComCtrls;

type
  TProgressBar = class(Vcl.ComCtrls.TProgressBar)
  const
    HUNDO = 100;
    MIN_TO_REWORK_PCTS = 5000;
  private
    function  GetMax: integer;
    procedure SetMax(value: integer);
    function  GetPosition: integer;
    procedure SetPosition(value: integer);
  published
    property Max: integer read GetMax write SetMax default 100;
    property Position: integer read GetPosition write SetPosition default 0;

  private
    FReworkingPcts: boolean;
    FOriginalMax:   integer;
    FLastPct:       integer;
  end;

implementation

function TProgressBar.GetMax: integer;
begin
result := inherited Max;
end;

procedure TProgressBar.SetMax(value: integer);
begin
FOriginalMax := value;
FLastPct := 0;

FReworkingPcts := FOriginalMax > MIN_TO_REWORK_PCTS;

if FReworkingPcts then
  inherited Max := HUNDO
else
  inherited Max := value;
end;

function TProgressBar.GetPosition: integer;
begin
result := inherited Position;
end;

procedure TProgressBar.SetPosition(value: integer);
var
  pct: integer;
begin
//Application.ProcessMessages;

if value = inherited Position then
  exit;

if FReworkingPcts then
  begin
  if Abs(FOriginalMax - value) <= 1 then
    pct := HUNDO
  else
    pct := Trunc((value / FOriginalMax) * HUNDO);

  if pct = FLastPct then
    exit;

  FLastPct := pct;

  value := pct;
  end;

if value < Max then
  begin
  inherited Position := Succ(value);
  inherited Position := value;
  end
else
  begin
  Max := Succ(Max);
  inherited Position := Max;
  inherited Position := value;
  Max := Pred(Max);
  end;
end;

end.
于 2015-09-15T10:35:45.443 に答える