2017-12-09 2 views
6

현재 런타임에 컨트롤에 동적으로 추가되는 전체 화면 Grid이 포함 된 C# WPF 응용 프로그램에서 작업하고 있습니다. 사용자가이 컨트롤을 마우스 이벤트와 함께 Grid을 통해 이동할 수있게 해주는 코드가 있습니다. 내가 지금하고 싶은 것은 컨트롤을 (가로 세로 비율을 유지하면서) 또한 런타임에 허용 할 수 있습니다. Canvas (및 Thumb 컨트롤)을 사용하는 방법을 설명하는 다양한 자습서를 보았지만 Grid에는 아무 것도 없습니다. 내 응용 프로그램에서 Canvas을 사용할 수 없기 때문에 그리드에서이를 구현하는 효율적인 방법이 있습니까? 당신이 내 코드가 어떻게 생겼는지의 아이디어를 제공하기 위해, 나는 아래로 내 마우스 이벤트를 배치 : [아래 편집] 런타임에 격자에서 WPF 컨트롤의 크기를 조절하는 방법 (가로 세로 비율 유지)

MainWindow.xaml.cs를 :

public partial class MainWindow : Window 
{ 
    //Orientation variables: 
    public static Point _anchorPoint; 
    public static Point _currentPoint; 
    private static double _originalTop; 
    private static double _originalLeft; 
    private static Point _startPoint; 
    private static bool _isDown = false; 
    private static bool _isInDrag = false; 
    private static bool _isDragging = false; 
    public static UIElement selectedElement = null; 
    public static bool elementIsSelected = false; 
    public static Dictionary<object, TranslateTransform> PointDict = new Dictionary<object, TranslateTransform>(); 
    public static AdornerLayer aLayer; 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 
    //Control events: 
    public static void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) //HUD element left mouse button up 
    {  
     if (_isInDrag) 
     { 
      var element = sender as FrameworkElement; 
      element.ReleaseMouseCapture(); 
      _isInDrag = false; 
      e.Handled = true; 
      aLayer = AdornerLayer.GetAdornerLayer(selectedElement); 
      aLayer.Add(new ResizingAdorner(selectedElement)); 
     } 
    } 
    public static void HUD_MouseDown(object sender, MouseButtonEventArgs e) 
    { 
     if (elementIsSelected) 
      { 
       elementIsSelected = false; 
       _isDown = false; 
       if (selectedElement != null) 
       { 
        aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]); 
        selectedElement = null; 
       } 
      } 
      if (e.Source != mw.PACSGrid) 
      { 
       _isDown = true; 
       _startPoint = e.GetPosition(mw.PACSGrid); 
       selectedElement = e.Source as UIElement; 
       _originalLeft = VisualWorker.GetLeft(selectedElement); 
       _originalTop = VisualWorker.GetTop(selectedElement); 
       aLayer = AdornerLayer.GetAdornerLayer(selectedElement); 
       aLayer.Add(new ResizingAdorner(selectedElement)); 
       elementIsSelected = true; 
       e.Handled = true; 
      } 
    } 
    public static void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) //HUD element left mouse button down 
    { 
     if (elementIsSelected) 
      { 
       aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]); 
       selectedElement = sender as UIElement; 
       var element = sender as FrameworkElement; 
       _anchorPoint = e.GetPosition(null); 
       element.CaptureMouse(); 
       _isInDrag = true; 
       e.Handled = true; 
      } 
    } 
    public static void Control_MouseMove(object sender, MouseEventArgs e) //Drag & drop HUD element 
    { 
     if (_isInDrag) // The user is currently dragging the HUD element... 
     { 
      _currentPoint = e.GetPosition(null); 
      TranslateTransform tt = new TranslateTransform(); 
      bool isMoved = false; 
      if (PointDict.ContainsKey(sender)) 
      { 
       tt = PointDict[sender]; 
       isMoved = true; 
      } 
      tt.X += _currentPoint.X - _anchorPoint.X; 
      tt.Y += (_currentPoint.Y - _anchorPoint.Y); 
      _anchorPoint = _currentPoint; 
      (sender as UIElement).RenderTransform = tt; 
      if (isMoved) 
      { 
       PointDict.Remove(sender); 
      } 
      PointDict.Add(sender, tt);  
     } 
    } 
} 
    // Adorner Class: 
