Start by creating a new WPF project called DockPanelExample.
Here's the XAML. Note I use a canvas so I can drag the rectangles from the stack panel to the dock panel. I need to access the children of the stack panel and the dock panel. I chose to do this by storing a reference to the panels during their loaded events.
<Window x:Class="DockPanelExample.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:DockPanelExample"
mc:Ignorable="d"
Title="Dock Panel Example"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Canvas Loaded="Canvas_Loaded" MouseMove="Canvas_MouseMove" Width="500" Height="350">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0" Orientation="Vertical" Background="AliceBlue" Loaded="StackPanel_Loaded" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<CheckBox Content="Last
Fill?" IsChecked="{Binding IsLastFill}"/>
</StackPanel>
<Border
Grid.Column="1" BorderBrush="Navy" BorderThickness="1">
<DockPanel Background="BlanchedAlmond" Width="400" Height="350" Loaded="DockPanel_Loaded" LastChildFill="{Binding IsLastFill}"></DockPanel>
</Border>
</Grid>
</Canvas>
</Window>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
public partial class MainWindow : Window, INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if
(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
Let's modify StackPanel_Loaded to capture the reference to the stack panel and initialize it. Add a place to store the reference.
public partial class MainWindow : Window, INotifyPropertyChanged
{
private StackPanel Stack;
And then the StackPanel_Loaded event handler.
private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
Stack = (StackPanel)sender;
InitializeRectangles();
InitializeStack();
}
The routines InitializeRectangles and InitializeStack need a class and a list of that class.
private class cRectangle
{
public String Name;
public double Width;
public double Height;
public Brush Color;
}
private List<cRectangle> Rectangles = new List<cRectangle>();
public void InitializeRectangles()
{
Rectangles.Add(new cRectangle { Name = "SmallSquare", Width = 50, Height = 50,
Color = new SolidColorBrush(Colors.Red) });
Rectangles.Add(new cRectangle { Name = "TallRectangle", Width = 50, Height = 100,
Color = new SolidColorBrush(Colors.Orange) });
Rectangles.Add(new cRectangle { Name = "WideRectangle", Width = 100, Height = 50,
Color = new SolidColorBrush(Colors.Yellow) });
Rectangles.Add(new cRectangle { Name = "LargeSquare", Width = 100, Height = 100,
Color = new SolidColorBrush(Colors.Green) });
}
public void InitializeStack()
{
foreach (cRectangle r in Rectangles)
{
Stack.Children.Add(InitializeRect(r));
}
}
{
Rectangle re = new Rectangle { Tag = r.Name, Width =
r.Width, Height = r.Height, Fill = r.Color, Margin = new Thickness(2) };
return re;
}
private Rectangle TempRect = new Rectangle { Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 1,
Visibility = Visibility.Hidden
};
private Canvas Canvas;
private void Canvas_Loaded(object sender, RoutedEventArgs e)
{
Canvas = (Canvas)sender;
Canvas.Children.Add(TempRect);
}
private Rectangle InitializeRect(cRectangle r)
{
Rectangle re = new Rectangle { Tag = r.Name, Width =
r.Width, Height = r.Height, Fill = r.Color, Margin = new Thickness(2) };
re.MouseLeftButtonDown +=
DoMouseDown;
return re;
}
private void DoMouseDown(object sender, MouseEventArgs e)
{
Rectangle SourceRect = (Rectangle)sender;
Point SourceLocation;
CloneRect(SourceRect);
TempRect.Visibility = Visibility.Visible;
SourceLocation = SourceRect.TranslatePoint(new Point(0, 0), Canvas);
TempRect.SetValue(Canvas.TopProperty,
SourceLocation.Y);
TempRect.SetValue(Canvas.LeftProperty,
SourceLocation.X);
}
private void CloneRect(Rectangle r)
{
TempRect.Tag = r.Tag;
TempRect.Width = r.Width;
TempRect.Height = r.Height;
TempRect.Fill = r.Fill;
}
At this point clicking on one of the rectangles will clone the rectangle with a border. Now we need to figure out how to drag it. We start by determining how far the mouse cursor is from the top/left corner of the rectangle, then every time the mouse moves, the rectangle follows it.
Add two new doubles to the class.
private Double MouseOffsetX;
private Double MouseOffsetY;
MouseOffsetX = SourceLocation.X -
e.GetPosition(Canvas).X;
MouseOffsetY = SourceLocation.Y -
e.GetPosition(Canvas).Y;
Mouse.Capture(SourceRect);
Now we complete our Canvas_MouseMove event handler. If the temporary rectangle is visible we make it track mouse movements.
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if
(TempRect.Visibility == Visibility.Visible)
{
Point NewPosition = e.GetPosition(Canvas);
TempRect.SetValue(Canvas.TopProperty, NewPosition.Y +
MouseOffsetY);
TempRect.SetValue(Canvas.LeftProperty, NewPosition.X
+ MouseOffsetX);
}
}
Now you can drag the selected rectangle.
Our next task is to handle the drop. This is done via the MouseUp event. We determine which side of the dock panel the mouse is nearest, create a new rectangle, dock it, and hide the temporary rectangle.
To handle the drop, we need to capture a reference to the DockPanel in it's loaded event. Create a class reference to the DockPanel.
private DockPanel Dock;
and flesh out the DockPanel_Loaded event handler.
private void DockPanel_Loaded(object sender, RoutedEventArgs e)
{
Dock = (DockPanel)sender;
}
Let's start by defining and registering the Rectangle's MouseUp event handler. In InitializeRect, register a DoMouseUp event handler.
re.MouseLeftButtonUp +=
DoMouseUp;
Most of the event handler is involved with determining how to dock the rectangle. Once we have done that we dock the rectangle, add it to the dock panel, hide the temporary rectange, and turn off mouse capture. It looks like this.
private void DoMouseUp(object sender, MouseEventArgs e)
{
Rectangle NewRect = new Rectangle { Tag = TempRect.Tag, Width
= TempRect.Width, Height = TempRect.Height, Fill = TempRect.Fill };
Point MousePosition = e.GetPosition(Dock);
if
(MousePosition.X > 0 && MousePosition.X < Dock.Width &&
MousePosition.Y > 0 && MousePosition.Y < Dock.Height)
{
Double DistFromTop = e.GetPosition(Dock).Y;
Double DistFromBottom = Dock.Height - e.GetPosition(Dock).Y;
Double DistFromLeft = e.GetPosition(Dock).X;
Double DistFromRight = Dock.Width - e.GetPosition(Dock).X;
Double MinDistX = Math.Min(DistFromLeft, DistFromRight);
Double MinDistY = Math.Min(DistFromTop, DistFromBottom);
Double MinDist = Math.Min(MinDistX,
MinDistY);
Dock DockLocation = System.Windows.Controls.Dock.Top;
if (MinDist == DistFromBottom) DockLocation = System.Windows.Controls.Dock.Bottom;
if (MinDist == DistFromLeft) DockLocation = System.Windows.Controls.Dock.Left;
if (MinDist == DistFromRight) DockLocation = System.Windows.Controls.Dock.Right;
NewRect.SetValue(DockPanel.DockProperty, DockLocation);
Dock.Children.Add(NewRect);
}
TempRect.Visibility = Visibility.Hidden;
Mouse.Capture(null);
}
Now we can click, drag, and drop a rectangle into the DockPanel.
Our next task is to wire up the Last Fill checkbox by binding it to a property and binding the DockPanel's LastChildFill property to the same property. Our XAML already references this property so let's create it.
private bool _IsLastFill;
public bool IsLastFill
{
get
{ return _IsLastFill; }
set
{ _IsLastFill = value;
NotifyPropertyChanged("IsLastFill"); }
}
NewRect.MouseLeftButtonDown +=
DeleteRect;
The event handler looks like this.
private void DeleteRect(object sender, MouseEventArgs e)
{
Rectangle DeletedRect = (Rectangle)sender;
Dock.Children.Remove(DeletedRect);
DeletedRect.MouseLeftButtonDown -=
DeleteRect;
DeletedRect = null;
}
The full code behind looks like this...
-----------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace DockPanelExample
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private StackPanel Stack;
private DockPanel Dock;
private Canvas
Canvas;
private Double MouseOffsetX;
private Double MouseOffsetY;
private Rectangle TempRect = new Rectangle { Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 1,
Visibility = Visibility.Hidden
};
private class cRectangle
{
public String Name;
public double Width;
public double Height;
public Brush Color;
}
private List<cRectangle> Rectangles = new List<cRectangle>();
private bool _IsLastFill;
public bool IsLastFill
{
get
{ return _IsLastFill; }
set
{ _IsLastFill = value;
NotifyPropertyChanged("IsLastFill"); }
}
public MainWindow()
{
InitializeComponent();
}
public void InitializeRectangles()
{
Rectangles.Add(new cRectangle { Name = "SmallSquare", Width = 50, Height = 50,
Color = new SolidColorBrush(Colors.Red) });
Rectangles.Add(new cRectangle { Name = "TallRectangle", Width = 50, Height = 100,
Color = new SolidColorBrush(Colors.Orange) });
Rectangles.Add(new cRectangle { Name = "WideRectangle", Width = 100, Height = 50,
Color = new SolidColorBrush(Colors.Yellow) });
Rectangles.Add(new cRectangle { Name = "LargeSquare", Width = 100, Height = 100,
Color = new SolidColorBrush(Colors.Green) });
}
public void InitializeStack()
{
foreach (cRectangle r in Rectangles)
{
Stack.Children.Add(InitializeRect(r));
}
}
private Rectangle InitializeRect(cRectangle r)
{
Rectangle re = new Rectangle { Tag = r.Name, Width =
r.Width, Height = r.Height, Fill = r.Color, Margin = new Thickness(2) };
re.MouseLeftButtonDown +=
DoMouseDown;
re.MouseLeftButtonUp += DoMouseUp;
return re;
}
private void CloneRect(Rectangle r)
{
TempRect.Tag = r.Tag;
TempRect.Width = r.Width;
TempRect.Height = r.Height;
TempRect.Fill = r.Fill;
}
private void DoMouseDown(object sender, MouseEventArgs e)
{
Rectangle SourceRect = (Rectangle)sender;
Point SourceLocation;
CloneRect(SourceRect);
TempRect.Visibility = Visibility.Visible;
SourceLocation = SourceRect.TranslatePoint(new Point(0, 0), Canvas);
TempRect.SetValue(Canvas.TopProperty,
SourceLocation.Y);
TempRect.SetValue(Canvas.LeftProperty,
SourceLocation.X);
MouseOffsetX = SourceLocation.X -
e.GetPosition(Canvas).X;
MouseOffsetY = SourceLocation.Y -
e.GetPosition(Canvas).Y;
Mouse.Capture(SourceRect);
}
private void DoMouseUp(object sender, MouseEventArgs e)
{
Rectangle NewRect = new Rectangle { Tag = TempRect.Tag, Width
= TempRect.Width, Height = TempRect.Height, Fill = TempRect.Fill };
Point MousePosition = e.GetPosition(Dock);
if
(MousePosition.X > 0 && MousePosition.X < Dock.Width &&
MousePosition.Y > 0 && MousePosition.Y < Dock.Height)
{
Double DistFromTop = e.GetPosition(Dock).Y;
Double DistFromBottom = Dock.Height - e.GetPosition(Dock).Y;
Double DistFromLeft = e.GetPosition(Dock).X;
Double DistFromRight = Dock.Width - e.GetPosition(Dock).X;
Double MinDistX = Math.Min(DistFromLeft, DistFromRight);
Double MinDistY = Math.Min(DistFromTop, DistFromBottom);
Double MinDist = Math.Min(MinDistX,
MinDistY);
Dock DockLocation = System.Windows.Controls.Dock.Top;
if (MinDist == DistFromBottom) DockLocation = System.Windows.Controls.Dock.Bottom;
if (MinDist == DistFromLeft) DockLocation = System.Windows.Controls.Dock.Left;
if (MinDist == DistFromRight) DockLocation = System.Windows.Controls.Dock.Right;
NewRect.SetValue(DockPanel.DockProperty, DockLocation);
NewRect.MouseLeftButtonDown +=
DeleteRect;
Dock.Children.Add(NewRect);
}
TempRect.Visibility = Visibility.Hidden;
Mouse.Capture(null);
}
private void DeleteRect(object sender, MouseEventArgs e)
{
Rectangle DeletedRect = (Rectangle)sender;
Dock.Children.Remove(DeletedRect);
DeletedRect.MouseLeftButtonDown -=
DeleteRect;
DeletedRect = null;
}
private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
Stack = (StackPanel)sender;
InitializeRectangles();
InitializeStack();
}
private void DockPanel_Loaded(object sender, RoutedEventArgs e)
{
Dock = (DockPanel)sender;
}
private void Canvas_Loaded(object sender, RoutedEventArgs e)
{
Canvas = (Canvas)sender;
Canvas.Children.Add(TempRect);
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if
(TempRect.Visibility == Visibility.Visible)
{
Point NewPosition = e.GetPosition(Canvas);
TempRect.SetValue(Canvas.TopProperty, NewPosition.Y +
MouseOffsetY);
TempRect.SetValue(Canvas.LeftProperty, NewPosition.X
+ MouseOffsetX);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if
(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
No comments:
Post a Comment