WPFでカーソルを変更すると機能する場合と機能しない場合がある


123

いくつかのユーザーコントロールで、次のコマンドを使用してカーソルを変更します

this.Cursor = Cursors.Wait;

何かをクリックすると

次に、ボタンをクリックしてWPFページで同じことを行います。ボタンの上にカーソルを置くと、カーソルが手の形に変わりますが、クリックすると、待機カーソルに変わりません。これがボタンであるという事実と関係があるのか​​、またはこれはページでありユーザーコントロールではないためでしょうか?これは奇妙な動作のようです。

回答:


211

特定のページ/ユーザーコントロール上にある場合にのみ、カーソルを「待機」カーソルにする必要がありますか?そうでない場合は、Mouse.OverrideCursorを使用することを勧めします

Mouse.OverrideCursor = Cursors.Wait;
try
{
    // do stuff
}
finally
{
    Mouse.OverrideCursor = null;
}

これは、UIの一部だけではなく、アプリケーションのカーソルをオーバーライドするため、説明している問題はなくなります。


私自身の回答と同様に、3年後の日付です(ほぼ正確です)。私はこの質問の答えが好きですが、最も単純なのは常に最も魅力的です:)
Robin Maben 2012

このソリューションは、カーソルを「待機」カーソルに変更しますが、それ以上のマウス入力を無効にしません。このソリューションを試してみましたが、マウスが待機カーソルに変わっても、WPFアプリケーション内の任意のUI要素を問題なくクリックできます。待機カーソルがアクティブなときにユーザーが実際にマウスを使用できないようにする方法はありますか?
Thomas Huber

2
古くてそのまま受け入れられているのは、正しい答えではありません。アプリカーソルのオーバーライドは、コントロールカーソルのオーバーライドとは異なります(2番目のカーソルはWPFで問題が発生します)。アプリカーソルをオーバーライドすると、厄介な副作用が発生する可能性があります。たとえば、ポップアップ(エラー)メッセージボックスは、オーバーライドされた同じカーソルを誤って使用するように強制されることがありますが、実際のアクティブなコントロールの上にマウスが置かれているときにオーバーライドすることが意図されていました。
ガーボル

64

アプリケーションでこれを行う1つの方法は、IDisposableを使用してから、using(){}ブロックを使用して、完了時にカーソルが確実にリセットされるようにすることです。

public class OverrideCursor : IDisposable
{

  public OverrideCursor(Cursor changeToCursor)
  {
    Mouse.OverrideCursor = changeToCursor;
  }

  #region IDisposable Members

  public void Dispose()
  {
    Mouse.OverrideCursor = null;
  }

  #endregion
}

そしてあなたのコードで:

using (OverrideCursor cursor = new OverrideCursor(Cursors.Wait))
{
  // Do work...
}

オーバーライドは、usingステートメントの最後に達したとき、または 例外がスローされ、制御がステートメントブロックの前にステートメントの終了前に残った場合。

更新

カーソルのちらつきを防ぐには、次のようにします。

public class OverrideCursor : IDisposable
{
  static Stack<Cursor> s_Stack = new Stack<Cursor>();

  public OverrideCursor(Cursor changeToCursor)
  {
    s_Stack.Push(changeToCursor);

    if (Mouse.OverrideCursor != changeToCursor)
      Mouse.OverrideCursor = changeToCursor;
  }

  public void Dispose()
  {
    s_Stack.Pop();

    Cursor cursor = s_Stack.Count > 0 ? s_Stack.Peek() : null;

    if (cursor != Mouse.OverrideCursor)
      Mouse.OverrideCursor = cursor;
  }

}

