Introduction
Last year I joined a WPF project. And there I experienced many problems. One problem is that I made a UserControl that can move in a Grid Control, but it couldn't resize himself in the Grid Control. This was because the Resizing Control couldn't go over the boundary of the Grid Cells. After all, I implemented moving functions only. The resize function can't be implemented.
Until now. I have been thinking how to resize the control in a grid. Here is one solution I tried.
This is not Grid control, this is Adorner(Fake Grid). However, you could add some features (resize element etc.) to it, if you wanted to resize elements.
Background
You make the Adorner Element From Canvas. If you started dragging, then the fake grid adorner is shown up. And if you stop dragging, it is shown down.
Grid(Fake Grid) is a large composition of many geometries.
Grid's color is Rainbow Color because I want to show that you can change grid color anyway.
How to make this.
1. This is MainWindow.xaml below
<Canvas Name="_canvas" Background="White" Width="500" Height="300">
<Thumb DragStarted="Thumb_DragStarted"
DragDelta="Thumb_DragDelta"
DragCompleted="Thumb_DragCompleted" Canvas.Left="112" Canvas.Top="50">
<Thumb.Template>
<ControlTemplate>
-->
<Grid Width="150" Height="150">
<Ellipse Fill="Blue" Stroke="LightBlue"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
/>
</Grid>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Canvas>
First, we replace grid to canvas in the Window tag, or place canvas in the grid tag. On Canvas we place one Thumb there. All Thumb Controls have one Template
property. We set this property the ControlTemplate
as shown below. This ControlTemplate
has an Ellipse in a Grid . It decides the look at the Thumb itself. Finally, we add three events to that Thumb Control.
2. We use Adorner to show the grid, then add AdornerLayer
and Adorner fields to the MainWindow.xaml.cs file.
AdornerLayer _layer;
CanvasAdorner _adorner;
3. Next we make three EventHandlers
of Thumb Control and write it.
private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
{
_layer = AdornerLayer.GetAdornerLayer(_canvas);
_adorner = new CanvasAdorner(_canvas);
Path _backGrid = MakeStreamGeometry(_canvas, new Size(100, 100));
Canvas.SetTop(_backGrid, 0);
Canvas.SetLeft(_backGrid, 0);
_adorner.AddAdornment(_backGrid);
_layer.Add(_adorner);
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
if (thumb == null) return;
Canvas.SetLeft(thumb, Canvas.GetLeft(thumb) + e.HorizontalChange);
Canvas.SetTop(thumb, Canvas.GetTop(thumb) + e.VerticalChange);
}
private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
var layer = AdornerLayer.GetAdornerLayer(_canvas);
_layer.Remove(_adorner);
}
The first method starts on Mouse down. This is to get Canvas 's AdornerLayer
. And the next line is to get Canvas's AdornerControl
to add that layer. The third line is a Method, MakeStreamGeometry()
, that gets Fake Grid GeometryObjects
, and set it to the Canvas's AdornerLayer
with AdornerElement
. (Size 100 means Fake Grid's Cell Width and Height)
The second Method means Thumb Moving.
The third Method occurs on Mouse up and gets to AdornerLayer
again and delete it.
4. Next Method is to initialize drawing objects.
private Path MakeStreamGeometry(Canvas _canvas, Size _cellSize)
{
Brush myBrush = MakeRainbowBrush();
Path myPath = new Path()
{
Stroke = myBrush,
StrokeThickness = 3.0,
StrokeDashOffset = 1.5,
Opacity = 0.95,
StrokeDashArray = { 5.0, 2.5 },
};
var geometry = new StreamGeometry();
using (var ctx = geometry.Open())
{
DrawGridLines(_canvas, _cellSize, ctx);
}
myPath.Data = geometry;
return myPath;
}
It sets Brush and Path first. myPath
is Shape instance that means the Shape of Grid. That is initialized and set by geometry object at Data property. DrawGridLines
Method is make geometry drawings.
5. This Method calculates point for drawing grid. and calculates number of Rows, Columns.
private static Point DrawGridLines(Canvas _canvas, Size _size, StreamGeometryContext ctx)
{
Point _pointOffset = new Point(0, 0);
int rowCount = (int)System.Math.Floor(_canvas.ActualHeight / _size.Height);
int colCount = (int)System.Math.Floor(_canvas.ActualWidth / _size.Width);
for (int i = 0; i < colCount + 1; i++)
{
ctx.BeginFigure(_pointOffset, true , false );
_pointOffset.Y += _size.Height * rowCount;
ctx.LineTo(_pointOffset, true , false );
_pointOffset.Y = 0;
_pointOffset.X += _size.Width;
}
for (int i = 0; i < rowCount + 1; i++)
{
ctx.BeginFigure(_pointOffset, true , false);
_pointOffset.X += _size.Width * (colCount);
ctx.LineTo(_pointOffset, true, false);
_pointOffset.X = 0;
_pointOffset.Y += _size.Height;
}
return _pointOffset;
}
using GeometryContext
, it draws "L" like lines many times and two lines finally, because repeating Rectangles caused a Line on Line problem. Therefore, I do it this way.
6. It is the Method to make Brush using by LinearGradientBrush
Class.
private static Brush MakeRainbowBrush()
{
LinearGradientBrush rainbowBrush = new LinearGradientBrush()
{
StartPoint = new Point(0, 0),
EndPoint = new Point(1, 1),
GradientStops = new GradientStopCollection()
{
new GradientStop(Colors.Red, 0.16),
new GradientStop(Colors.Orange, 0.32),
new GradientStop(Colors.Yellow, 0.48),
new GradientStop(Colors.Green, 0.64),
new GradientStop(Colors.Blue, 0.80),
new GradientStop(Colors.Violet, 0.96),
new GradientStop(Colors.Black, 1.0),
},
};
return rainbowBrush;
}
This Method makes Rainbow Color Brush Object and returns to it.
This program is the result.
Points of Interest
You can change the Grid any color. And move and resize elements.
History
7/7/2014 I posted it to CodeProject first time.