views:

403

answers:

3

I'm trying to write a panel class from this two resources:

The panel class will have two attached properties "X" and "Y" and if any element gives x and y to be zero then it will be placed on the center of the Panel. The panel will also let the user to drag things around . Please help me write this class. I'm very new to WPF.


This is how far I've come. Now I tried to implement this but its not working, if you can help me implement the GetTop,GetLeft,GetBottom,GetRight functions which are not by default defined in panel class and which are neccessary. If these 4 methods are present then dragging functions can be implemented here.

using System;
using System.Linq;
using System.Windows;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Media;

namespace SmartERP.Elements
{
    public class SmartCanvas : Panel
    {
        public static readonly DependencyProperty TopProperty;
        public static readonly DependencyProperty LeftProperty;
        public static readonly DependencyProperty BottomProperty;
        public static readonly DependencyProperty RightProperty;

        static SmartCanvas()
        {
            TopProperty = DependencyProperty.Register("Top", typeof(double), typeof(SmartCanvas), new PropertyMetadata(0.0));
            LeftProperty = DependencyProperty.Register("Left", typeof(double), typeof(SmartCanvas), new PropertyMetadata(0.0));
            BottomProperty = DependencyProperty.Register("Bottom", typeof(double), typeof(SmartCanvas), new PropertyMetadata(0.0));
            RightProperty = DependencyProperty.Register("Right", typeof(double), typeof(SmartCanvas), new PropertyMetadata(0.0));
        }

        public double Top
        {
            get { return (double)base.GetValue(TopProperty); }
            set { base.SetValue(TopProperty, value); }
        }

        public double Bottom
        {
            get { return (double)base.GetValue(BottomProperty); }
            set { base.SetValue(BottomProperty, value); }
        }

        public double Left
        {
            get { return (double)base.GetValue(LeftProperty); }
            set { base.SetValue(LeftProperty, value); }
        }

        public double Right
        {
            get { return (double)base.GetValue(RightProperty); }
            set { base.SetValue(RightProperty, value); }
        }

       private double GetTop(UIElement element)
        {
            return (double)this.GetValue(TopProperty);
        }

        private double GetLeft(UIElement element)
        {
            return (double)this.GetValue(LeftProperty);
        }

        private double GetBottom(UIElement element)
        {
            return (double)this.GetValue(BottomProperty);
        }

        private double GetRight(UIElement element)
        {
            return (double)this.GetValue(RightProperty);
        }

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            Point middle = new Point(arrangeSize.Width / 2, arrangeSize.Height / 2);

            foreach (UIElement element in base.InternalChildren)
            {
                if (element == null)
                {
                    continue;
                }
                double x = 0.0;
                double y = 0.0;
                double left = GetLeft(element);
                if (!double.IsNaN(left))
                {
                    x = left;
                }

                double top = GetTop(element);
                if (!double.IsNaN(top))
                {
                    y = top;
                }

                element.Arrange(new Rect(new Point(middle.X + x, middle.Y + y), element.DesiredSize));
            }
            return arrangeSize;
        }
    }
}
+1  A: 

The panel class will have two attached properties "X" and "Y" [...]

OK, then you should implement those attached properties. See the example in Section Custom Attached Properties of the Attached Properties Overview on MSDN. Here's how this would look for X:

public static readonly DependencyProperty XProperty =
    DependencyProperty.RegisterAttached("X", typeof(double),
        typeof(SmartCanvas), new FrameworkPropertyMetadata(0.0));

public static void SetX(UIElement element, double value) { element.SetValue(XProperty, value); }
public static double GetX(UIElement element) { return (double)element.GetValue(XProperty); }

Once you have done this, you have GetX and GetY, which is probably what you mean by GetTop and GetLeft.

Heinzi
Now please I'm using the RegisterAttached but I'm still not able to drag things around the canvas and not place any element's center on canvas center. Please help and if you can suggest any better ways to place things on canvas center by attached properties then please tell me how to implement it. Please help.
Soham Dasgupta
Why should you be able to drag and drop stuff around --- your code does not contain anything related to drag and drop! Maybe a short clarification is in order: People here will gladly help you (a) find bugs in your code, (b) get you on the right track to solve a problem and (c) help you understand WPF concepts. What you probably *won't* get is: "I need a program which does X, please write it for me."
Heinzi
OK, to get back to your problem: Why do you use a Panel instead of a Canvas as the base class? The dragging example you linked in your question needs the Left, Top etc. attached properties of `Canvas`, which probably have some code attached to them (so you cannot create the same effect by using a Panel and re-creating these attached properties).
Heinzi
No no getting the code written was not my intention, I'm sorry if I've put it that way. But the thing is If use the drag concept and the centering conept on the same canvas class everything is getting messed up. I'm not able to do either. Can you suggest any good ways to put elements on canvas center with attached properties. What I need is a guide to not the program. Please don't misunderstand me.
Soham Dasgupta
A: 

