2017-03-09 9 views
6

से हटा दिया जाता है मैं एक ListBox है कि है अपने ItemsSource एक कस्टम वर्ग (ठीक से) एक INotifyCollectionChanged और एक SelectedItem एक ViewModel में एक क्षेत्र के लिए बाध्य लागू करता है करने के लिए बाध्य।निकालें चयन जब चयनित आइटम ListBox

समस्या यह है कि जब मैं वर्तमान में SelectedItemItemsSource संग्रह से हटाता हूं तो यह तुरंत चयन को पड़ोसी आइटम में बदल देता है। अगर मैं सिर्फ चयन हटा देता हूं तो मैं बहुत पसंद करूंगा।

कारण मेरे लिए यह समस्या क्यों है। ItemsSource कक्षा में कुछ अन्य संग्रह से तत्व शामिल हैं जो या तो कुछ (रनटाइम स्थिरता के दौरान) को संतुष्ट करते हैं या Active हैं। Active होने के नाते SelectedItem होने के साथ "सिंक्रनाइज़" है (इसके लिए कारण हैं)। इसलिए ListBox में किसी आइटम को केवल तभी संभव है जब इसे चुना गया हो, जिसका अर्थ यह है कि जब उपयोगकर्ता किसी अन्य का चयन करता है तो इसे गायब होना चाहिए।

मेरे समारोह (गहरी "मॉडल" में) जब SelectedItem बदल जाती है कहा जाता हो जाता है कि:

//Gets old Active item 
var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive); 

