2012-12-13 16 views
5

Moja aplikacja WPF ma UserControl, która ma wyglądać i zachowywać się jak okno wyskakujące, ale nie jest to okno. Powód, dla którego kontrola nie pochodzi z klasy Window, ponieważ zawiera wirtualną klawiaturę ekranową innej firmy i ta kontrolka musi być w tym samym oknie, co kontrolki, do których wysyła znaki wejściowe, kiedy klikniesz jego przyciski. Jeśli sterowanie klawiaturą nie znajduje się w tym samym oknie, nie widać nawet kontrolek TextBox.Jak ustawić sterowanie przez użytkownika na ekranie jak okno

Problem, który mam, to wydajność jest nędzna podczas przeciągania okna dialogowego. Jest wystarczająco powolny, gdy mysz opuszcza obszar przeciągania i przestaje podążać za myszą. Potrzebuję lepszego sposobu.

Oto fragment z XAML do sterowania:

<Grid Name="LayoutRoot"> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto" /> 
     <RowDefinition Height="*" /> 
    </Grid.RowDefinitions> 
    <Border Background="{DynamicResource PopupBackground}" 
      BorderBrush="{DynamicResource PopupBorder}" 
      BorderThickness="5,5,5,0" 
      MouseLeftButtonDown="Grid_MouseLeftButtonDown" 
      MouseLeftButtonUp="Grid_MouseLeftButtonUp" 
      MouseMove="Grid_MouseMove"> 
    . . . 
    </Border> 
</Grid> 

oto obsługi zdarzeń myszy:

private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { 
     Canvas canvas = Parent as Canvas; 
     if (canvas == null) { 
      throw new InvalidCastException("The parent of a KeyboardPopup control must be a Canvas."); 
     } 
     DraggingControl = true; 
     CurrentMousePosition = e.GetPosition(canvas); 
     e.Handled = true; 
    } 

    private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { 
     Canvas canvas = Parent as Canvas; 
     if (canvas == null) { 
      throw new InvalidCastException("The parent of a KeyboardPopup control must be a Canvas."); 
     } 

     if (DraggingControl) { 
      Point mousePosition = e.GetPosition(canvas); 

      // Correct the mouse coordinates in case they go off the edges of the control 
      if (mousePosition.X < 0.0) mousePosition.X = 0.0; else if (mousePosition.X > canvas.ActualWidth) mousePosition.X = canvas.ActualWidth; 
      if (mousePosition.Y < 0.0) mousePosition.Y = 0.0; else if (mousePosition.Y > canvas.ActualHeight) mousePosition.Y = canvas.ActualHeight; 

      // Compute the new Left & Top coordinates of the control 
      Canvas.SetLeft(this, Left += mousePosition.X - CurrentMousePosition.X); 
      Canvas.SetTop(this, Top += mousePosition.Y - CurrentMousePosition.Y); 
     } 
     e.Handled = true; 
    } 

    private void Grid_MouseMove(object sender, MouseEventArgs e) { 
     Canvas canvas = Parent as Canvas; 
     if (canvas == null) { 
      // It is not. Throw an exception 
      throw new InvalidCastException("The parent of a KeyboardPopup control must be a Canvas."); 
     } 

     if (DraggingControl && e.LeftButton == MouseButtonState.Pressed) { 
      Point mousePosition = e.GetPosition(canvas); 

      // Correct the mouse coordinates in case they go off the edges of the control 
      if (mousePosition.X < 0.0) mousePosition.X = 0.0; else if (mousePosition.X > canvas.ActualWidth ) mousePosition.X = canvas.ActualWidth; 
      if (mousePosition.Y < 0.0) mousePosition.Y = 0.0; else if (mousePosition.Y > canvas.ActualHeight) mousePosition.Y = canvas.ActualHeight; 

      // Compute the new Left & Top coordinates of the control 
      Canvas.SetLeft(this, Left += mousePosition.X - CurrentMousePosition.X); 
      Canvas.SetTop (this, Top += mousePosition.Y - CurrentMousePosition.Y); 

      CurrentMousePosition = mousePosition; 
     } 
     e.Handled = true; 
    } 