2
使用部分での素晴らしい解決策。私は実際にいくつかのプロジェクトでまったく同じことを書きました(スタックなしで、つまり)。使用法を簡略化できる1つのことは、おそらく使用しない変数を割り当てる代わりに、(new OverrideCursor(Cursors.Wait)){// do stuff}を使用することだけです。
Olli

1
必要ありません。これに設定Mouse.OverrideCursorするnullと、設定が解除され、システムカーソルがオーバーライドされなくなります。IF I(すなわち上書きしない)直接現在のカーソルを変更した後、問題がある可能性があります。
デニス

2
これは便利ですが、複数のビューが同時にカーソルを更新している場合は安全ではありません。ViewAがカーソルを設定し、次にViewBが別のカーソルを設定し、ViewAがカーソルをリセットしようとする(これにより、ViewBがスタックからポップされ、ViewAのカーソルがアクティブなままになります)。ViewBがカーソルをリセットするまで、物事は通常に戻りません。
Simon Gillbee、

2
@SimonGillbee確かに可能です-これを書いたとき、私が10年前に持っていたのは問題ではありませんでした。おそらくを使用して解決策を見つけた場合はConcurrentStack<Cursor>、上記の回答を編集するか、独自の回答を追加してください。
デニス

2
@デニス私は実際に数日前にこれを書いた(それが私がSOを探していた理由です)。私はConcurrentStackで遊んでみましたが、間違ったコレクションであることがわかりました。スタックでは、上部からポップすることのみが可能です。この場合、スタックの一番上が破棄される前にカーソルが破棄される場合は、スタックの真ん中から削除します。最終的に、ReaderWriterLockSlimでList <T>を使用して、同時アクセスを管理しました。
Simon Gillbee、

38

ボタンでデータトリガー(ビューモデルを使用)を使用して、待機カーソルを有効にすることができます。

<Button x:Name="NextButton"
        Content="Go"
        Command="{Binding GoCommand }">
    <Button.Style>
         <Style TargetType="{x:Type Button}">
             <Setter Property="Cursor" Value="Arrow"/>
             <Style.Triggers>
                 <DataTrigger Binding="{Binding Path=IsWorking}" Value="True">
                     <Setter Property="Cursor" Value="Wait"/>
                 </DataTrigger>
             </Style.Triggers>
         </Style>
    </Button.Style>
</Button>

ビューモデルのコードは次のとおりです。

public class MainViewModel : ViewModelBase
{
   // most code removed for this example

   public MainViewModel()
   {
      GoCommand = new DelegateCommand<object>(OnGoCommand, CanGoCommand);
   }

   // flag used by data binding trigger
   private bool _isWorking = false;
   public bool IsWorking
   {
      get { return _isWorking; }
      set
      {
         _isWorking = value;
         OnPropertyChanged("IsWorking");
      }
   }

   // button click event gets processed here
   public ICommand GoCommand { get; private set; }
   private void OnGoCommand(object obj)
   {
      if ( _selectedCustomer != null )
      {
         // wait cursor ON
         IsWorking = true;
         _ds = OrdersManager.LoadToDataSet(_selectedCustomer.ID);
         OnPropertyChanged("GridData");

         // wait cursor off
         IsWorking = false;
      }
   }
}

4
反対票ももらいません。この回答は、MVvM(分離コードがないため)を使用していて、特定のコントロールのカーソルを制御する場合に役立ちます。非常に便利。
Simon Gillbee、

4
私はMVVMの利点を活用していますが、これは完璧な答えです。
g1ga 2012

私、私はそれがMVVMなど、のviewmodels、とのより良い仕事と考えているように、このソリューションのように
ロッド

このコードで発生する問題は、マウスがボタンの上にある間だけカーソルが「待機」することですが、マウスを外側に移動すると、「矢印」に戻ります。
スパイダーマン2018年

7

アプリケーションが非同期のものを使用していて、マウスのカーソルをいじっている場合は、おそらくメインUIスレッドでのみ実行する必要があります。そのためにアプリのDispatcherスレッドを使用できます。

Application.Current.Dispatcher.Invoke(() =>
{
    // The check is required to prevent cursor flickering
    if (Mouse.OverrideCursor != cursor)
        Mouse.OverrideCursor = cursor;
});

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.