İmleci WPF'de değiştirmek bazen işe yarıyor, bazen işe yaramıyor


123

Kullanıcı kontrollerimin birçoğunda imleci kullanarak değiştiriyorum

this.Cursor = Cursors.Wait;

bir şeye tıkladığımda.

Şimdi aynı şeyi bir WPF sayfasında bir düğmeye tıklayarak yapmak istiyorum. Düğmemin üzerine geldiğimde imleç bir ele dönüşüyor, ancak tıkladığımda bekleme imlecine dönüşmüyor. Bunun bir düğme olmasıyla mı ilgisi var acaba, yoksa bu bir sayfa ve kullanıcı denetimi değil mi? Bu garip bir davranış gibi görünüyor.

Yanıtlar:


211

İmlecin, yalnızca o belirli sayfanın / kullanıcı kontrolünün üzerindeyken "bekleme" imleci olmasına mı ihtiyacınız var? Değilse, Mouse.OverrideCursor'ı kullanmanızı öneririm :

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

Bu, yalnızca kullanıcı arayüzünün bir parçası yerine uygulamanız için imleci geçersiz kılar, böylece açıkladığınız sorun ortadan kalkar.


Kendi cevabıma benzer şekilde , 3 yıl sonrasına ait (neredeyse aynen!). Bu sorunun cevaplarını beğendim ama en basit olanı her zaman en cezbedici olanıdır :)
Robin Maben

Bu çözüm, imleci bir "bekleme" imleci olacak şekilde değiştirecek, ancak daha fazla fare girişini devre dışı bırakmayacaktır. Bu çözümü kullanmayı denedim ve farenin bekleme imlecine dönüşmesine rağmen WPF uygulamamdaki herhangi bir UI öğesini sorunsuz bir şekilde tıklayabiliyorum. Bekleme imleci etkinken kullanıcının fareyi kullanmasını nasıl engelleyebileceğime dair bir fikriniz var mı?
Thomas Huber

2
Eski olduğu ve olduğu gibi kabul edildiği için doğru cevap bu DEĞİLDİR. Uygulama imlecini geçersiz kılmak, bir kontrol imlecini geçersiz kılmaktan farklıdır (ve ikincisinde WPF'de sorun var). Uygulama imlecini geçersiz kılmak kötü yan etkilere neden olabilir; örneğin, bir açılır (hata) mesaj kutusu, aynı geçersiz kılınan imleci yanlışlıkla kullanmaya zorlanabilirken, amaç yalnızca fare gerçek ve etkin kontrolün üzerine gelirken geçersiz kılmaktır.
Gábor

64

Bunu uygulamamızda yapmamızın bir yolu IDisposable kullanmak ve ardından using(){}imlecin tamamlandığında sıfırlandığından emin olmak için bloklar kullanmaktır.

public class OverrideCursor : IDisposable
{

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

  #region IDisposable Members

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

  #endregion
}

ve sonra kodunuzda:

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

Geçersiz kılma şunlardan biri olduğunda sona erecektir: using ifadesinin sonuna ulaşıldığında veya; bir istisna atılırsa ve kontrol, ifadenin sonundan önce ifade bloğunu bırakır.

Güncelleme

İmlecin titremesini önlemek için şunları yapabilirsiniz:

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
Kullanım kısmı ile güzel çözüm. Aslında bazı projelerimizde aynı şeyi yazdım (yığın olmadan, yani). Kullanımı basitleştirebileceğiniz bir şey, muhtemelen kullanmayacağınız bir değişken atamak yerine (new OverrideCursor (Cursors.Wait)) {// do stuff} kullanarak yazmaktır.
Olli

1
Gerekli değil. Eğer ayarlarsanız Mouse.OverrideCursoriçin nullkendisine ayarlanmazsa ve artık geçersiz kılma sistem imleç. EĞER ben doğrudan geçerli imleç değiştirerek edildi (yani geçersiz kılma değil) daha sonra bir sorun olabilir.
Dennis

2
Bu güzel, ancak aynı anda birden fazla görünüm imleci güncelliyorsa güvenli değildir. ViewA kümesinin imlecinin bulunduğu, ardından ViewB'nin farklı bir tane ayarladığı, ardından ViewA'nın imlecini sıfırlamaya çalıştığı bir yarış durumuna kolayca girilebilir (bu, daha sonra ViewB'yi yığından çıkarır ve ViewA'nın imlecini etkin bırakır). ViewB imlecini sıfırlayana kadar işler normale dönmez.
Simon Gillbee

2
@SimonGillbee bu gerçekten mümkün - 10 yıl önce bunu yazarken yaşadığım bir sorun değildi. Bir çözüm bulursanız, belki a kullanarak ConcurrentStack<Cursor>, yukarıdaki cevabı düzenlemekten veya kendi cevabınızı eklemekten çekinmeyin.
Dennis

2
@Dennis Bunu birkaç gün önce yazdım (bu yüzden SO'ya bakıyordum). ConcurrentStack ile oynadım ama yanlış koleksiyon olduğu ortaya çıktı. Yığın yalnızca yukarıdan çıkmanıza izin verir. Bu durumda, imleç yığının üst kısmı atılmadan önce yerleştirilmişse, yığının ortasından çıkarmak istersiniz. Eşzamanlı erişimi yönetmek için ReaderWriterLockSlim ile List <T> kullanmaya son verdim.
Simon Gillbee

38

Bekleme imlecini etkinleştirmek için düğme üzerinde bir veri tetikleyicisi (görünüm modeliyle) kullanabilirsiniz.

<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>

İşte görünüm modelindeki kod:

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
Ben de olumsuz oy alamıyorum. Bu cevap, MVvM kullanırken (yani arka planda kod yok) ve belirli bir kontrol için imleci kontrol etmek istediğinizde kullanışlıdır. Çok kullanışlı.
Simon Gillbee

4
MVVM'nin avantajlarından yararlanıyorum ve bu mükemmel bir cevap.
g1ga

MVVM, görünüm modelleri vb. İle daha iyi çalışacağına inandığım için bu çözümü seviyorum.
Rod

Bu kodda gördüğüm sorun, imleçlerin yalnızca fare düğmenin üzerindeyken "Bekle" olması, ancak fareyi dışarı doğru hareket ettirdiğinizde bir "Ok" haline gelmesidir.
örümcek adam

7

Uygulamanız eşzamansız şeyler kullanıyorsa ve Mouse'un imleciyle uğraşıyorsanız, muhtemelen bunu yalnızca ana UI iş parçacığında yapmak istersiniz. Bunun için uygulamanın Dispatcher iş parçacığını kullanabilirsiniz:

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

0

Aşağıdakiler benim için çalıştı:

ForceCursor = true;
Cursor = Cursors.Wait;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.