2010-12-17 3 views

답변

24

WPF는 크기 조정 프로세스가 끝날 때 발생하는 이벤트를 제공하지 않습니다. SizeChanged은 창 크기 조정과 관련된 유일한 이벤트이며 크기 조정 과정에서 여러 번 실행됩니다.

전체 해킹은 SizeChanged 이벤트가 발생하면 타이머가 똑딱 거리는 것을 계속 설정하는 것입니다. 그런 다음 타이머는 크기 조정이 끝날 때까지 틱 할 기회를 얻지 않으며 그 시점에서 한 번 처리합니다.

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

Form.ResizeBegin/End in Winforms. 알림은 여전히 ​​있지만 WPF에서는 무시됩니다. 두 걸음 앞으로, 한 걸음 뒤로. –

+1

@ 마틴, 이유를 설명 해주세요 _resizeTimer.IsEnabled = true; 그만 시작하기 전에? 저에게는 감각이 없어 보입니다. –

+0

사용자가 크기를 일시 정지 할 때 일부 처리를 수행 할 수 있으므로이 메커니즘이 마음에 듭니다. 사용자가 크기를 조정할 때 캔버스를 다시 배치해야하는 상황이있었습니다. 이 타이머 방식을 사용하면 사용자가 마우스를 움직일 때 멈추었지만 마우스를 놓지 않아도 다시 레이아웃을 실행하고 새 크기의 영향을 볼 수 있습니다. 내 테스트 팀이 이전보다 마우스 포인터를 놓았을 때만 마우스를 튕겨서 다시 훑어 보았습니다. 즉 WM_EXITSIZEMOVE 접근 방식입니다. 이 예제 코드에서 사용 된 1500 값 대신 타이머 간격을 200ms로 설정했습니다. – pjm

11

.NET 용 Reactive Extensions에는 이벤트를 조절할 수있는 기능을 포함하여 표준 이벤트 패턴을 처리 할 수있는 몇 가지 유용한 기능이 있습니다. 크기가 변경된 이벤트를 처리 할 때도 비슷한 문제가 있었지만 솔루션이 여전히 "해킹"되는 반면 Reactive Extensions는 훨씬 더 우아한 구현 방법을 제공한다고 생각합니다.

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); 
    }); 

이 효과적으로 (사용자 정의 코드를 실행할 수 있습니다) 당신의 SIZE_CHANGED 방법은 200 밀리 초까지 실행되지 않습니다 (또는 그러나 당신이 기다릴하고자)하는 SizeChanged 이벤트 등을 스로틀 것이다있다 : 여기 내 구현 SizeChanged 이벤트가 발생하지 않고 전달되었습니다.

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

정말 우아합니다! – MuiBienCarlota

3

정확히 언제 WPF 창의 크기가 조정되었는지를 감지 할 수 있으며 타이머가 필요하지 않습니다. 네이티브 윈도우는 사용자가 윈도우 크기 변경 또는 이동 작업의 끝에서 마우스 왼쪽 버튼을 놓을 때 WM_EXITSIZEMOVE 메시지를 수신합니다. WPF 창이이 메시지를받지 못하기 때문에 수신 할 WndProc 함수를 연결해야합니다. 우리는 창 핸들을 얻기 위해 HwndSourceWindowInteropHelper과 함께 사용할 수 있습니다. 그런 다음 우리의 WndProc 함수에 후크를 추가 할 것입니다. 우리는 창 Loaded 경우에 모든 것을 할 것입니다 (vb.net 코드) : 우리의 WndProc에서 지금

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 

, 우리는 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 

이와 유사한 기술을 herehere으로 설명됩니다.

이 함수는 IntPtr.Zero를 반환해야합니다. 또한이 func에서는 관심있는 특정 메시지를 처리하는 것을 제외하고는하지 마십시오.

이제는 이동 작업이 끝날 때 WM_EXITSIZEMOVE이 전송되며 크기 조정에만 관심이 있습니다. 이 작업이 크기 조정 작업의 끝임을 확인하는 데는 여러 가지 방법이 있습니다. 나는 깃발과 결합 된 WM_SIZING 메시지 (크기 재조정 중에 여러 번 보낸 메시지)를 들음으로써 그것을했다. 전체 솔루션은 다음과 같습니다

: 그것 뿐이다

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 

을 (참고, 여기에 강조 코드와 혼동 vb.net에 대한 잘못이 발생하지 마십시오).

+1

이 힌트를 주셔서 대단히 감사합니다! 타이머가있는 솔루션은 매우 좋지 않습니다. –

+0

우리는'WM_SIZING' 대신'WM_ENTERSIZEMOVE'를 처리해야한다고 생각합니다. – yumetodo

관련 문제