2017-12-09 105 views
6

Şu anda çalışma zamanında dinamik olarak eklenen denetimlere sahip bir tam ekran Grid içeren bir C# WPF uygulaması üzerinde çalışıyorum. Kullanıcının bu denetimleri Grid arasında fare olayları ile taşımasına izin verecek bir kod var. Şimdi yapmak istediğim, kullanıcının çalışma zamanında da kontrollerini (en boy oranını korurken) yeniden boyutlandırmasına izin vermesidir. Bunu bir Canvas (ve Thumb denetimleri) kullanarak nasıl yapılacağını açıklayan çeşitli öğreticiler gördüm, ancak hiçbiri Grid. Uygulamamda bir Canvas kullanamadığımdan, bunu bir ızgara üzerinde uygulamak için verimli bir yol var mı? Size kod neye benzediğini hakkında bir fikir vermek amacıyla aşağıda, benim fare olayları yerleştirilen: [aşağıda Düzenlendi] Çalışma zamanında bir ızgarada WPF denetimlerini yeniden boyutlandırma (en boy oranını koruyarak)

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 (örnek):

<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" /> 

Düzenleme: TranslateTransform kullanarak bir denetimin marjı değişmez bulduk. Bunun işe yaraması için uygun şekilde değişmesi gereken marja ihtiyacım var.

Düzenleme 2: Şimdi yeniden boyutlandırma yapan bir adorner için [değiştirilmiş] kodunu ekledim (here bulundu). Neredeyse işe yarıyor. Sorun, süsleyicilerle garip davranışlar yaşadığım. Kontrollerimden birinde (sol üst köşede), 4 tanesi uygun köşelerinde görünür. Ama benim kontrollerimin geri kalanı sadece 1-2 ad hocasını alıyor ve doğru şekilde yerleştirilmemişler. Bunlarla davranış da yabancı. Evet, bunun çok fazla kod olduğunu anlıyorum ama sorunun ResizingAdorner sınıfında olacağını düşünürdüm.

Düzenleme 3: & yapıştırmını kopyalamak isteyenler için 'boilerplate' kodu eklendi. Problemsiz derler. Herhangi bir sorununuz olursa lütfen bize bildirin.

Edit 4 (1/10/2018): Yine de iyi cevap yok. Kontrolörün Margin değeri 0,01 ise, adorner üzerindeki Thumb kontrollerinin kendilerini doğru olarak hizaladığı görülüyor. Bu pozisyondan taşındığında, süslemeler elemandan dışarı çıkar.

Düzenleme 5 (1/15/2018): Adorner sınıfı, bir Canvas için tasarlanmış ve bir Grid üzerinde çalışan sorunlara katkıda bulunabilir. En iyi tahminim, bu nedenle ArrangeOverride yönteminin (UIElement numaralarına yerleştirildiği yer) karışıklık yaratmasıdır.

+0

Sorunuz çok geniş. Lütfen bir şeyi uygulamaya çalışın ve bazı ** ** sorunlarıyla karşılaşırsanız bize bildirin. – dymanoid

+0

@dymanoid Bu soruyu sordum çünkü araştırdım ve uygulamak için herhangi bir çözüm bulamadım. Olaylarıma kod koydum ve ne yaptığını bilen herkes bana doğru yönde işaret edebilir. –

+0

Neden Canvas kullanamazsınız? Bu görevler için iyi bir paneldir – Netstep

cevap

0

Günümüzde bazı mahkemeden sonra ve erro r, Adorner hatayı nasıl düzeltebileceğimi anlayabildim. Gönderdiğim gibi, öğeleri yeniden boyutlandırmak için kullanılan köşe Başlığı, denetimlere düzgün şekilde uymuyordu.Orijinal kod, kullanıyorum Grid konteyner yerine bir Canvas kullanım için tasarlanmıştır. Hata, ArrangeOverride yöntemindeki bazı double değerlerinde idi (Thumbs'u düzenler). Özgün kodu:

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

Özellikle Thumb ler için gerçek boyutlarını sağlayan değildi düzenlemek için kullanılan desiredWidth ve desiredHeight çift değişkenleri görünüyor: İçine değiştirilmiş

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.... 

... kontrol. Bu değişiklikle kodun kullanılması işlevseldir, ancak sol üst, sağ üst ve alt sol reklam öğeleri için hala oldukça belirsizdir. Kendimi değiştirebileceğime inandığım bir şey. Geri bildirim sağlayan herkese teşekkürler.

Link to original (canvas) code

1

yeniden boyutlandırma:

<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> 

Kod Arkası:

Belki bu

XAML ... bir marş olduğunu

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, bir sebepten dolayı marjı değiştirmez (ihtiyacım olan). Yine de başka ne kullanacağından emin değilim. Ayrıca, hareket iyi çalışıyor. Yeniden boyutlandırma çözümüne ihtiyacım var. –

+0

Üzgünüz, sadece baktığım yeniden boyutlandırma sorusunu okumadım. Ancak yeniden boyutlandırma, bir tuvalde ızgaradan farklı olmamalıdır. Bir Adorner'a (Thumb) veya UIElement'in tıklanıp yeniden boyutlandırılacağı bir alana ve sadece orijinal en boy oranını sabitleyen yeni yüksekliği ve genişliğini ayarlamanız gereken bir alana ihtiyacınız var .... Bunun gibi bir şey de Grid Veli için çalışmalıdır: https://denisvuyka.wordpress.com/2007/10/15/wpf-simple-adorner-usage-with-drag-and-resize-operations/ – Markus

+0

Süslemelerin sadece Tuvallerde kullanıldığı izlenimiydi. Bir ızgara için kodunuz var mı? –

İlgili konular