2010-12-17 12 views

उत्तर

24

डब्ल्यूपीएफ ऐसी घटना प्रदान नहीं करता है जो पूरी तरह से आकार बदलने की प्रक्रिया के अंत में आग लगती है। आकार बदलकर विंडो आकार बदलने से जुड़ा एकमात्र घटना है - और यह आकार बदलने की प्रक्रिया के दौरान कई बार आग लग जाएगी।

कुल हैक एक टाइमर टिकिंग सेट करना होगा जब साइज चेंजेड घटना आग लगती है। फिर टाइमर को आकार समाप्त होने तक टिकने का मौका नहीं मिलेगा और उस समय आपके एक बार प्रसंस्करण करें।

public MyUserControl() 
{ 
    _resizeTimer.Tick += _resizeTimer_Tick; 
} 

DispatcherTimer _resizeTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1500), IsEnabled = false }; 

private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) 
{ 
    _resizeTimer.IsEnabled = true; 
    _resizeTimer.Stop(); 
    _resizeTimer.Start(); 
} 

void _resizeTimer_Tick(object sender, EventArgs e) 
{ 
    _resizeTimer.IsEnabled = false;  

    //Do end of resize processing 
} 
+6

फॉर्म। रीसाइजबिन/विनफॉर्म में अंत। अधिसूचना अभी भी वहां है, लेकिन WPF में अनदेखा किया गया है। दो कदम आगे, एक कदम पीछे। –

+1

@ मार्टिन, कृपया समझाएं कि आपने _resizeTimer क्यों रखा है।IsEnabled = सत्य; स्टॉप-स्टार्ट से पहले? ऐसा लगता है कि मेरे लिए कोई मतलब नहीं है। –

+0

मुझे यह तंत्र पसंद है क्योंकि यह कुछ प्रसंस्करण करने की अनुमति देता है जब उपयोगकर्ता आकार बदलता है। जब उपयोगकर्ता का आकार बदलता था तो कैनवास को फिर से लेआउट करने की आवश्यकता होती थी। इस टाइमर दृष्टिकोण के साथ जब उपयोगकर्ता ने माउस को ले जाना बंद कर दिया (लेकिन इसे जारी नहीं किया) फिर से लेआउट निष्पादित किया जा सकता है और नए आकार की प्रभाव देखी जा सकती है। मेरी टेस्ट टीम को पिछली बार फिर से लेआउट की तुलना में पसंद आया जब माउस जारी किया गया था यानी WM_EXITSIZEMOVE दृष्टिकोण। मैंने इस उदाहरण कोड में उपयोग किए गए 1500 मान के बजाय टाइमर अंतराल 200ms पर सेट किया था। – pjm

11

.NET के लिए प्रतिक्रियाशील एक्सटेंशन मानक घटना पैटर्न से निपटने के लिए कुछ वास्तव में अच्छी क्षमताओं प्रदान करता है जिसमें घटनाओं को थ्रोटल करने में सक्षम होना शामिल है। मुझे आकार बदलने वाली घटनाओं से निपटने में भी एक ही समस्या थी और समाधान अभी भी कुछ हद तक "हैकी" है, मुझे लगता है कि प्रतिक्रियाशील एक्सटेंशन इसे लागू करने का एक और अधिक शानदार तरीका प्रदान करता है।

IObservable<SizeChangedEventArgs> ObservableSizeChanges = Observable 
    .FromEventPattern<SizeChangedEventArgs>(this, "SizeChanged") 
    .Select(x => x.EventArgs) 
    .Throttle(TimeSpan.FromMilliseconds(200)); 

IDisposable SizeChangedSubscription = ObservableSizeChanges 
    .ObserveOn(SynchronizationContext.Current) 
    .Subscribe(x => { 
     Size_Changed(x); 
    }); 

यह प्रभावी रूप से थ्रॉटल जाएगा SizeChanged घटना ऐसी है कि आपके Size_Changed विधि (जहां कस्टम कोड निष्पादित कर सकते हैं) 200 मिलीसेकेंड तक निष्पादित नहीं किया जाएगा (या तथापि तक आप प्रतीक्षा करने के लिए चाहते हैं) है यहाँ मेरी कार्यान्वयन है बिना किसी SizeChanged ईवेंट को निकाल दिया गया।

