현재 런타임에 컨트롤에 동적으로 추가되는 전체 화면 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
에있는 곳이기 때문에).
귀하의 질문은 너무 광범위합니다. ** 특정 ** 문제가 발생하면 무언가를 구현하고 알려주십시오. – dymanoid
@dymanoid 나는이 문제를 물었습니다. 왜냐하면 제가 연구했고 전혀 구현할 해결책을 찾지 못했기 때문입니다. 내 이벤트에 대한 코드를 제공했으며, 그들이하는 일을 아는 사람은 나를 올바른 방향으로 안내 할 수 있습니다. –
Canvas를 사용할 수없는 이유는 무엇입니까? 그러한 작업을위한 좋은 패널입니다. – Netstep