none
Move / Resize Parent relative to Child Control Position Size

    Question

  • Hi All,

    I am looking to implement a Canvas like control that resizes itself based on the size and relative position of its child controls.  All child controls have "Left", "Top", Width and Height properties, like Canvas, and would initially be arrange using these.  After their initial arrangement, the user can resize or translate the children within space of their parent.  However, as child elements are transformed, the parent control must resize to fit the bounds of all of its children.  In addition, when the parent control resizes, the position and size of the child controls must stay fixed in screen space.  In other words, when the parent is being resized because a child is outside of its bounds, the only control that should be moving, in screen space, should be the child control being modified and the parent, which is resizing.  Finally, there would be several of these controls imbedded in each other.  

    As i understand things, most of the requirements mentioned above can be filled with the Canvas control; the user can scale and transform child elements and the canvas can "grow" to squeeze in children.  However the one problem i'm having is repositioning the Canvas.  

    Say, for example, one of the children moves off of the left side of the canvas (Canvas.GetLeft(child) < 0).  How can this be handled and in what method (measure or arrange)?  The issue with this case, as well as where the child moves off the top of the canvas (Canvas.GetTop(child) < 0), is that the Canvas must:

       #1 Move to the left by the overlap amount
       #2 Grow in width by the overlap amount
       #3 Move all children, except the overlapping child, to the right by the overlap amount

    This seems simple enough.  But how can this be done in the context of the layout system?  

    From what i understand there are two passes, measure and arrange, which deal only with child controls.  Arrange almost works, because the full bounds can be provided, but only for children of the control.  What seems to be the fit for the above is a pass that allows the control the specify the bounds it needs to have to fill in its children.

    I've gotten this to work where the canvas always has a small constant size (e.g. 10,10) and then grows one of its children (a border).  It visually looks correct but mouse overlap and click events are registered to the border which isn't clean.

    Does any one have a suggestion for a simpler way to implement a control like this?
    Friday, November 14, 2008 5:01 AM