public class ResizingAdorner : Adorner 
{ 
    Thumb topLeft, topRight, bottomLeft, bottomRight; 

    // To store and manage the adorner's visual children. 
    VisualCollection visualChildren; 

    // Initialize the ResizingAdorner. 
    public ResizingAdorner(UIElement adornedElement) 
     : base(adornedElement) 
    { 
     visualChildren = new VisualCollection(this); 

     // Call a helper method to initialize the Thumbs 
     // with a customized cursors. 
     BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE); 
     BuildAdornerCorner(ref topRight, Cursors.SizeNESW); 
     BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW); 
     BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE); 

     // Add handlers for resizing. 
     bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft); 
     bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight); 
     topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft); 
     topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight); 
    } 

    // Handler for resizing from the bottom-right. 
    void HandleBottomRight(object sender, DragDeltaEventArgs args) 
    { 
     FrameworkElement adornedElement = this.AdornedElement as FrameworkElement; 
     Thumb hitThumb = sender as Thumb; 

     if (adornedElement == null || hitThumb == null) return; 
     FrameworkElement parentElement = adornedElement.Parent as FrameworkElement; 

     // Ensure that the Width and Height are properly initialized after the resize. 
     EnforceSize(adornedElement); 

     // Change the size by the amount the user drags the mouse, as long as it's larger 
     // than the width or height of an adorner, respectively. 
     adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width); 
     adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height); 
    } 

    // Handler for resizing from the top-right. 
    void HandleTopRight(object sender, DragDeltaEventArgs args) 
    { 
     FrameworkElement adornedElement = this.AdornedElement as FrameworkElement; 
     Thumb hitThumb = sender as Thumb; 

     if (adornedElement == null || hitThumb == null) return; 
     FrameworkElement parentElement = adornedElement.Parent as FrameworkElement; 

     // Ensure that the Width and Height are properly initialized after the resize. 
     EnforceSize(adornedElement); 

     // Change the size by the amount the user drags the mouse, as long as it's larger 
     // than the width or height of an adorner, respectively. 
     adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width); 
     //adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height); 

     double height_old = adornedElement.Height; 
     double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height); 
     double top_old = VisualWorker.GetTop(adornedElement); 
     //double top_old = Canvas.GetTop(adornedElement); 
     adornedElement.Height = height_new; 
     //Canvas.SetTop(adornedElement, top_old - (height_new - height_old)); 
     VisualWorker.SetTop(adornedElement, top_old - (height_new - height_old)); 
    } 

    // Handler for resizing from the top-left. 
    void HandleTopLeft(object sender, DragDeltaEventArgs args) 
    { 
     FrameworkElement adornedElement = AdornedElement as FrameworkElement; 
     Thumb hitThumb = sender as Thumb; 

     if (adornedElement == null || hitThumb == null) return; 

     // Ensure that the Width and Height are properly initialized after the resize. 
     EnforceSize(adornedElement); 

     // Change the size by the amount the user drags the mouse, as long as it's larger 
     // than the width or height of an adorner, respectively. 
     //adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width); 
     //adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height); 

     double width_old = adornedElement.Width; 
     double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width); 
     double left_old = VisualWorker.GetLeft(adornedElement); 
     //double left_old = Canvas.GetLeft(adornedElement); 
     adornedElement.Width = width_new; 
     VisualWorker.SetLeft(adornedElement, left_old - (width_new - width_old)); 

     double height_old = adornedElement.Height; 
     double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height); 
     double top_old = VisualWorker.GetTop(adornedElement); 
     //double top_old = Canvas.GetTop(adornedElement); 
     adornedElement.Height = height_new; 
     //Canvas.SetTop(adornedElement, top_old - (height_new - height_old)); 
     VisualWorker.SetTop(adornedElement, top_old - (height_new - height_old)); 
    } 

    // Handler for resizing from the bottom-left. 
    void HandleBottomLeft(object sender, DragDeltaEventArgs args) 
    { 
     FrameworkElement adornedElement = AdornedElement as FrameworkElement; 
     Thumb hitThumb = sender as Thumb; 

     if (adornedElement == null || hitThumb == null) return; 

     // Ensure that the Width and Height are properly initialized after the resize. 
     EnforceSize(adornedElement); 

     // Change the size by the amount the user drags the mouse, as long as it's larger 
     // than the width or height of an adorner, respectively. 
     //adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width); 
     adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height); 

     double width_old = adornedElement.Width; 
     double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width); 
     double left_old = VisualWorker.GetLeft(adornedElement); 
     //double left_old = Canvas.GetLeft(adornedElement); 
     adornedElement.Width = width_new; 
     //Canvas.SetLeft(adornedElement, left_old - (width_new - width_old)); 
     VisualWorker.SetLeft(adornedElement, left_old - (width_new - width_old)); 
    } 

    // Arrange the Adorners. 
    protected override Size ArrangeOverride(Size finalSize) 
    { 
     // desiredWidth and desiredHeight are the width and height of the element that's being adorned. 
     // These will be used to place the ResizingAdorner at the corners of the adorned element. 
     double desiredWidth = AdornedElement.DesiredSize.Width; 
     double desiredHeight = AdornedElement.DesiredSize.Height; 
     // adornerWidth & adornerHeight are used for placement as well. 
     double adornerWidth = this.DesiredSize.Width; 
     double adornerHeight = this.DesiredSize.Height; 

     topLeft.Arrange(new Rect(-adornerWidth/2, -adornerHeight/2, adornerWidth, adornerHeight)); 
     topRight.Arrange(new Rect(desiredWidth - adornerWidth/2, -adornerHeight/2, adornerWidth, adornerHeight)); 
     bottomLeft.Arrange(new Rect(-adornerWidth/2, desiredHeight - adornerHeight/2, adornerWidth, adornerHeight)); 
     bottomRight.Arrange(new Rect(desiredWidth - adornerWidth/2, desiredHeight - adornerHeight/2, adornerWidth, adornerHeight)); 

     // Return the final size. 
     return finalSize; 
    } 

    // Helper method to instantiate the corner Thumbs, set the Cursor property, 
    // set some appearance properties, and add the elements to the visual tree. 
    void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor) 
    { 
     if (cornerThumb != null) return; 

     cornerThumb = new Thumb(); 

     // Set some arbitrary visual characteristics. 
     cornerThumb.Cursor = customizedCursor; 
     cornerThumb.Height = cornerThumb.Width = 10; 
     cornerThumb.Opacity = 1; 
     cornerThumb.Background = new ImageBrush(new BitmapImage(new Uri(@"pack://application:,,,/Images/Thumb 1.jpg"))); 
     visualChildren.Add(cornerThumb); 
    } 

    // This method ensures that the Widths and Heights are initialized. Sizing to content produces 
    // Width and Height values of Double.NaN. Because this Adorner explicitly resizes, the Width and Height 
    // need to be set first. It also sets the maximum size of the adorned element. 
    void EnforceSize(FrameworkElement adornedElement) 
    { 
     if (adornedElement.Width.Equals(Double.NaN)) 
      adornedElement.Width = adornedElement.DesiredSize.Width; 
     if (adornedElement.Height.Equals(Double.NaN)) 
      adornedElement.Height = adornedElement.DesiredSize.Height; 

     FrameworkElement parent = adornedElement.Parent as FrameworkElement; 
     if (parent != null) 
     { 
      adornedElement.MaxHeight = parent.ActualHeight; 
      adornedElement.MaxWidth = parent.ActualWidth; 
     } 
    } 
    // Override the VisualChildrenCount and GetVisualChild properties to interface with 
    // the adorner's visual collection. 
    protected override int VisualChildrenCount { get { return visualChildren.Count; } } 
    protected override Visual GetVisualChild(int index) { return visualChildren[index]; } 
} 
// Canvas alternative class: 
public class VisualWorker 
{ 
    public static void SetTop(UIElement uie, double top) 
    { 
     var frame = uie as FrameworkElement; 
     frame.Margin = new Thickness(frame.Margin.Left, top, frame.Margin.Right, frame.Margin.Bottom); 
    } 
    public static void SetLeft(UIElement uie, double left) 
    { 
     var frame = uie as FrameworkElement; 
     frame.Margin = new Thickness(left, frame.Margin.Top, frame.Margin.Right, frame.Margin.Bottom); 
    } 
    public static double GetTop(UIElement uie) 
    { 
     return (uie as FrameworkElement).Margin.Top; 
    } 
    public static double GetLeft(UIElement uie) 
    { 
     return (uie as FrameworkElement).Margin.Left; 
    } 
} 

