WPF में कर्सर बदलना कभी-कभी काम करता है, कभी-कभी नहीं


123

मेरे कई उपयोगकर्ता कॉन्ट्रोक्रोल पर, मैं कर्सर को बदलकर उपयोग करता हूं

this.Cursor = Cursors.Wait;

जब मैं किसी चीज पर क्लिक करता हूं।

अब मैं एक बटन क्लिक पर WPF पेज पर एक ही काम करना चाहता हूं। जब मैं अपने बटन पर मंडराता हूं, तो कर्सर हाथ में बदल जाता है, लेकिन जब मैं इसे क्लिक करता हूं, तो यह प्रतीक्षा कर्सर में नहीं बदलता है। मुझे आश्चर्य है कि अगर यह इस तथ्य के साथ कुछ करना है कि यह एक बटन है, या क्योंकि यह एक पृष्ठ है और एक उपयोगकर्ता नहीं है? यह अजीब व्यवहार लगता है।

जवाबों:


211

क्या आपको कर्सर की "प्रतीक्षा" करने के लिए केवल कर्सर की आवश्यकता है, जब वह उस विशेष पृष्ठ / usercontrol पर हो? यदि नहीं, तो मैं Mouse.OverrideCursor का उपयोग करने का सुझाव दूंगा :

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

यह आपके यूआई के एक हिस्से के बजाय आपके एप्लिकेशन के लिए कर्सर को ओवरराइड करता है, इसलिए आपके द्वारा बताई गई समस्या दूर हो जाती है।


मेरे अपने जवाब के समान , 3 साल बाद (लगभग बिल्कुल!)। मुझे इस प्रश्न के उत्तर पसंद हैं, लेकिन सबसे सरल हमेशा सबसे अधिक आकर्षक है :)
रॉबिन माबेन जूल

यह समाधान कर्सर को "प्रतीक्षा" कर्सर के रूप में बदल देगा लेकिन यह किसी भी आगे के माउस इनपुट को अक्षम नहीं करेगा। मैंने इस समाधान का उपयोग करने की कोशिश की और यद्यपि माउस को प्रतीक्षा कर्सर में बदल दिया, मैं अभी भी किसी भी समस्या के बिना अपने WPF आवेदन के भीतर किसी भी यूआई तत्व पर क्लिक करने में सक्षम हूं। किसी भी विचार मैं वेट कर्सर के दौरान माउस का उपयोग करने से वास्तव में उपयोगकर्ता को कैसे रोक सकता हूं सक्रिय है?
थॉमस हबेर

2
यह जैसा है वैसा ही पुराना है और स्वीकार किया जाता है, यह उचित उत्तर नहीं है। एप्लिकेशन कर्सर को ओवरराइड करना एक नियंत्रण कर्सर को ओवरराइड करने से अलग है (और दूसरे में WPF में सभी समस्याएं हैं)। उदाहरण के लिए, ऐप कर्सर को ओवरराइड करने से बुरा साइड इफेक्ट हो सकता है, उदाहरण के लिए, एक पॉपिंग अप (त्रुटि) संदेश बॉक्स को गलत तरीके से एक ही ओवरराइड कर्सर का उपयोग करने के लिए मजबूर किया जा सकता है, जबकि इरादा केवल ओवरराइड करना था जबकि माउस वास्तविक और सक्रिय नियंत्रण पर मँडरा रहा है।
गार्बर

64

एक तरह से हम अपने आवेदन में ऐसा करते हैं कि आईडीआईजॉरी का उपयोग कर रहे हैं और फिर 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...
}

जब या तो ओवरराइड समाप्त हो जाएगी: उपयोग कथन का अंत या पहुंच गया है; यदि अपवाद को फेंक दिया जाता है और नियंत्रण कथन के अंत से पहले स्टेटमेंट ब्लॉक छोड़ देता है।

अपडेट करें

