दृष्टिकोण मैं एक BackgroundWorker में फ़ॉर्मेटर तर्क को चलाने के लिए था ले लिया। मैंने इसे चुना क्योंकि प्रारूप में "लंबा" समय लगेगा, 1 सेकंड या दो से अधिक, इसलिए मैं इसे यूआई थ्रेड पर नहीं कर सका।
बस समस्या को पुन: स्थापित करने के लिए: BackgroundWorker द्वारा RichTextBox पर सेटटर पर किए गए प्रत्येक कॉल। चयनकॉलर ने टेक्स्ट चेंजेड ईवेंट को फिर से निकाल दिया, जो फिर से बीजी थ्रेड शुरू कर देगा। टेक्स्ट चेंजेड इवेंट के भीतर, मुझे "प्रोग्राम ने टेक्स्ट टाइप किया है" ईवेंट से "उपयोगकर्ता ने कुछ टाइप किया है" ईवेंट को अलग करने का कोई तरीका नहीं ढूंढ पाया। तो आप देख सकते हैं कि यह परिवर्तनों की अनंत प्रगति होगी।
सरल दृष्टिकोण काम नहीं करता
एक आम दृष्टिकोण (as suggested by Eric) करने के लिए "अक्षम" पाठ परिवर्तन करते हुए पाठ परिवर्तन हैंडलर के भीतर चल निपटने घटना है।लेकिन निश्चित रूप से यह मेरे मामले के लिए काम नहीं करेगा, क्योंकि टेक्स्ट बदलता है (चयन रंग परिवर्तन) पृष्ठभूमि थ्रेड द्वारा उत्पन्न किया जा रहा है। वे टेक्स्ट चेंज हैंडलर के दायरे में नहीं किए जा रहे हैं। तो उपयोगकर्ता द्वारा शुरू की गई घटनाओं को फ़िल्टर करने का सरल दृष्टिकोण मेरे मामले के लिए काम नहीं करेगा, जहां पृष्ठभूमि धागा परिवर्तन कर रहा है।
अन्य उपयोगकर्ता द्वारा शुरू किए परिवर्तन
मैं एक तरह से द्वारा किए गए richtextbox में बदलाव से मेरी फ़ॉर्मेटर धागा से होने वाले richtextbox में परिवर्तन भेद करने के लिए के रूप में उपयोग करने की कोशिश RichTextBox.Text.Length पता लगाने का प्रयास उपभोक्ता। यदि लंबाई बदल नहीं गई थी, तो मैंने तर्क दिया, तो परिवर्तन मेरे कोड द्वारा एक प्रारूप परिवर्तन किया गया था, न कि उपयोगकर्ता संपादन। लेकिन RichTextBox को पुनर्प्राप्त करना। टेक्स्ट संपत्ति महंगी है, और प्रत्येक टेक्स्ट चेंज ईवेंट के लिए ऐसा करने से पूरे यूआई को अस्वीकार्य रूप से धीमा कर दिया जाता है। यहां तक कि यदि यह पर्याप्त तेज़ था, तो यह सामान्य मामले में काम नहीं करता है, क्योंकि उपयोगकर्ता स्वरूप परिवर्तन भी करते हैं। और, एक उपयोगकर्ता संपादन एक ही लंबाई पाठ का उत्पादन कर सकता है, अगर यह एक प्रकार का ऑपरेशन था।
मैं उपयोगकर्ता से उत्पन्न परिवर्तनों का पता लगाने के लिए केवल टेक्स्ट चेंज ईवेंट को पकड़ने और संभालने की उम्मीद कर रहा था। चूंकि मैं ऐसा नहीं कर सका, मैंने ऐप को कीप्रेस ईवेंट और पेस्ट इवेंट का उपयोग करने के लिए बदल दिया। नतीजतन अब मुझे स्वरूपण परिवर्तनों के कारण नकली TextChange घटनाएं नहीं मिलती हैं (जैसे RichTextBox.SelectionColor = Color.Blue)।
अपने कार्य
ठीक करने के लिए कार्यकर्ता धागा सिग्नलिंग, मैं एक धागा चल रहा है कि स्वरूपण परिवर्तन कर सकते हैं मिल गया है। संकल्पनात्मक रूप से, यह यह करता है:
while (forever)
wait for the signal to start formatting
for each line in the richtextbox
format it
next
next
मैं बीजी थ्रेड को स्वरूपण शुरू करने के लिए कैसे कह सकता हूं?
मैंने ManualResetEvent का उपयोग किया। जब एक कुंजीपटल का पता चला है, तो कुंजीपटल हैंडलर उस ईवेंट को सेट करता है (इसे चालू करता है)। पृष्ठभूमि कार्यकर्ता एक ही घटना पर इंतजार कर रहा है। जब यह चालू होता है, बीजी थ्रेड इसे बंद कर देता है, और स्वरूपण शुरू करता है।
लेकिन अगर बीजी कार्यकर्ता पहले से ही स्वरूपण है तो क्या होगा? उस स्थिति में, एक नई कीप्रेस ने टेक्स्टबॉक्स की सामग्री को बदल दिया हो सकता है, और अब तक किए गए किसी भी स्वरूपण को अब अमान्य हो सकता है, इसलिए प्रारूपण को पुनरारंभ करना आवश्यक है। क्या मैं सच में फ़ॉर्मेटर थ्रेड के लिए चाहते हैं कुछ इस तरह है:
while (forever)
wait for the signal to start formatting
for each line in the richtextbox
format it
check if we should stop and restart formatting
next
next
इस तर्क के साथ
, जब ManualResetEvent सेट किया गया है (चालू), फ़ॉर्मेटर धागा पता लगाता है कि, और फिर सेट करता है यह (यह बंद कर देता है) , और स्वरूपण शुरू होता है। यह पाठ के माध्यम से चलता है और यह तय करता है कि इसे कैसे प्रारूपित किया जाए। समय-समय पर फॉर्मेटर थ्रेड मैन्युअल रीसेट इवेंट को फिर से जांचता है। यदि स्वरूपण के दौरान एक और कीप्रेस घटना होती है, तो ईवेंट फिर से संकेतित स्थिति पर जाता है। जब फॉर्मेटर देखता है कि यह फिर से संकेत दिया गया है, तो फॉर्मेटर बाहर निकलता है और पाठ की शुरुआत से फिर से स्वरूपण शुरू करता है, जैसे सिसिफस। एक और बुद्धिमान तंत्र दस्तावेज़ में बिंदु से प्रारूपण को पुनरारंभ करेगा जहां परिवर्तन हुआ था।
विलंबित शुरुआत प्रारूपण
एक और मोड़: मैं फ़ॉर्मेटर हर कुंजी दबाने के साथ अपने स्वरूपण काम तुरंत शुरू करने के लिए नहीं करना चाहती। मानव प्रकार के रूप में, कीस्ट्रोक के बीच सामान्य विराम 600-700ms से कम है। अगर फॉर्मेटर विलंब के बिना स्वरूपण शुरू करता है, तो यह कीस्ट्रोक के बीच स्वरूपण शुरू करने का प्रयास करेगा। सुंदर व्यर्थ।
तो फॉर्मेटर तर्क केवल अपने स्वरूपण कार्य को शुरू करता है अगर यह 600ms से अधिक लंबे समय के कीस्ट्रोक में रोक देता है। सिग्नल प्राप्त करने के बाद, यह 600ms इंतजार कर रहा है, और यदि कोई हस्तक्षेप करने वाली कीप्रेस नहीं है, तो टाइपिंग बंद हो गई है और स्वरूपण शुरू होना चाहिए। यदि कोई हस्तक्षेप परिवर्तन हुआ है, तो फॉर्मेटर कुछ भी नहीं करता है, यह निष्कर्ष निकाला है कि उपयोगकर्ता अभी भी टाइप कर रहा है। कोड में:
private System.Threading.ManualResetEvent wantFormat = new System.Threading.ManualResetEvent(false);
कुंजी दबाने घटना:
private void richTextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
_lastRtbKeyPress = System.DateTime.Now;
wantFormat.Set();
}
colorizer विधि में, जो पृष्ठभूमि धागा में चलता है:
....
do
{
try
{
wantFormat.WaitOne();
wantFormat.Reset();
// We want a re-format, but let's make sure
// the user is no longer typing...
if (_lastRtbKeyPress != _originDateTime)
{
System.Threading.Thread.Sleep(DELAY_IN_MILLISECONDS);
System.DateTime now = System.DateTime.Now;
var _delta = now - _lastRtbKeyPress;
if (_delta < new System.TimeSpan(0, 0, 0, 0, DELAY_IN_MILLISECONDS))
continue;
}
...analyze document and apply updates...
// during analysis, periodically check for new keypress events:
if (wantFormat.WaitOne(0, false))
break;
उपयोगकर्ता अनुभव है कि कोई भी स्वरूपण होता है, जबकि वे टाइप कर रहे हैं एक बार विराम लिखने के बाद, स्वरूपण शुरू होता है। यदि टाइपिंग फिर से शुरू होती है, तो स्वरूपण बंद हो जाता है और फिर प्रतीक्षा करता है।
प्रारूप दौरान स्क्रॉल को अक्षम करने से बदलता है
एक अंतिम समस्या थी: एक RichTextBox में पाठ स्वरूपण RichTextBox.Select() के लिए एक कॉल, जब RichTextBox ध्यान केंद्रित है जो, चयनित पाठ को RichTextBox to automatically scroll का कारण बनता है की आवश्यकता है। चूंकि स्वरूपण एक ही समय में हो रहा है क्योंकि उपयोगकर्ता नियंत्रण, पढ़ने और पाठ को संपादित करने में केंद्रित है, मुझे स्क्रॉलिंग को दबाने के लिए एक तरीका चाहिए। मुझे आरटीबी के सार्वजनिक इंटरफ़ेस का उपयोग करके स्क्रॉलिंग को रोकने का कोई तरीका नहीं मिला, हालांकि मुझे इसके बारे में पूछने वाले इंटरबेट में कई लोगों को मिला। कुछ प्रयोग करने के बाद, मैंने पाया कि Win32 SendMessage() कॉल (user32.dll से) का उपयोग करके, चयन() से पहले और बाद में WM_SETREDRAW भेजना, चयन() को कॉल करते समय RichTextBox में स्क्रॉल को रोक सकता है।
क्योंकि मैं स्क्रॉल को रोकने के लिए PInvoke का सहारा था, मैं भी PInvoke SendMessage पर मिलता है या चयन या पाठ बॉक्स (EM_GETSEL या EM_SETSEL) में कैरट स्थापित करने के लिए, और चयन (EM_SETCHARFORMAT) पर स्वरूपण सेट करने के लिए इस्तेमाल किया। प्रबंधित इंटरफ़ेस का उपयोग करने से पिनवोक दृष्टिकोण थोड़ा तेज़ हो गया।
जवाबदेही
के लिए और क्योंकि कुछ गणना भूमि के ऊपर किए गए स्क्रॉल रोकने
बैच अपडेट, मैं दस्तावेज़ में किए गए परिवर्तनों अप बैच का फैसला किया। एक संगत खंड या शब्द को हाइलाइट करने के बजाय, तर्क हाइलाइट की सूची रखता है या बनाने के लिए प्रारूपों को स्वरूपित करता है। प्रत्येक बार, यह दस्तावेज़ में एक समय में शायद 30 परिवर्तन लागू होता है। फिर यह सूची को साफ़ करता है और विश्लेषण और कतार में वापस जाता है कि कौन से स्वरूप परिवर्तन किए जाने की आवश्यकता है। यह इतना तेज़ है कि परिवर्तन के इन बैचों को लागू करते समय दस्तावेज़ में टाइपिंग बाधित नहीं होती है।
उपरोक्त दस्तावेज ऑटो-स्वरूपित हो जाता है और अलग-अलग हिस्सों में रंगीन हो जाता है, जब कोई टाइपिंग नहीं होती है। यदि उपयोगकर्ता कुंजीपटल के बीच पर्याप्त समय गुजरता है, तो संपूर्ण दस्तावेज़ अंततः स्वरूपित हो जाएगा। यह 1k एक्सएमएल दस्तावेज़ के लिए 200ms से कम है, शायद 30k डॉक्टर के लिए 2 एस, या 100k डॉक्टर के लिए 10s। यदि उपयोगकर्ता दस्तावेज़ को संपादित करता है, तो प्रगति पर मौजूद किसी भी स्वरूपण को निरस्त कर दिया गया है, और प्रारूपण फिर से शुरू होता है।
पुhew!
मुझे आश्चर्य है कि एक समृद्ध टेक्स्टबॉक्स स्वरूपण के रूप में प्रतीत होता है जैसा कि उपयोगकर्ता इसमें टाइप करता है।लेकिन मैं कुछ भी आसान नहीं कर सका जिसने टेक्स्ट बॉक्स को लॉक नहीं किया, फिर भी अजीब स्क्रॉलिंग व्यवहार से परहेज किया।
आप बात मैं ऊपर वर्णित के लिए view the code कर सकते हैं।
मुझे खुशी है कि मेरा समाधान और टिप्पणियां आपके अंतिम समाधान के लिए कुछ प्रेरणा प्रदान कर सकती हैं। –
बहुत बहुत धन्यवाद, एरिक। – Cheeso
शायद मेरे उत्तर को अन-डाउन-वोट दें? –