//Makes the new Item active (which triggers adding it into `ItemsSource` in case it didn't satisfy the Predicate) 
((PowerSchema)newActiveSchema).IsActive = true; 
//Triggers PropertyChanged on ViewModel with the new Active item 
CurrentSchema = newActiveSchema; 
RaisePropertyChangedEvent(nameof(CurrentSchema)); (#1) 

//Changes the old item so it stops being Active -> gets removed from `ItemsSource` (#2) 
if (oldActiveSchema != null) { ((PowerSchema)oldActiveSchema).IsActive = false; } 

मुद्दा है कि ListBox की एक अद्यतन है कि शुरू हो पाने के लिए चाहिए था SelectedItem के परिवर्तन के कारण किसी कारण से द्वारा (# 1) स्थगित हो जाता है (ListBox को अद्यतन करने के लिए संदेश शायद एक WPF संदेश पाश में समाप्त होता है और वर्तमान गणना समाप्त होने तक वहां प्रतीक्षा करता है)।

ItemsSource से oldActiveSchema को हटाने, दूसरे हाथ पर, तत्काल है और यह भी तुरन्त है कि पुराने एक के बगल में एक एक के लिए SelectedItem का एक परिवर्तन से चलाता है (जब आप चयनित आइटम को हटा दें, एक पड़ोसी के बजाय चयनित हो जाता) । और क्योंकि SelectedItem का परिवर्तन मेरे फ़ंक्शन को ट्रिगर करता है जो CurrentSchema को गलत (पड़ोसी) आइटम पर सेट करता है, यह उपयोगकर्ता द्वारा चयनित CurrentSchema (# 1) को फिर से लिखता है और जब तक ListBox को अपडेट करने के लिए संदेश PropertyChanged चलाता है तो यह इसे अपडेट करता है पड़ोसी एक।

किसी भी मदद की बहुत सराहना की जाती है।


वास्तविक कोड अगर कोई बेहतर जानकारी के लिए करना चाहते हैं:

  • ListBox
  • ViewModel
  • The model's method
  • Callstack जब पड़ोसी आइटम एक उपयोगकर्ता के बजाय SelectedItem के रूप में चयनित हो जाता चुना
    • लाइन 46: SelectedItem उपयोगकर्ता द्वारा चुना एक के रूप में विधि डालता है सक्रिय पाने के लिए चाहिए था
    • लाइन 45: वर्ष SelectedItem बंद हो जाता है सक्रिय किया जा रहा है -> संग्रह से हटा दिया जाता (44-41)
    • लाइन 32: MoveCurrencyOffDeletedElement चाल SelectedItem
    • लाइन 5: SelectedItem एक पड़ोसी एक
+0

चयन को हटाने के लिए केवल आपके व्यूमोडेल में चयनित आईडीम संपत्ति को शून्य करने की आवश्यकता होनी चाहिए।चूंकि समस्या एक स्कीमा परिवर्तन है, बस अपने चुने हुए को स्थानीय चर में संग्रहीत करें, चयनित में सेट करें, और फिर * संग्रह से चयनित आइटम को हटाएं। –

+0

[#WPF] में ड्रॉप करें (https://chat.stackoverflow.com/rooms/18165/wpf) और अगर मैं चैट में सक्रिय नहीं हूं तो मुझे एक पिंग छोड़ दें। मैंने आपके कोड पर एक नज़र डाली थी लेकिन इस मुद्दे को ट्रिगर करने के बारे में तुरंत पता नहीं लगाया जा सकता था। वहां कुछ अन्य सहायक निवासी भी हैं जो निष्क्रिय होने पर हाथ उधार दे सकते हैं। – Maverik

+1

और वास्तव में ब्रैंडन का चयन परिवर्तन का विचार वह है जो मैं भी सोच रहा था। इसके अलावा यदि आप चयनकर्ता को बाध्य करते हैं। यह IsActive के लिए चुना गया है .. आपका चयन स्वचालित आइटम – Maverik

उत्तर

1

निदान

+०१२३५१६४१०६१ करने के लिए बदल जाता है

आपकी समस्याओं की कुंजी यह है कि आप IsSynchronizedWithCurrentItem="True" को अपने ListBox पर सेट करते हैं। यह क्या करता है यह सिंक में ListBox.SelectedItem और ListBox.Items.CurrentItem रखता है। इसके अलावा, ListBox.Items.CurrentItem स्रोत संग्रह के लिए डिफ़ॉल्ट संग्रह दृश्य की ICollectionView.CurrentItem संपत्ति के साथ सिंक्रनाइज़ किया गया है (यह दृश्य आपके मामले में CollectionViewSource.GetDefaultView(Schemas) द्वारा वापस किया गया है)। अब जब आप Schemas संग्रह से कोई आइटम हटाते हैं जो संबंधित संग्रह दृश्य के CurrentItem भी होता है, तो डिफ़ॉल्ट रूप से दृश्य अगले आइटम पर CurrentItem अपडेट करता है (या पिछला एक अगर हटाया गया आइटम अंतिम था, या null यदि संग्रहित आइटम संग्रह में एकमात्र आइटम था)।

समस्या के दूसरे भाग है कि जब ListBox.SelectedItem अपनी व्यू-मॉडल संपत्ति के लिए एक अद्यतन के कारण बदल गया है, अपने RaisePropertyChangedEvent(nameof(ActiveSchema)) के बाद अद्यतन प्रक्रिया समाप्त हो गया है संसाधित किया जाता है, विशेष रूप से नियंत्रण के बाद ActiveSchema से लौटे है सेटर। आप देख सकते हैं कि गेटर तुरंत हिट नहीं होता है, लेकिन सेटटर के बाद ही किया जाता है। क्या महत्वपूर्ण है, Schemas व्यू को भी चयनित चयनित आइटम को प्रतिबिंबित करने के लिए तत्काल अपडेट नहीं किया गया है। दूसरी तरफ, जब आप पहले चयनित आइटम पर IsActive = false सेट करते हैं, तो यह Schemas संग्रह से इस आइटम का तत्काल "निष्कासन" का कारण बनता है, जो बदले में संग्रह दृश्य के CurrentItem का अद्यतन करता है, और श्रृंखला तुरंत अपडेट हो रही है ListBox.SelectedItem। आप देख सकते हैं कि इस बिंदु पर ActiveSchema सेटर फिर से मारा जाएगा। इसलिए पिछले 0% (उपयोगकर्ता द्वारा चुने गए आइटम पर) को समाप्त करने से पहले भी आपके ActiveSchema को फिर से बदला जाएगा (पहले चयनित किसी के बगल में आइटम में)।अपने ListBox पर

# 1

IsSynchronizedWithCurrentItem="False" सेट (या इसे अछूता छोड़):

समाधान

वहाँ इस मुद्दे के समाधान के लिए कई तरीके हैं। यह आपकी समस्या को बिना किसी प्रयास के दूर कर देगा। हालांकि, किसी कारण से यह आवश्यक है, तो किसी भी अन्य समाधान का उपयोग करें।

# 2

एक गार्ड ध्वज का उपयोग करके ActiveSchema स्थापित करने के लिए

रोकें रैत्रांत प्रयास:

bool ignoreActiveSchemaChanges = false; 
public IPowerSchema ActiveSchema 
{ 
    get { return pwrManager.CurrentSchema; } 
    set 
    { 
     if (ignoreActiveSchemaChanges) return; 
     if (value != null && !value.IsActive) 
     { 
      ignoreActiveSchemaChanges = true; 
      pwrManager.SetPowerSchema(value); 
      ignoreActiveSchemaChanges = false; 
     } 
    } 
} 

इस संग्रह को देखने के CurrentItem के लिए स्वचालित अपडेट का कारण होगा आपके व्यू-मॉडल के आधार पर ध्यान नहीं दिया जा करने के लिए , और अंत में ActiveSchema अपेक्षित मूल्य बनाए रखेगा।

# 3

मैन्युअल अद्यतन संग्रह देखने के CurrentItem नव चयनित आइटम के लिए इससे पहले कि आप "निकालें" पहले से एक का चयन किया। आपको MainWindowViewModel.Schemas संग्रह के संदर्भ की आवश्यकता होगी, ताकि आप इसे अपने setNewCurrSchema विधि पर पैरामीटर के रूप में पास कर सकें या किसी प्रतिनिधि में कोड को समाहित कर सकें और पैरामीटर के रूप में पास कर सकें।

PowerManager वर्ग में:

//we pass the action as an optional parameter so that we don't need to update 
//other code that uses this method 
private void setNewCurrSchema(IPowerSchema newActiveSchema, Action action = null) 
{ 
    var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive); 

    ((PowerSchema)newActiveSchema).IsActive = true; 
    CurrentSchema = newActiveSchema; 
    RaisePropertyChangedEvent(nameof(CurrentSchema)); 

    action?.Invoke(); 

    if (oldActiveSchema != null) 
    { 
     ((PowerSchema)oldActiveSchema).IsActive = false; 
    } 
} 

MainWindowViewModel वर्ग में:

public IPowerSchema ActiveSchema 
{ 
    get { return pwrManager.CurrentSchema; } 
    set 
    { 
     if (value != null && !value.IsActive) 
     { 
      var action = new Action(() => 
      { 
       //this will cause a reentrant attempt to set the ActiveSchema, 
       //but it will be ignored because at this point value.IsActive == true 
       CollectionViewSource.GetDefaultView(Schemas).MoveCurrentTo(value); 
      }); 
      pwrManager.SetPowerSchema(value, action); 
     } 
    } 
} 

नोट कि हालांकि इस PresentationFramework विधानसभा के लिए एक संदर्भ की आवश्यकता है मैं सिर्फ दूसरा विकल्प दिखाई देंगे। यदि आप अपनी दृश्य-मॉडल असेंबली में उस निर्भरता को नहीं चाहते हैं, तो आप एक ऐसा ईवेंट बना सकते हैं जो दृश्य द्वारा सब्सक्राइब किया जाएगा और आवश्यक कोड दृश्य द्वारा चलाया जाएगा (जो पहले से ही PresentationFramework असेंबली पर निर्भर करता है)। प्रिज्म 5.0एमएसडीएन पर इस विधि को अक्सर इंटरैक्शन अनुरोध पैटर्न (User Interaction Patterns) अनुभाग के रूप में संदर्भित किया जाता है।

# 4

आस्थगित करें बाध्यकारी अद्यतन जब तक पहले से चयनित आइटम के "हटाने" समाप्त हो गया है। इस कोड को कतार से प्राप्त किया जा सकता का उपयोग कर निष्पादित करने के लिए एक Dispatcher:

private void setNewCurrSchema(IPowerSchema newActiveSchema) 
{ 
    var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive); 

    ((PowerSchema)newActiveSchema).IsActive = true; 
    CurrentSchema = newActiveSchema; 
    RaisePropertyChangedEvent(nameof(CurrentSchema)); 

    if (oldActiveSchema != null) 
    { 
     //queue the code for execution 
     //in case this code is called due to binding update the current dispatcher will be 
     //the one associated with UI thread so everything should work as expected 
     Dispatcher.CurrentDispatcher.InvokeAsync(() => 
     { 
      ((PowerSchema)oldActiveSchema).IsActive = false; 
     }); 
    } 
} 

यह WindowsBase विधानसभा है, जो फिर से विधि # 3 समाधान के लिए वर्णित का उपयोग करके दृश्य-मॉडल विधानसभा में बचा जा सकता है के संदर्भ की आवश्यकता है ।

व्यक्तिगत रूप से मैं समाधान # 1 या # 2 के साथ जाऊंगा क्योंकि यह आपके PowerManager कक्षा को साफ रखता है, और # 3 और # 4 अप्रत्याशित व्यवहार से प्रवण प्रतीत होता है।

+0

धन्यवाद, मेरे पास # 3 और # 4 के समान विचार थे लेकिन इन दोनों समाधानों में मुझे थोड़ा "गंदे" लग रहा था। इसके अलावा मैं वास्तव में UI से संबंधित हैक्स के साथ 'PowerManager' को प्रदूषित नहीं करना चाहता था। # 2 एक अच्छा विचार है। मैंने इस बारे में सोचा नहीं कि यह स्पष्ट रूप से स्पष्ट है। मैं # 1 के साथ जाऊंगा, हालांकि। मुझे गलत समझा जाता है कि 'IsSynchronizedWithCurrentItem = 'क्या करता है और सोचा कि यह मेरे उपयोग के मामले के लिए आवश्यक था। बाहर निकला यह मामला नहीं है। – Petrroll

संबंधित मुद्दे