कर्सर फ़्लिकरिंग को रोकने के लिए आप यह कर सकते हैं:

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
उपयोग भाग के साथ अच्छा समाधान। मैंने वास्तव में हमारी कुछ परियोजनाओं में (स्टैक के बिना, वही) लिखा है। उपयोग में आप एक चीज को सरल बना सकते हैं, बस यह लिखना है: (नया ओवरराइडकोरसर (Cursors.Wait)) का उपयोग करके {// do stuff} के बजाय इसे वैरिएबल असाइन करने के बजाय आप शायद उपयोग नहीं करेंगे।
ओली

1
जरूरत नहीं। यदि आप इसे सेट Mouse.OverrideCursorकरते हैं nullतो परेशान है और अब सिस्टम कर्सर को ओवरराइड नहीं करता है। यदि मैं सीधे वर्तमान कर्सर को संशोधित कर रहा था (यानी ओवरराइडिंग नहीं) तो कोई समस्या हो सकती है।
डेनिस

2
यह अच्छा है, लेकिन यह सुरक्षित नहीं है यदि एक ही समय में कई दृश्य कर्सर को अपडेट कर रहे हैं। रेस की स्थिति में आना आसान है जहां ViewA का कर्सर सेट होता है, फिर ViewB एक अलग सेट करता है, फिर ViewA अपने कर्सर को रीसेट करने की कोशिश करता है (जो तब ViewB के स्टैक को पॉप करता है और ViewA के कर्सर को सक्रिय छोड़ देता है)। जब तक ViewB अपने कर्सर को रीसेट नहीं करता, तब तक चीजें वापस सामान्य हो जाती हैं।
साइमन गिल्बी

2
@SimonGillbee जो वास्तव में संभव है - यह एक समस्या नहीं थी जो मैंने 10yrs पहले की थी जब मैंने यह लिखा था। यदि आपको कोई समाधान मिलता है, तो शायद, एक का उपयोग करके ConcurrentStack<Cursor>, उपरोक्त उत्तर को संपादित करने या अपना खुद का जोड़ने के लिए स्वतंत्र महसूस करें।
डेनिस

2
@ डेनिस वास्तव में मैंने कुछ दिन पहले लिखा था (यही वजह है कि मैं एसओ को देख रहा था)। मैं कॉनकॉन्ट्रैकस्टैक के साथ खेला, लेकिन यह गलत संग्रह निकला। ढेर केवल आपको शीर्ष पर पॉप करने की अनुमति देता है। इस स्थिति में आप स्टैक के बीच से हटाना चाहते हैं यदि उस कर्सर को स्टैक के शीर्ष से पहले निपटाया जाता है। मैं समवर्ती पहुँच को नियंत्रित करने के लिए ReaderWriterLockSlim के साथ सूची <T> का उपयोग करके समाप्त हुआ।
साइमन गिल्बेई

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
मैं या तो नीचे नहीं मिलता है। यह उत्तर तब उपयोगी होता है जब आप एमवीवीएम (कोई कोड-पीछे नहीं) का उपयोग कर रहे हों और विशिष्ट नियंत्रण के लिए कर्सर को नियंत्रित करना चाहते हों। बहुत उपयोगी।
साइमन गिल्बी

4
मैं MVVM के लाभों का लाभ उठा रहा हूं और यह सही उत्तर है।
g1ga

मुझे यह समाधान पसंद है क्योंकि मेरा मानना ​​है कि यह MVVM, व्यूमॉडल्स, आदि के साथ बेहतर काम करेगा
Rod

इस कोड के साथ मुझे जो समस्या दिख रही है, वह यह है कि कर्सर केवल "वेट" है, जबकि माउस बटन के ऊपर है, लेकिन जैसे ही आप माउस को बाहर निकालते हैं, यह "एरो" हो जाता है।
स्पाइडरमैन

7

यदि आपका एप्लिकेशन एसिंक्स सामान का उपयोग करता है और आप माउस के कर्सर के साथ फ़िडलिंग कर रहे हैं, तो आप शायद इसे केवल मुख्य UI थ्रेड में करना चाहते हैं। आप इसके लिए ऐप के डिस्पैचर धागे का उपयोग कर सकते हैं:

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.