MainWindow.xaml (예) :

<Window x:Name="MW" x:Class="MyProgram.MainWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:local="clr-namespace:MyProgram" 
mc:Ignorable="d" 
Title="MyProgram" d:DesignHeight="1080" d:DesignWidth="1920" ResizeMode="NoResize" WindowState="Maximized" WindowStyle="None" MouseLeave="HUD_MouseLeave"> 

    <Grid x:Name="MyGrid MouseDown="HUD_MouseDown" /> 
     <Image x:Name="Image1" Source="pic.png" Margin="880,862,0,0" Height="164" Width="162" HorizontalAlignment="Left" VerticalAlignment="Top" MouseLeftButtonDown="Control_MouseLeftButtonDown" MouseLeftButtonUp="Control_MouseLeftButtonUp" MouseMove="Control_MouseMove" /> 
     <TextBox x:Name="Textbox1" Margin="440,560,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" MouseLeftButtonDown="Control_MouseLeftButtonDown" MouseLeftButtonUp="Control_MouseLeftButtonUp" MouseMove="Control_MouseMove" /> 

편집 : TranslateTransform을 사용하여 컨트롤의 여백을 변경하지 않는 것으로 나타났습니다. 이 작업을 수행하려면 적절하게 변경하려면 여백이 필요합니다.