This is my new class I'm able to add elements to the panel but the drag behaviour is not working. And I want the child center to be on panel center throught the attached properties TOP=0 And LEFT=0. Is there any better way to place child elements on the center through the use of attached properties.

 public class SmartCanvas : Panel
 { 
  public static readonly DependencyProperty TopProperty;
  public static readonly DependencyProperty LeftProperty;
  public static readonly DependencyProperty BottomProperty;
  public static readonly DependencyProperty RightProperty;
  public static readonly DependencyProperty AllowDraggingProperty;
  public static readonly DependencyProperty AllowDragOutOfViewProperty;
  public static readonly DependencyProperty CanBeDraggedProperty;

  private UIElement elementBeingDragged;
  private Point origCursorLocation;
  private double origHorizOffset, origVertOffset;
  private bool modifyLeftOffset, modifyTopOffset;
  private bool isDragInProgress;

  static SmartCanvas()
  {
      TopProperty = DependencyProperty.RegisterAttached("Top", typeof(double), typeof(SmartCanvas), new FrameworkPropertyMetadata(0.0));
      LeftProperty = DependencyProperty.RegisterAttached("Left", typeof(double), typeof(SmartCanvas), new FrameworkPropertyMetadata(0.0));
      BottomProperty = DependencyProperty.RegisterAttached("Bottom", typeof(double), typeof(SmartCanvas), new FrameworkPropertyMetadata(0.0));
      RightProperty = DependencyProperty.RegisterAttached("Right", typeof(double), typeof(SmartCanvas), new FrameworkPropertyMetadata(0.0));
      AllowDraggingProperty = DependencyProperty.Register("AllowDragging", typeof(bool), typeof(SmartCanvas), new PropertyMetadata(true));
      AllowDragOutOfViewProperty = DependencyProperty.Register("AllowDragOutOfView", typeof(bool), typeof(SmartCanvas), new UIPropertyMetadata(false));
      CanBeDraggedProperty = DependencyProperty.RegisterAttached("CanBeDragged", typeof(bool), typeof(SmartCanvas), new UIPropertyMetadata(true));
  }

  public static void SetTop(UIElement element, double value)
  {
      element.SetValue(TopProperty, value);
  }

  public static void SetBottom(UIElement element, double value)
  {
      element.SetValue(BottomProperty, value);
  }

  public static void SetLeft(UIElement element, double value)
  {
      element.SetValue(LeftProperty, value);
  }

  public static void SetRight(UIElement element, double value)
  {
      element.SetValue(RightProperty, value);
  }

  public static double GetTop(UIElement element)
  {
      return (double)element.GetValue(TopProperty);
  }

  public static double GetBottom(UIElement element)
  {
      return (double)element.GetValue(BottomProperty);
  }

  public static double GetLeft(UIElement element)
  {
      return (double)element.GetValue(LeftProperty);
  }

  public static double GetRight(UIElement element)
  {
      return (double)element.GetValue(RightProperty);
  }

  public static bool GetCanBeDragged(UIElement uiElement)
  {
      if (uiElement == null) return false;
      return (bool)uiElement.GetValue(CanBeDraggedProperty);
  }

  public static void SetCanBeDragged(UIElement uiElement, bool value)
  {
      if (uiElement != null)
          uiElement.SetValue(CanBeDraggedProperty, value);
  }

  public bool AllowDragging
  {
      get { return (bool)base.GetValue(AllowDraggingProperty); }
      set { base.SetValue(AllowDraggingProperty, value); }
  }

  public bool AllowDragOutOfView
  {
      get { return (bool)GetValue(AllowDragOutOfViewProperty); }
      set { SetValue(AllowDragOutOfViewProperty, value); }
  }

  public void BringToFront(UIElement element)
  {
      this.UpdateZOrder(element, true);
  }

  public void SendToBack(UIElement element)
  {
      this.UpdateZOrder(element, false);
  }

  public UIElement ElementBeingDragged
  {
      get
      {
          if (!this.AllowDragging)
          {
              return null;
          }
          else
          {
              return this.elementBeingDragged;
          }
      }
      protected set
      {
          if (this.elementBeingDragged != null)
          {
              this.elementBeingDragged.ReleaseMouseCapture();
          }

          if (!this.AllowDragging)
          {
              this.elementBeingDragged = null;
          }
          else
          {
              if (SmartCanvas.GetCanBeDragged(value))
              {
                  this.elementBeingDragged = value;
                  this.elementBeingDragged.CaptureMouse();
              }
              else
              {
                  this.elementBeingDragged = null;
              }
          }
      }
  }

  public UIElement FindCanvasChild(DependencyObject depObj)
  {
      while (depObj != null)
      {
          UIElement elem = depObj as UIElement;
          if (elem != null && base.Children.Contains(elem))
              break;

          if (depObj is Visual || depObj is Visual3D)
              depObj = VisualTreeHelper.GetParent(depObj);
          else
              depObj = LogicalTreeHelper.GetParent(depObj);
      }
      return depObj as UIElement;
  }

  protected override Size ArrangeOverride(Size arrangeSize)
  {
      Point middle = new Point(arrangeSize.Width / 2, arrangeSize.Height / 2);

      foreach (UIElement element in base.InternalChildren)
      {
          if (element == null)
          {
              continue;
          }
          double x = 0.0;
          double y = 0.0;
          double left = SmartCanvas.GetLeft(element);
          if (!double.IsNaN(left))
          {
              x = left;
          }

          double top = SmartCanvas.GetTop(element);
          if (!double.IsNaN(top))
          {
              y = top;
          }

          element.Arrange(new Rect(new Point(middle.X + x, middle.Y + y), element.DesiredSize));
      }
      return arrangeSize;
  }

  protected override Size MeasureOverride(Size availableSize)
  {
      Size resultSize = new Size(0, 0);

      foreach (UIElement child in Children)
      {
          child.Measure(availableSize);
          resultSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
          resultSize.Height = Math.Max(resultSize.Height, child.DesiredSize.Height);
      }

      resultSize.Width = double.IsPositiveInfinity(availableSize.Width) ?
          resultSize.Width : availableSize.Width;

      resultSize.Height = double.IsPositiveInfinity(availableSize.Height) ?
          resultSize.Height : availableSize.Height;

      return resultSize;
  }

  protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
  {
      base.OnPreviewMouseLeftButtonDown(e);

      this.isDragInProgress = false;

      this.origCursorLocation = e.GetPosition(this);

      this.ElementBeingDragged = this.FindCanvasChild(e.Source as DependencyObject);
      if (this.ElementBeingDragged == null)
          return;

      double left = SmartCanvas.GetLeft(this.ElementBeingDragged);
      double right = SmartCanvas.GetRight(this.ElementBeingDragged);
      double top = SmartCanvas.GetTop(this.ElementBeingDragged);
      double bottom = SmartCanvas.GetBottom(this.ElementBeingDragged);

      this.origHorizOffset = ResolveOffset(left, right, out this.modifyLeftOffset);
      this.origVertOffset = ResolveOffset(top, bottom, out this.modifyTopOffset);

      e.Handled = true;

      this.isDragInProgress = true;
  }

  protected override void OnPreviewMouseMove(MouseEventArgs e)
  {
      base.OnPreviewMouseMove(e);

      if (this.ElementBeingDragged == null || !this.isDragInProgress)
          return;

      Point cursorLocation = e.GetPosition(this);

      double newHorizontalOffset, newVerticalOffset;

      if (this.modifyLeftOffset)
          newHorizontalOffset = this.origHorizOffset + (cursorLocation.X - this.origCursorLocation.X);
      else
          newHorizontalOffset = this.origHorizOffset - (cursorLocation.X - this.origCursorLocation.X);

      if (this.modifyTopOffset)
          newVerticalOffset = this.origVertOffset + (cursorLocation.Y - this.origCursorLocation.Y);
      else
          newVerticalOffset = this.origVertOffset - (cursorLocation.Y - this.origCursorLocation.Y);

      if (!this.AllowDragOutOfView)
      {
          Rect elemRect = this.CalculateDragElementRect(newHorizontalOffset, newVerticalOffset);
          bool leftAlign = elemRect.Left < 0;
          bool rightAlign = elemRect.Right > this.ActualWidth;

          if (leftAlign)
              newHorizontalOffset = modifyLeftOffset ? 0 : this.ActualWidth - elemRect.Width;
          else if (rightAlign)
              newHorizontalOffset = modifyLeftOffset ? this.ActualWidth - elemRect.Width : 0;

          bool topAlign = elemRect.Top < 0;
          bool bottomAlign = elemRect.Bottom > this.ActualHeight;

          if (topAlign)
              newVerticalOffset = modifyTopOffset ? 0 : this.ActualHeight - elemRect.Height;
          else if (bottomAlign)
              newVerticalOffset = modifyTopOffset ? this.ActualHeight - elemRect.Height : 0;
      }

      if (this.modifyLeftOffset)
      {
          SmartCanvas.SetLeft(this.ElementBeingDragged, newHorizontalOffset);
      }
      else
      {
          SmartCanvas.SetRight(this.ElementBeingDragged, newHorizontalOffset);
      }

      if (this.modifyTopOffset)
      {
          SmartCanvas.SetTop(this.ElementBeingDragged, newVerticalOffset);
      }
      else
      {
          SmartCanvas.SetBottom(this.ElementBeingDragged, newVerticalOffset);
      }

  }

  protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
  {
      base.OnPreviewMouseUp(e);
      this.ElementBeingDragged = null;
  }

  private Rect CalculateDragElementRect(double newHorizOffset, double newVertOffset)
  {
      if (this.ElementBeingDragged == null)
      {
          throw new InvalidOperationException("ElementBeingDragged is null.");
      }

      Size elemSize = this.ElementBeingDragged.RenderSize;

      double x, y;

      if (this.modifyLeftOffset)
      {
          x = newHorizOffset;
      }
      else
      {
          x = this.ActualWidth - newHorizOffset - elemSize.Width;
      }

      if (this.modifyTopOffset)
      {
          y = newVertOffset;
      }
      else
      {
          y = this.ActualHeight - newVertOffset - elemSize.Height;
      }

      Point elemLoc = new Point(x, y);

      return new Rect(elemLoc, elemSize);
  }

  private static double ResolveOffset(double side1, double side2, out bool useSide1)
  {
      useSide1 = true;
      double result;
      if (Double.IsNaN(side1))
      {
          if (Double.IsNaN(side2))
          {
              result = 0;
          }
          else
          {
              result = side2;
              useSide1 = false;
          }
      }
      else
      {
          result = side1;
      }
      return result;
  }

  private void UpdateZOrder(UIElement element, bool bringToFront)
  {
      if (element == null)
          throw new ArgumentNullException("element");

      if (!base.Children.Contains(element))
          throw new ArgumentException("Must be a child element of the Canvas.", "element");

      int elementNewZIndex = -1;
      if (bringToFront)
      {
          foreach (UIElement elem in base.Children)
              if (elem.Visibility != Visibility.Collapsed)
                  ++elementNewZIndex;
      }
      else
      {
          elementNewZIndex = 0;
      }

      int offset = (elementNewZIndex == 0) ? +1 : -1;

      int elementCurrentZIndex = SmartCanvas.GetZIndex(element);

      foreach (UIElement childElement in base.Children)
      {
          if (childElement == element)
          {
              SmartCanvas.SetZIndex(element, elementNewZIndex);
          }
          else
          {
              int zIndex = SmartCanvas.GetZIndex(childElement);

              if (bringToFront && elementCurrentZIndex < zIndex || !bringToFront && zIndex < elementCurrentZIndex)
              {
                  SmartCanvas.SetZIndex(childElement, zIndex + offset);
              }
          }
      }

  }

}