All replies

  • Hi All,

    The following seems to be a solution to what i was looking for.

    C# (not posting as "code" because its over the character limit):

    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Data;

    namespace
    {
        /// <summary>
        /// DOC
        /// </summary>
        public abstract class Node : Control
        {
            #region Enumerations
           
            /// <summary>
            /// IDs for the thumbs associated with this node.
            /// </summary>
            protected enum Thumbs
            {
                LeftThumb = 0,
                TopLeftThumb,
                TopThumb,
                TopRightThumb,
                RightThumb,
                BottomRightThumb,
                BottomThumb,
                BottomLeftThumb
            }

            #endregion

            #region Construction

            /// <summary>
            /// DOC
            /// </summary>
            public Node(Node parent)
                :base()
            {
                DefaultStyleKey = typeof(Node);

                X = 0;
                Y = 0;

                m_parent = parent;
            }

            #region Static Construction
           
            /// <summary>
            /// Static constructor for Node class. 
            /// </summary>
            /// <remarks>
            /// Initializes class specific sytle properties for all Nodes with WPF templated style system.  The default
            /// style for the node must be specified in a static constructor to get style overrides to work correctly in
            /// generic.xaml.
            /// </remarks>
            static Node()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(Node), new FrameworkPropertyMetadata(typeof(Node)));
            }

            #endregion

            #endregion

            #region Properties

            /// <summary>
            /// The horizontal position of the node if placed on a canvas.  Typcially the node is placed on the canvas control
            /// of its parent so this is the horizontal position in the space of the parents canvas.
            /// </summary>
            public double X
            {
                get { return Canvas.GetLeft(this); }
                set { Canvas.SetLeft(this, value); }
            }

            /// <summary>
            /// The vertical position of the node if placed on a canvas.  Typcially the node is placed on the canvas control
            /// of its parent so this is the vertical position in the space of the parents canvas.
            /// </summary>
            public double Y
            {
                get { return Canvas.GetTop(this); }
                set { Canvas.SetTop(this, value); }
            }

            /// <summary>
            /// Gets or sets the name of the node
            /// </summary>
            public string NodeName
            {
                get { return (string)GetValue(NodeNameProperty); }
                set { SetValue(NodeNameProperty, value); }           
            }

            /// <summary>
            /// Gets the children associated with this node
            /// </summary>
            public Node[] NodeChildren
            {
                get { return m_children; }
            }

            #endregion               

            #region Methods

            /// <summary>
            /// Builds child Nodes for the Node.
            /// </summary>
            /// <returns></returns>
            protected abstract Node[] EnumerateChildren();  

            #region Mouse & Keyboard Input

            /// <summary>
            /// Captures and does setup for Node dragging.
            /// </summary>
            /// <param name="e">Arguments for the event.</param>
            protected override void OnMouseDown(MouseButtonEventArgs e)
            {
                m_lastMousePosition = e.GetPosition(null);

                CaptureMouse();

                e.Handled = true;
            }

            /// <summary>
            /// Releases and does shutdown for Node dragging.
            /// </summary>
            /// <param name="e">Arguments for the event.</param>
            protected override void OnMouseUp(MouseButtonEventArgs e)
            {
                m_lastMousePosition = new Point(0, 0);

                ReleaseMouseCapture();

                e.Handled = true;
            }

            /// <summary>
            /// Does Node dragging.
            /// </summary>
            /// <param name="e">Arguments for the event.</param>
            protected override void OnMouseMove(MouseEventArgs e)
            {
                if (IsMouseCaptured)
                {
                    Point currentMousePosition = e.GetPosition(null);

                    Translate(currentMousePosition - m_lastMousePosition);

                    m_lastMousePosition = e.GetPosition(null);

                    e.Handled = true;
                }
            }

            /// <summary>
            /// DOC
            /// </summary>
            /// <param name="thumb"></param>
            /// <param name="type"></param>
            protected void InitializeThumb(Thumb thumb, Thumbs type)
            {
                thumb.MouseEnter += new MouseEventHandler(OnStartResize);
                thumb.MouseLeave += new MouseEventHandler(OnStopReisze);

                switch (type)
                {
                    case Thumbs.LeftThumb:
                        thumb.Cursor = Cursors.SizeWE;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeLeft);
                        break;
                    case Thumbs.TopLeftThumb:
                        thumb.Cursor = Cursors.SizeNWSE;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeTopLeft);
                        break;
                    case Thumbs.TopThumb:
                        thumb.Cursor = Cursors.SizeNS;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeTop);
                        break;
                    case Thumbs.TopRightThumb:
                        thumb.Cursor = Cursors.SizeNESW;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeTopRight);
                        break;
                    case Thumbs.RightThumb:
                        thumb.Cursor = Cursors.SizeWE;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeRight);
                        break;
                    case Thumbs.BottomRightThumb:
                        thumb.Cursor = Cursors.SizeNWSE;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeBottomRight);
                        break;
                    case Thumbs.BottomThumb:
                        thumb.Cursor = Cursors.SizeNS;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeBottom);
                        break;
                    case Thumbs.BottomLeftThumb:
                        thumb.Cursor = Cursors.SizeNESW;
                        thumb.DragDelta += new DragDeltaEventHandler(OnResizeBottomLeft);
                        break;
                }
            }

            /// <summary>
            /// DOC
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void OnStopReisze(object sender, MouseEventArgs e)
            {
                m_isResizing = false;
            }

            /// <summary>
            /// DOC
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void OnStartResize(object sender, MouseEventArgs e)
            {
                m_isResizing = true;
            }

            /// <summary>
            /// Resizes the node from the bottom left
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeBottomLeft(object sender, DragDeltaEventArgs e)
            {
                Resize(e.HorizontalChange, 0, 0, e.VerticalChange);
            }

            /// <summary>
            /// Resizes the node from the bottom
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeBottom(object sender, DragDeltaEventArgs e)
            {
                Resize(0, 0, 0, e.VerticalChange);
            }

            /// <summary>
            /// Resizes the node from the bottom right
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeBottomRight(object sender, DragDeltaEventArgs e)
            {
                Resize(0, e.HorizontalChange, 0, e.VerticalChange);
            }

            /// <summary>
            /// Resizes the node from the right
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeRight(object sender, DragDeltaEventArgs e)
            {
                Resize(0, e.HorizontalChange, 0, 0);
            }

            /// <summary>
            /// Resizes the node from the top right
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeTopRight(object sender, DragDeltaEventArgs e)
            {
                Resize(0, e.HorizontalChange, e.VerticalChange, 0);
            }

            /// <summary>
            /// Resizes the node from the top
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeTop(object sender, DragDeltaEventArgs e)
            {
                Resize(0, 0, e.VerticalChange, 0);
            }

            /// <summary>
            /// Resizes the node from the top left
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeTopLeft(object sender, DragDeltaEventArgs e)
            {
                Resize(e.HorizontalChange, 0, e.VerticalChange, 0);
            }

            /// <summary>
            /// Resizes the node from the left
            /// </summary>
            /// <param name="sender">Sender of the event</param>
            /// <param name="e">Event arguments</param>
            void OnResizeLeft(object sender, DragDeltaEventArgs e)
            {
                Resize(e.HorizontalChange, 0, 0, 0);
            }

            #endregion

            #region Arrange & Measure

            /// <summary>
            /// Measures the Nodes size for the layout system.
            /// </summary>
            /// <param name="constraint">Size constraint the node must fit within.</param>
            /// <returns>The size of the control.  This size is the maximum of the size required by all children and the
            /// Nodes current size.  This forces the control to maintain its size until the user resizes it manually.</returns>
            protected override Size MeasureOverride(Size constraint)
            {
                Rect bounds = new Rect(0, 0, MinWidth, MinHeight);

                for (int i = 0; i < VisualChildrenCount; ++i)
                {
                    UIElement child = GetVisualChild(i) as UIElement;

                    if (child != null)
                    {
                        child.Measure(constraint);
                        bounds.Width = Math.Max(bounds.Width, child.DesiredSize.Width);
                        bounds.Height = Math.Max(bounds.Height, child.DesiredSize.Height);
                    }
                }

                if (m_isResizing)
                {
                    return bounds.Size;
                }
                else
                {
                    return new Size(Math.Max(ActualWidth, bounds.Width), Math.Max(ActualHeight, bounds.Height));
                }
            }

            /// <summary>
            /// Resizes this node based on the position and size of its children.
            /// </summary>
            /// <remarks>
            /// The Node class has a Canvas control, m_childNodeCanvas, where child Nodes are arranged.  TResizeToFitChildNodes
            /// ensures that all child Nodes fit within the bounds of this Canvas.  If a child Node is outside of the bounds
            /// of the m_childNodeCanvas this Node is re-sized and or re-positioned to include all of its child Nodes.  If a
            /// child Node is over the width or height bounds, the m_childNodeCanvas is expanded to include them.  If a child
            /// Node is over the left or top bounds, this Node is shifted by the penetration depth, all child Nodes are moved
            /// in the opposite direction of the penetration on m_childNodeCanvas and the size of this Node is expanded by the
            /// penetration depth.  This gives the effect that child Nodes stay fixed, in screen space, while the parent Node
            /// expands to include them.  Also, this node is not shrunk to fit all of children (i.e. rubber banding); this Node
            /// will maintain its maximum size until the user shrinks it by resizing.
            /// </remarks>
            protected void ResizeToChildNodes()
            {
                // Flag if we need to resize this nodes parent
                bool resizeParent = false;

                // Find the bounds of all children
                Rect childBounds = new Rect(0, 0, MinWidth, MinHeight);
                {
                    foreach (Node child in m_children)
                    {
                        childBounds.X = Math.Min(childBounds.X, child.X);
                        childBounds.Y = Math.Min(childBounds.Y, child.Y);
                        childBounds.Width = Math.Max(childBounds.Width, child.X + child.DesiredSize.Width);
                        childBounds.Height = Math.Max(childBounds.Height, child.Y + child.DesiredSize.Height);
                    }
                }

                // Shift the canvas, and this control, left and all child nodes right if required
                if (childBounds.X < 0)
                {
                    resizeParent = true;

                    X += childBounds.X;

                    foreach (Node child in m_children)
                    {
                        child.X -= childBounds.X;
                    }
                }

                // Shift the canvas, and this control, up and all child nodes down if required
                if (childBounds.Y < 0)
                {
                    resizeParent = true;

                    Y += childBounds.Y;

                    foreach (Node child in m_children)
                    {
                        child.Y -= childBounds.Y;
                    }
                }

                // Finally set the node canvas's size
                if (m_isResizing)
                {
                    if( m_childNodeCanvas.Width == Double.NaN )
                        m_childNodeCanvas.Width = childBounds.Width - (childBounds.X < 0 ? childBounds.X : 0);
                    if( m_childNodeCanvas.Height == Double.NaN )
                        m_childNodeCanvas.Height = childBounds.Height - (childBounds.Y < 0 ? childBounds.Y : 0);
                }
                else
                {
                    m_childNodeCanvas.Width = Math.Max(m_childNodeCanvas.ActualWidth, childBounds.Width) - (childBounds.X < 0 ? childBounds.X : 0);
                    m_childNodeCanvas.Height = Math.Max(m_childNodeCanvas.ActualHeight, childBounds.Height) - (childBounds.Y < 0 ? childBounds.Y : 0);
                }

                if (m_childNodeCanvas.Width > m_childNodeCanvas.ActualWidth ||
                    m_childNodeCanvas.Height > m_childNodeCanvas.ActualHeight)
                {
                    resizeParent = true;
                }

                // Resize the parent if required
                if (resizeParent && m_parent != null)
                {
                    m_parent.ResizeToChildNodes();
                }
            }

            /// <summary>
            /// Resizes the node
            /// </summary>
            /// <remarks>
            /// When resizing only one direction on each dimension can be specified.  For, example, the user can resize
            /// left but not resize left and right.  Also when resizing the Node is resized such that it does not overlap
            /// with any of its child Nodes.  If this happens the bounds of the Node will be set to where it does not
            /// overlap any children.
            ///
            /// Left, right, top and bottom are also units and not scalar values.
            /// </remarks>
            /// <param name="left">Amout to resize the left of the Node</param>
            /// <param name="right">Amount to resize the right of the Node</param>
            /// <param name="top">Amount to resize the top of the Node</param>
            /// <param name="bottom">Amount to resize the bottom of the Node</param>
            protected void Resize(double left, double right, double top, double bottom)
            {
                System.Diagnostics.Debug.Assert((left != 0 && right == 0) || (left == 0 && right != 0) || (left == 0 && right == 0), "Cannot resize left and right at the same time.");
                System.Diagnostics.Debug.Assert((top != 0 && bottom == 0) || (top == 0 && bottom != 0) || (top == 0 && bottom == 0), "Cannot resize top and bottom at the same time.");

                // Find the bounds of all children
                Rect childBounds = new Rect(0, 0, MinWidth, MinHeight);
                {
                    foreach (Node child in m_children)
                    {
                        childBounds.X = Math.Min(childBounds.X, child.X);
                        childBounds.Y = Math.Min(childBounds.Y, child.Y);
                        childBounds.Width = Math.Max(childBounds.Width, child.X + child.DesiredSize.Width);
                        childBounds.Height = Math.Max(childBounds.Height, child.Y + child.DesiredSize.Height);
                    }
                }

                // Resize left
                if (left != 0)
                {
                    double adjustLeftAmount = left;
                    double adjustWidthAmount = -left;

                    // If shrinking the node do not shink by more than the min size of the node
                    // and do not shink so that the node overlaps with its children
                    if (left > 0 )
                    {
                        // Clamp to width
                        if (m_childNodeCanvas.Width + adjustWidthAmount < childBounds.Width)
                        {
                            adjustWidthAmount = childBounds.Width - m_childNodeCanvas.Width;
                            adjustLeftAmount = -adjustWidthAmount;
                        }
     
                        // Limit adjustLeftAmoun to the max distance we can shift all child nodes
                        // to the right without them overlapping with m_childNodeCanvas
                        foreach (Node child in m_children)
                        {
                            double newLeft = child.X - adjustLeftAmount;

                            if (newLeft < 0)
                            {
                                adjustLeftAmount = adjustLeftAmount + newLeft;
                                adjustWidthAmount = -adjustLeftAmount;
                            }
                        }
                       
                        // Now shift all children to the right
                        foreach (Node child in m_children)
                        {
                            child.X -= adjustLeftAmount;
                        }
                    }

                    // If expanding left shift all nodes to the right
                    if (left < 0)
                    {
                        foreach (Node child in m_children)
                        {
                            child.X -= adjustLeftAmount;
                        }
                    }
                   
                    this.X += adjustLeftAmount;
                    m_childNodeCanvas.Width += adjustWidthAmount;
                }

                // Resize right
                if (right != 0)
                {
                    m_childNodeCanvas.Width = Math.Max(childBounds.Width, m_childNodeCanvas.Width + right);
                }

                // Resize top
                if (top != 0)
                {
                    double adjustTopAmount = top;
                    double adjustHeightAmount = -top;

                    // If shrinking the node do not shink by more than the min size of the node
                    // and do not shink so that the node overlaps with its children
                    if (top > 0)
                    {
                        // Clamp to width
                        if (m_childNodeCanvas.Height + adjustHeightAmount < childBounds.Height)
                        {
                            adjustHeightAmount = childBounds.Height - m_childNodeCanvas.Height;
                            adjustTopAmount = -adjustHeightAmount;
                        }

                        // Limit adjustLeftAmoun to the max distance we can shift all child nodes
                        // up without them overlapping with m_childNodeCanvas
                        foreach (Node child in m_children)
                        {
                            double newTop = child.Y - adjustTopAmount;

                            if (newTop < 0)
                            {
                                adjustTopAmount = adjustTopAmount + newTop;
                                adjustHeightAmount = -adjustTopAmount;
                            }
                        }

                        // Now shift all children up
                        foreach (Node child in m_children)
                        {
                            child.Y -= adjustTopAmount;
                        }
                    }

                    // If expanding up shift all nodes down
                    if (top < 0)
                    {
                        foreach (Node child in m_children)
                        {
                            child.Y -= adjustTopAmount;
                        }
                    }

                    Y += adjustTopAmount;
                    m_childNodeCanvas.Height += adjustHeightAmount;
                }

                // Resize bottom
                if (bottom != 0)
                {
                    m_childNodeCanvas.Height = Math.Max(childBounds.Height, m_childNodeCanvas.Height + bottom);
                }

                if (m_parent != null)
                {
                    ResizeToChildNodes();
                }
            }
           
            /// <summary>
            /// Translates the node by offset
            /// </summary>
            /// <param name="offset">Vector to offset the node by</param>
            public void Translate(Vector offset)
            {
                X += offset.X;
                Y += offset.Y;

                if (m_parent != null)
                {
                    m_parent.ResizeToChildNodes();
                }
            }

            #endregion

            #region Content Template

            /// <summary>
            /// Callback raised when content template is applied to the node.  Initializes child nodes for the
            /// control and adds them to the m_childNodeCanvas.
            /// </summary>
            public override void OnApplyTemplate()
            {
                // Call base method (see http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.onapplytemplate.aspx)
                base.OnApplyTemplate();

                // Initialize node children
                {
                    m_children = EnumerateChildren();

                    // Find child layout canvas
                    m_childNodeCanvas = Template.FindName("ChildNodeContainer", this) as Canvas;

                    if (m_childNodeCanvas == null)
                    {
                        throw new Exception("Node control must have a Canvas child named ChildNodeContainer");
                    }

                    // Add all children to the canvas
                    foreach (Node child in m_children)
                    {
                        m_childNodeCanvas.Children.Add(child);
                    }
                }

                // Initialize thumbs
                if (m_children.Length > 0)
                {
                    Array thumbEnums = Enum.GetValues(typeof(Node.Thumbs));
                    m_thumbs = new Thumb[thumbEnums.Length];

                    for (int i = 0; i < thumbEnums.Length; ++i)
                    {
                        m_thumbs[i] = Template.FindName(thumbEnums.GetValue(i).ToString(), this) as Thumb;

                        if (m_thumbs[i] != null)
                        {
                            InitializeThumb(m_thumbs[i], (Node.Thumbs)thumbEnums.GetValue(i));
                        }
                    }
                }

                // User layout
                {
                   
                }
            }

            #endregion

            #endregion

            #region Members

            /// <summary>
            /// Parent node of this Node.
            /// </summary>
            protected Node m_parent;

            /// <summary>
            /// List of child nodes associated with this node
            /// </summary>
            protected Node[] m_children = new Node[] { };

            /// <summary>
            /// Canvas used to layout child nodes
            /// </summary>
            protected Canvas m_childNodeCanvas;

            /// <summary>
            /// The last known mouse position.  Is set when the mouse is pressed over the control, updated during mouse
            /// move and reset to (0,0) when the mouse is released.  Last mouse position is always in screen space.
            /// </summary>
            Point m_lastMousePosition;

            /// <summary>
            /// Thumbs used for resizing.  This may not be initialized depending on what the content template looks like.
            /// See OnApplyTemplate for details.
            /// </summary>
            Thumb[] m_thumbs;

            /// <summary>
            /// DOC
            /// </summary>
            bool m_isResizing = false;

            #endregion
        }
    }

    xaml:

    <Style TargetType="{x:Type local:Node}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:Node}">
                        <Border BorderThickness="1" BorderBrush="Black" CornerRadius="5">

                            <Border.Background>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                    <GradientStop Color="#FFB1B1B1" Offset="1"/>
                                </LinearGradientBrush>
                            </Border.Background>
                           
                            <DockPanel LastChildFill="True">
                               
                                <DockPanel LastChildFill="True" DockPanel.Dock="Left">
                                    <Thumb x:Name="TopLeftThumb" DockPanel.Dock="Top" Width="5" Opacity="0" ></Thumb>
                                    <Thumb x:Name="BottomLeftThumb" DockPanel.Dock="Bottom" Width="5" Opacity="0"></Thumb>
                                    <Thumb x:Name="LeftThumb" Width="5" Opacity="0"></Thumb>
                                </DockPanel>
                                <DockPanel LastChildFill="True" DockPanel.Dock="Top">
                                    <Thumb x:Name="TopRightThumb" DockPanel.Dock="Right" Width="5" Opacity="0"></Thumb>
                                    <Thumb x:Name="TopThumb" Height="5" Opacity="0"></Thumb>
                                </DockPanel> 
                                <DockPanel LastChildFill="True" DockPanel.Dock="Right"> 
                                    <Thumb x:Name="BottomRightThumb" DockPanel.Dock="Bottom" Width="5" Opacity="0"></Thumb>
                                    <Thumb x:Name="RightThumb" Width="5" Opacity="0"></Thumb>
                                </DockPanel>
                                <Thumb x:Name="BottomThumb" DockPanel.Dock="Bottom" Height="5" Opacity="0"></Thumb>
                               
                                <StackPanel>

                                    <StackPanel FlowDirection="RightToLeft" Orientation="Horizontal">
                                        <Button Width="10" Height="10" Padding="5" Margin="2.5"></Button>
                                        <Button Width="10" Height="10" Padding="5" Margin="2.5" ></Button>
                                        <Button Width="10" Height="10" Padding="5" Margin="2.5"></Button>
                                    </StackPanel>
                                       
                                    <StackPanel>
                                        <TextBox TextAlignment="Center" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Node}}, Path=NodeName}" />
                                        <Canvas x:Name="ChildNodeContainer">
                                            <Canvas.Background>
                                                <SolidColorBrush Color="Red">
                                                   
                                                </SolidColorBrush>
                                            </Canvas.Background>
                                        </Canvas>
                                    </StackPanel>
                                   
                                </StackPanel>
                               
                            </DockPanel>
                       
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="MinWidth">
                <Setter.Value>
                    200
                </Setter.Value>
            </Setter>
            <Setter Property="MinHeight">
                <Setter.Value>
                    50
                </Setter.Value>
            </Setter>
        </Style>


    Thursday, November 20, 2008 5:27 AM