편집 2 : 크기 조정 adorner (찾은 here)에 [수정] 코드를 추가했습니다. 거의 실제로 작동합니다. 문제는 내가 꾸며낸 사람들과 이상한 행동을하고 있다는 것입니다. 내 컨트롤 중 하나 (왼쪽 상단 모서리)에서 모든 4 개의 모서리가 나타납니다. 하지만 나머지 컨트롤은 1-2 명의 광고 자만 확보 할 수 있으며 올바르게 배치되지 않습니다. 그것들과의 행동은 낯선 사람에게도 마찬가지입니다. 네, 이것이 많은 코드라는 것을 알지만 문제가 ResizingAdorner 클래스에 있다고 의심됩니다.

편집 3 : & 붙여 넣기를 복사하려는 사람들에게 '상용구'코드가 추가되었습니다. 문제없이 컴파일해야합니다. 문제가 있으면 알려주세요.

편집 4 (1/10/2018) : 여전히 좋은 답변입니다. 컨트롤의 Margin이 0,0 인 경우에만 adorner의 Thumb 컨트롤이 올바르게 정렬됩니다. 그 위치에서 움직일 때, adorners는 요소에서 멀리 떨어진다.

편집 5 (1/15/2018) : adorner 클래스는 원래 Canvas 용으로 설계되었으며 Grid에서 실행하면 문제를 일으킬 수 있습니다. 가장 좋은 추측은 ArrangeOverride 메서드가 엉망인 경우입니다 (엄지 손가락이 UIElement에있는 곳이기 때문에).

+0

귀하의 질문은 너무 광범위합니다. ** 특정 ** 문제가 발생하면 무언가를 구현하고 알려주십시오. – dymanoid

+0

@dymanoid 나는이 문제를 물었습니다. 왜냐하면 제가 연구했고 전혀 구현할 해결책을 찾지 못했기 때문입니다. 내 이벤트에 대한 코드를 제공했으며, 그들이하는 일을 아는 사람은 나를 올바른 방향으로 안내 할 수 있습니다. –

+0

Canvas를 사용할 수없는 이유는 무엇입니까? 그러한 작업을위한 좋은 패널입니다. – Netstep

답변

0

오늘, 일부 재판 후 및 erro r, Adorner 버그를 수정하는 방법을 알 수있었습니다. 내가 게시 한 것처럼 요소의 크기를 조정하는 데 사용되는 코너 Thumbs가 컨트롤에 올바르게 정렬되지 않았습니다.원래 코드는 내가 사용중인 Grid 컨테이너가 아닌 Canvas에서 사용하기위한 것입니다. 결함은 ArrangeOverride 방법의 double 값에 있습니다 (Thumbs을 정렬 함). 원래 코드였다

