在WPF中更改光标有时会起作用,有时则不然

Sco*_*ttG 119 c# wpf

在我的几个用户控件中,我使用了更改光标

this.Cursor = Cursors.Wait;
Run Code Online (Sandbox Code Playgroud)

当我点击某些东西时.

现在我想在按钮点击的WPF页面上做同样的事情.当我将鼠标悬停在我的按钮上时,光标会变为一只手,但是当我单击它时,它不会更改为等待光标.我想知道这是否与它是一个按钮,或者因为这是一个页面而不是用户控件这一事实有关?这看似奇怪的行为.

Mat*_*ton 204

只有当光标位于特定页面/ usercontrol上时,您是否需要将光标作为"等待"光标?如果没有,我建议使用Mouse.OverrideCursor:

Mouse.OverrideCursor = Cursors.Wait;
try
{
    // do stuff
}
finally
{
    Mouse.OverrideCursor = null;
}
Run Code Online (Sandbox Code Playgroud)

这会覆盖应用程序的光标而不仅仅是其UI的一部分,因此您描述的问题就会消失.

  • 尽管它是旧的并且已经被接受,但这不是正确的答案。覆盖应用程序光标与覆盖控制光标不同(第二个在 WPF 中存在问题)。覆盖应用程序光标可能会产生令人讨厌的副作用,例如,弹出(错误)消息框可能会被迫错误地使用相同的覆盖光标,而意图只是在鼠标悬停在实际和活动控件上时覆盖。 (3认同)

Den*_*nis 60

我们在应用程序中执行此操作的一种方法是使用IDisposable然后使用using(){}块来确保光标在完成后重置.

public class OverrideCursor : IDisposable
{

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

  #region IDisposable Members

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

  #endregion
}
Run Code Online (Sandbox Code Playgroud)

然后在你的代码中:

using (OverrideCursor cursor = new OverrideCursor(Cursors.Wait))
{
  // Do work...
}
Run Code Online (Sandbox Code Playgroud)

覆盖将在以下任何一个结束时结束:到达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;
  }

}
Run Code Online (Sandbox Code Playgroud)

  • 使用部分的好解决方案.实际上我在一些项目中写的完全相同(没有堆栈,就是这样).你可以简化用法的一件事就是写:using(new OverrideCursor(Cursors.Wait)){// do stuff}而不是为它分配一个你可能不会使用的变量. (2认同)
  • 这很好,但如果多个视图同时更新光标,则不安全。很容易进入竞争条件,其中 ViewA 设置光标,然后 ViewB 设置不同的光标,然后 ViewA 尝试重置其光标(然后从堆栈中弹出 ViewB 并使 ViewA 的光标保持活动状态)。直到 ViewB 重置其光标才会恢复正常。 (2认同)
  • @SimonGillbee 这确实是可能的——这不是我 10 年前写这篇文章时遇到的问题。如果你找到了一个解决方案,也许使用`ConcurrentStack&lt;Cursor&gt;`,可以随意编辑上面的答案或添加你自己的答案。 (2认同)
  • @Dennis 我实际上是在几天前写的(这就是我通过 SO 寻找的原因)。我玩过 ConcurrentStack,但结果是错误的集合。堆栈只允许您从顶部弹出。在这种情况下,如果在处理堆栈顶部之前处理该光标,则您希望从堆栈中间删除。我最终只使用 List&lt;T&gt; 和 ReaderWriterLockSlim 来管理并发访问。 (2认同)

Zam*_*oni 37

您可以在按钮上使用数据触发器(使用视图模型)来启用等待光标.

<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>
Run Code Online (Sandbox Code Playgroud)

以下是视图模型中的代码:

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;
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 请为您的投票添加评论,以帮助我理解原因.我多次使用此代码并获得了良好的效果. (5认同)
  • 我也没有得到downvote.当您使用MVvM(因此没有代码隐藏)并且想要控制特定控件的光标时,这个答案很有用.很有用. (4认同)
  • 我正在利用MVVM的优势,这是完美的答案. (4认同)

Val*_*ean 6

如果您的应用程序使用异步内容并且您正在摆弄鼠标光标,那么您可能只想在主UI线程中执行此操作.你可以使用app的Dispatcher线程:

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