private void Size_Changed(SizeChangedEventArgs e) { 
    // custom code for dealing with end of size changed here 
} 
+0

वास्तव में सुरुचिपूर्ण! – MuiBienCarlota

3

जब आप एक WPF विंडो का आकार बदलते हैं तो आप ठीक से पता लगा सकते हैं, और आपको टाइमर की आवश्यकता नहीं है। एक मूल विंडो WM_EXITSIZEMOVE संदेश प्राप्त करती है जब उपयोगकर्ता विंडो आकार के अंत में बाएं माउस बटन को या चालन ऑपरेशन का अंत देता है। एक डब्ल्यूपीएफ विंडो को यह संदेश प्राप्त नहीं होता है, इसलिए हमें WndProc फ़ंक्शन को हुक करने की आवश्यकता है जो इसे प्राप्त करेगा। हम अपने विंडो हैंडल प्राप्त करने के लिए WindowInteropHelper के साथ HwndSource का उपयोग कर सकते हैं। फिर हम हुक को हमारे WndProc फ़ंक्शन में जोड़ देंगे। (Vb.net कोड) हम सब कि क्या करेंगे खिड़की Loaded स्थिति में:

Dim WinSource As HwndSource  

Private Sub WindowLoaded_(sender As Object, e As RoutedEventArgs) 

    WinSource = HwndSource.FromHwnd(New WindowInteropHelper(Me).Handle) 
    WinSource.AddHook(New HwndSourceHook(AddressOf WndProc)) 
End Sub 

अब, हमारा WndProc में, हम WM_EXITSIZEMOVE संदेश सुनने देगा:

Const WM_EXITSIZEMOVE As Integer = &H232 

Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr 

    If msg = WM_EXITSIZEMOVE Then 

     DoWhatYouNeed() 
    End If 

    Return IntPtr.Zero 
End Function 

यह और एक समान तकनीक here और here समझाया गया है।

ध्यान दें कि फ़ंक्शन IntPtr.Zero को वापस करना चाहिए। साथ ही, आप जिस विशिष्ट संदेश में दिलचस्पी रखते हैं उसे संभालने के अलावा इस func में कुछ भी न करें।

अब, WM_EXITSIZEMOVE भी एक चाल ऑपरेशन के अंत में भेजा जाता है, और हम केवल आकार बदलने में रूचि रखते हैं। यह निर्धारित करने के कई तरीके हैं कि यह आकार बदलने का अंत था। मैंने इसे WM_SIZING संदेश (जिसे आकार के दौरान कई बार भेजा गया) सुनकर किया, एक ध्वज के साथ संयुक्त। पूरे समाधान इस तरह दिखता है:

(नोट: कोड यहाँ पर प्रकाश डाला के साथ भ्रमित मत हो, vb.net के लिए अपने गलत कारण)

Dim WinSource As HwndSource 
Const WM_SIZING As Integer = &H214 
Const WM_EXITSIZEMOVE As Integer = &H232 

Dim WindowWasResized As Boolean = False 

Private Sub WindowLoaded_(sender As Object, e As RoutedEventArgs) 

    WinSource = HwndSource.FromHwnd(New WindowInteropHelper(Me).Handle) 
    WinSource.AddHook(New HwndSourceHook(AddressOf WndProc)) 
End Sub 

Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr 

    If msg = WM_SIZING Then 

     If WindowWasResized = False Then 

      'indicate the the user is resizing and not moving the window 
      WindowWasResized = True 
     End If 
    End If 

    If msg = WM_EXITSIZEMOVE Then 

     'check that this is the end of resize and not move operation   
     If WindowWasResized = True Then 

      DoWhatYouNeed() 

      'set it back to false for the next resize/move 
      WindowWasResized = False 
     End If    
    End If 

    Return IntPtr.Zero 
End Function 

यह है कि।

+1

इस संकेत के लिए आपको बहुत बहुत धन्यवाद! टाइमर के साथ समाधान बहुत खराब है .. –

+0

मुझे लगता है कि हमें 'WM_SIZING' के बजाय' WM_ENTERSIZEMOVE' 'को संभालना चाहिए। – yumetodo

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