Należy zauważyć, że kontrola musi być umieszczony wewnątrz Canvas w oknie, które się go używa .

Nie mogę użyć DragMove, ponieważ jest to metoda klasy Window i ta klasa wywodzi się z UserControl. Jak poprawić wydajność przeciągania tej kontrolki? Czy muszę uciekać się do API Win32?

+0

http://stackoverflow.com/a/38830033/2507782 –

Odpowiedz

4

Na podstawie informacji zawartych w odpowiedzi użytkownika @ DmitryMartovoi, mam wymyślić sposób, aby tę pracę. Nadal daję Dmitrijem +1, ponieważ nie byłbym w stanie tego rozgryźć bez jego wkładu.

Co zrobiłem było stworzyłem TranslateTransform w moim UserControl's konstruktora i przypisać go do RenderTransform nieruchomości:

RenderTransform = new TranslateTransform(); 

W XAML, nazwałem kontrolę Border że użytkownik kliknie przeciągnąć całą kontrolę :

<Border Background="{DynamicResource PopupBackground}" 
     BorderBrush="{DynamicResource PopupBorder}" 
     BorderThickness="5,5,5,0" 
     MouseLeftButtonDown="Grid_MouseLeftButtonDown" 
     MouseLeftButtonUp="Grid_MouseLeftButtonUp" 
     MouseMove="Grid_MouseMove" 
     Name="TitleBorder"> 

    . . . 
</Border> 

Wreszcie modyfikacji różnych procedur obsługi zdarzeń myszy, jak następuje:

private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { 
    CurrentMousePosition = e.GetPosition(Parent as Window); 
    TitleBorder.CaptureMouse(); 
} 

private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { 
    if (TitleBorder.IsMouseCaptured) { 
     TitleBorder.ReleaseMouseCapture(); 
    } 
} 

private void Grid_MouseMove(object sender, MouseEventArgs e) { 
    Vector diff = e.GetPosition(Parent as Window) - CurrentMousePosition; 
    if (TitleBorder.IsMouseCaptured) { 
     (RenderTransform as TranslateTransform).X = diff.X; 
     (RenderTransform as TranslateTransform).Y = diff.Y; 
    } 
} 

To działa pięknie.Całość UserControl i cała jego zawartość poruszają się płynnie po przeciągnięciu myszy Border. I cały UserControl nie porusza się, jeśli klikniesz nigdzie indziej na jego powierzchni.

Jeszcze raz dziękuję @DmitryMartovoi za kod, który dostarczył.

EDYCJA: Edytuję tę odpowiedź, ponieważ powyższy kod, mimo że działał, nie był doskonały. Wada polega na tym, że kontrola powróciła do pierwotnej lokalizacji na ekranie po kliknięciu obszaru paska tytułu i przed rozpoczęciem przeciągania. To było denerwujące i całkowicie błędne.

Podejście, które wymyśliłem, zadziałało bezbłędnie, najpierw poddając sterowanie Canvas. Ważne jest, aby rodzicem kontroli był Canvas lub poniższy kod nie będzie działał. Przestałem również używać RenderTransform. Dodałem prywatną własność o nazwie canvas typu Canvas. Dodałem obsługi Loaded zdarzeń do kontroli podręcznego zrobić jakąś ważną inicjalizacji:

private void KeyboardPopup_Loaded(object sender, RoutedEventArgs e) { 
    canvas = Parent as Canvas; 
    if (canvas == null) { 
     throw new InvalidCastException("The parent of a KeyboardPopup control must be a Canvas."); 
    }  
} 

z tym wszystkim zrobić, tutaj są modyfikowane obsługi zdarzeń myszy:

private void TitleBorder_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { 
    StartMousePosition = e.GetPosition(canvas); 
    TitleBorder.CaptureMouse(); 
} 