double desiredWidth = (AdornedElement as FrameworkElement).ActualWidth; 
double desiredHeight = (AdornedElement as FrameworkElement).ActualHeight; 
// adornerWidth & adornerHeight are used for placement as well. 
double adornerWidth = (AdornedElement as FrameworkElement).Width; 
double adornerHeight = (AdornedElement as FrameworkElement).Height; 
//Arrange the thumbs: 
topLeft.Arrange(new Rect(-adornerWidth/2, -adornerHeight/2, adornerWidth, adornerHeight)); 
topRight.Arrange(new Rect(desiredWidth - adornerWidth/2, -adornerHeight/2, adornerWidth, adornerHeight)); 
bottomLeft.Arrange(new Rect(-adornerWidth/2, desiredHeight - adornerHeight/2, adornerWidth, adornerHeight)); 
bottomRight.Arrange(new Rect(desiredWidth - adornerWidth/2, desiredHeight - adornerHeight/2, adornerWidth, adornerHeight)); 

그것은 특히 Thumb들에 대한 실제 측정을 제공하지 않은 정렬하기 위해 사용되는 desiredWidthdesiredHeight 두 변수 같다 : I 내로 개질

double desiredWidth = AdornedElement.DesiredSize.Width; 
double desiredHeight = AdornedElement.DesiredSize.Height; 
// adornerWidth & adornerHeight are used for placement as well. 
double adornerWidth = this.DesiredSize.Width; 
double adornerHeight = this.DesiredSize.Height; 
//Arrange method calls below.... 

... 제어. 이 변경과 함께 코드를 사용하는 것은 기능적이지만 왼쪽 상단, 오른쪽 상단 및 왼쪽 하단 adorners에 대해서는 여전히 상당히 이상합니다. 그것은 내가 스스로 조정할 수 있다고 믿는 것입니다. 의견을 제공 한 모든 사람들에게 감사드립니다.

Link to original (canvas) code

1

조정 :

<Grid x:Name="Content"> 
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Background="Blue" Width="20" Height="20" x:Name="BorderToResize"/> 
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Background="Red" Width="10" Height="10" MouseLeftButtonDown="OnLeftMouseButtonDown" MouseLeftButtonUp="OnLeftMouseButtonUp" MouseMove="OnMouseMove" x:Name="BorderThumb"> 
    <Border.RenderTransform> 
     <TranslateTransform X="15" Y="15" /> 
    </Border.RenderTransform> 
</Border> 

코드 뒤에 :

어쩌면이

XAML ... 선발이다

private void OnLeftMouseButtonDown(object sender, MouseButtonEventArgs e) { 
     (sender as UIElement).CaptureMouse(); 
    } 

    private void OnLeftMouseButtonUp(object sender, MouseButtonEventArgs e) { 
     (sender as UIElement).ReleaseMouseCapture(); 
    } 

    private void OnMouseMove(object sender, MouseEventArgs e) { 
     if ((sender as UIElement).IsMouseCaptureWithin) { 
      var pos = e.GetPosition(Content); 
      BorderThumb.RenderTransform = new TranslateTransform(pos.X, pos.Y); 
      BorderToResize.Height = pos.Y; 
      BorderToResize.Width = pos.X; 
     } 
    } 
+0

TranslateTransform은 어떤 이유로 (나는 필요한) 마진을 변경하지 않습니다. 하지만 다른 용도는 무엇인지 잘 모릅니다. 또한 움직이는 것이 좋습니다. 방금 크기 조정 솔루션이 필요합니다. –

+0

죄송합니다. 크기를 묻는 질문을 읽지 않았습니다. 방금 코드를 살펴 봤습니다. 그러나 크기 조정은 그리드보다 캔버스에서 달라야합니다. Adorner (Thumb) 또는 UIElement를 클릭하고 크기를 조정할 영역이 필요하며 원래의 종횡비를 일정하게 유지하면서 새 높이와 너비를 설정하는 것보다 .... 이런 식으로도 Grid 부모 용으로 작동해야합니다. https ://denisvuyka.wordpress.com/2007/10/15/wpf-simple-adderner-usage-with-drag-and-resize-operations/ – Markus

+0

장식가가 캔버스에서만 사용되었다는 것이 내 인상이었습니다. 그리드에 대한 코드가 있습니까? –

관련 문제