Soham Dasgupta
I still don't understand: Why do you need "Right" and "Bottom"? Why do you register your attached properties with `.Register` rather than `.RegisterAttached`?
Heinzi
Now please I'm using the RegisterAttached but I'm still not able to drag things around the canvas and not place any element's center on canvas center. Please help and if you can suggest any better ways to place things on canvas center by attached properties then please tell me how to implement it.
Soham Dasgupta
A: 

Actually I found a solution Centering On Canvas. But the problem is I cannot use the same thing via C# because the canvas left and top values are returning zero and throwing an exception.Not always will I place elements on canvas via xaml, so how can I use C# to implement this. Please help.

<Canvas.Left>
 <MultiBinding Converter="{StaticResource MidValue}"
               ConverterParameter="1">
   <Binding ElementName="cnvMain2"
            Path="ActualWidth" />
   <Binding ElementName="tbSize2"
            Path="ActualWidth" />
 </MultiBinding>
</Canvas.Left>
<Canvas.Top>
 <MultiBinding Converter="{StaticResource MidValue}"
               ConverterParameter="7">
   <Binding ElementName="cnvMain2"
            Path="ActualHeight" />
   <Binding ElementName="tbSize2"
            Path="ActualHeight" />
 </MultiBinding>
</Canvas.Top>
Soham Dasgupta