private void TitleBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { 
    if (TitleBorder.IsMouseCaptured) { 
     Point mousePosition = e.GetPosition(canvas); 
     Canvas.SetLeft(this, Canvas.GetLeft(this) + mousePosition.X - StartMousePosition.X); 
     Canvas.SetTop (this, Canvas.GetTop (this) + mousePosition.Y - StartMousePosition.Y); 
     canvas.ReleaseMouseCapture(); 
    } 
} 

private void TitleBorder_MouseMove(object sender, MouseEventArgs e) { 
    if (TitleBorder.IsMouseCaptured && e.LeftButton == MouseButtonState.Pressed) { 
     Point mousePosition = e.GetPosition(canvas); 

     // Compute the new Left & Top coordinates of the control 
     Canvas.SetLeft(this, Canvas.GetLeft(this) + mousePosition.X - StartMousePosition.X); 
     Canvas.SetTop (this, Canvas.GetTop (this) + mousePosition.Y - StartMousePosition.Y); 
     StartMousePosition = mousePosition; 
    } 
} 

Pobyty kontrolne, gdzie spadły to po kliknięciu na pasku tytułu, aby przenieść go po raz drugi, a porusza się tylko po kliknięciu na pasku tytułu. Kliknięcie w dowolnym miejscu kontrolki nic nie robi, a przeciąganie jest płynne i szybkie.

5

Możesz po prostu użyć MouseDragElementBehavior.

UPD Ważna rzeczą MouseDragElementBehavior zachowanie:

Zachowanie MouseDragElementBehavior nie działa dla wszelkich kontroli, które obsługują zdarzenia MouseClick (Button, TextBox i kontroluje ListBox, na przykład). Jeśli potrzebujesz możliwości przeciągnięcia kontrolki jednego z tych typów, ustaw tę kontrolę jako element potomny elementu sterującego, który można przeciągnąć (na przykład krawędź). Następnie można zastosować zachowanie MouseDragElementBehavior do elementu nadrzędnego.

Można również realizować swoje własne zachowanie przeciągania tak:

public class DragBehavior : Behavior<UIElement> 
{ 
    private Point elementStartPosition; 
    private Point mouseStartPosition; 
    private TranslateTransform transform = new TranslateTransform(); 

    protected override void OnAttached() 
    { 
     Window parent = Application.Current.MainWindow; 
     AssociatedObject.RenderTransform = transform; 

     AssociatedObject.MouseLeftButtonDown += (sender, e) => 
     { 
      elementStartPosition = AssociatedObject.TranslatePoint(new Point(), parent); 
      mouseStartPosition = e.GetPosition(parent); 
      AssociatedObject.CaptureMouse(); 
     }; 

     AssociatedObject.MouseLeftButtonUp += (sender, e) => 
     { 
      AssociatedObject.ReleaseMouseCapture(); 
     }; 

     AssociatedObject.MouseMove += (sender, e) => 
     { 
      Vector diff = e.GetPosition(parent) - mouseStartPosition; 
      if (AssociatedObject.IsMouseCaptured) 
      { 
       transform.X = diff.X; 
       transform.Y = diff.Y; 
      } 
     }; 
    } 
} 
+1

Podczas gdy to działa, ma problemy. Po pierwsze dotyczy tylko elementu, do którego dodajesz zachowanie i jego dzieci. Na początku dodałem zachowanie do "granicy", która jest tłem "paska tytułu" kontrolki. Tylko "Border" był przeciągalny, a reszta kontroli pozostała. Kiedy zastosuję to zachowanie do czegokolwiek wyższego, np. 'UserControl' lub' Grid', które przechowuje wszystko, zachowanie działa, jeśli przeciągniesz w nich wszystko, w tym tło i dowolne przyciski tworzące klawisze. To też nie jest akceptowalne. –

+1

Czy istnieje sposób, w jaki mogę uzyskać przeciągnięcie do pracy dla całego elementu sterującego, nie pozwalając użytkownikowi przeciągać tła lub innych elementów sterujących w szablonie? –

+1

Nie można bezpośrednio przeciągnąć formantów obsługujących zdarzenie MouseClick. Zobacz aktualizację. –

Powiązane problemy