I can change my markers position with
markers.Markers[2].Position = new PointLatLng(30.0000, 30.00000);
but how can i change marker icon with setting a varible like above?
I am declaring points as
GMap.NET.WindowsForms.GMapMarker marker3 =
new GMap.NET.WindowsForms.Markers.GMarkerGoogle(
new GMap.NET.PointLatLng(30.0000, 30.00000),
new Bitmap("images/2.png"));
thanks...
To solve this problem, i contacted the creator of the library: radioman.
He referred some code called 'GMarkerArrow.' Here is the code:
namespace Demo.WindowsForms.CustomMarkers
{
using System;
using System.Drawing;
using System.Runtime.Serialization;
using GMap.NET;
using GMap.NET.WindowsForms;
[Serializable]
public class GMarkerArrow : GMapMarker, ISerializable
{
[NonSerialized]
public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.Blue));
//[NonSerialized]
//public Pen Pen = new Pen(Brushes.Blue, 5);
static readonly Point[] Arrow = new Point[] { new Point(-7, 7), new Point(0, -22), new Point(7, 7), new Point(0, 2) };
public float Bearing = 0;
private float scale = 1;
public float Scale
{
get
{
return scale;
}
set
{
scale = value;
Size = new System.Drawing.Size((int)(14 * scale), (int)(14 * scale));
Offset = new System.Drawing.Point(-Size.Width / 2, (int)(-Size.Height / 1.4));
}
}
public GMarkerArrow(PointLatLng p)
: base(p)
{
Scale = (float)1.4;
}
public override void OnRender(Graphics g)
{
//g.DrawRectangle(Pen, new System.Drawing.Rectangle(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height));
{
g.TranslateTransform(ToolTipPosition.X, ToolTipPosition.Y);
var c = g.BeginContainer();
{
g.RotateTransform(Bearing - Overlay.Control.Bearing);
g.ScaleTransform(Scale, Scale);
g.FillPolygon(Fill, Arrow);
}
g.EndContainer(c);
g.TranslateTransform(-ToolTipPosition.X, -ToolTipPosition.Y);
}
}
public override void Dispose()
{
//if(Pen != null)
//{
// Pen.Dispose();
// Pen = null;
//}
if (Fill != null)
{
Fill.Dispose();
Fill = null;
}
base.Dispose();
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
protected GMarkerArrow(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endregion
}
}
Save this as GMarkerArrow.cs and add it to your project.
Add this to your Form1 code:
using Demo.WindowsForms.CustomMarkers;
Now you can use the new marker via following:
GMarkerArrow marker1 = new GMarkerArrow(new PointLatLng(-30, -40));
marker1.ToolTipText = "blablabla";
marker1.ToolTip.Fill = Brushes.Black;
marker1.ToolTip.Foreground = Brushes.White;
marker1.ToolTip.Stroke = Pens.Black;
marker1.Bearing = 30; // Rotation angle
marker1.Fill = new SolidBrush(Color.FromArgb(155, Color.Blue)); // Arrow color
markers.Markers.Add(marker1);
gMapControl1.Overlays.Add(markers);
Also i want to thanks #rdoubleui , thanks you sir.
I hope this helps to everyone.
GMapOverlay markersOverlay;
private void AddOrUpdateMarker(string tag, double lat, double lng, Image NewImage)
{
Bitmap bmpmarker = (Bitmap)NewImage;
var marker = markersOverlay.Markers.FirstOrDefault(m => m.Tag == tag);
if(marker!=null)
{
markersOverlay.Markers.Remove(marker);
gMapControl1.Refresh();
marker = new GMarkerGoogle(new PointLatLng(lat, lng), bmpmarker);
marker.Tag = tag;
markersOverlay.Markers.Add(marker);
gMapControl1.Refresh();
}
else
{
marker = new GMarkerGoogle(new PointLatLng(lat, lng), bmpmarker);
marker.Tag = tag;
markersOverlay.Markers.Add(marker);
gMapControl1.Refresh();
}
}
The problem updating the image of that marker is that the Image property is not publicly accessible, therefore you can not update the image that way.
There are two possibilities: first one is to replace the marker reference with a new one giving you the opportunity to set a new image and copying the position of the current marker. However that is not the clean way as you unnecessarily create a whole bunch of references only to dispose them right away depending on the use case. If it's a one-time update, then this approach is fine.
The preferable way is to derive from GMapMarker as the Google marker does (you can use that as a template, leaving out the whole google specific icon logic). Are you familiar with the concept of deriving? It will require some more effort but will be worth it, could help with that.
Also your main reference is probably the project's github page.
EDIT
using System.Drawing;
public class GImageMarker : GMapMarker
{
public Bitmap Bitmap { get; set; }
public GImageMarker(PointLatLng p, Bitmap Bitmap)
: base(p)
{
this.Bitmap = Bitmap;
Size = new System.Drawing.Size(Bitmap.Width, Bitmap.Height);
Offset = new Point(-Size.Width / 2, -Size.Height);
}
public override void OnRender(Graphics g)
{
g.DrawImage(Bitmap, LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height);
}
}
Related
I'm trying to make a program that is able to read a dxf file and plot it's contents but when I try to draw the figures in the window's paint event, nothing is drawn unless I use this.Invalidate(); and this doesn't completely work because the objects appear to blink on the screen. The coordinates to draw the lines are stored in a list declared in the window class.
private void InitialWindow_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen blackPen = new Pen(Color.Black, 1);
if (entities.Count != 0)
{
for (int i = 0; i < entities.Count; i++)
{
for (int k = 0; k < entities[i].path.Count - 1; k++)
{
g.DrawLine(blackPen, D2F(entities[i].path[k]), D2F(entities[i].path[k + 1]));
}
g.DrawLine(blackPen, D2F(entities[i].path[0]), D2F(entities[i].path.Last()));
}
}
}
2DF is a function that converts a point-like type of data to PointF so it can be used in DrawLine. If I try to draw outside the for loops the lines are displayed correctly on the screen. Thanks in advance for any help with it.
I suggest you create a class to handle the model-to-pixel conversions (and in reverse) instead of a function D2F(). A class it going to give you a lot more flexibility and it will retain a state of the current scaling values
public class Canvas
{
public Canvas()
{
Target = new Rectangle(0, 0, 1, 1);
Scale = 1;
}
public Rectangle Target { get; set; }
public float Scale { get; set; }
public void SetModelBounds(float width, float height)
{
Scale = Math.Min(Target.Width / width, Target.Height / height);
}
public PointF ToPixel(Vector2 point)
{
var center = new PointF(Target.Left + Target.Width / 2, Target.Top + Target.Height / 2);
return new PointF(center.X + Scale * point.X, center.Y - Scale * point.Y);
}
public Vector2 FromPixel(Point pixel)
{
var center = new PointF(Target.Left + Target.Width / 2, Target.Top + Target.Height / 2);
return new Vector2((pixel.X - center.X) / Scale, -(pixel.Y - center.Y) / Scale);
}
}
This is setup for each paint event by calling for example
Canvas.Target = this.ClientRectangle;
Canvas.SetModelBounds(2f, 2f);
The above code is going to place coordinates (-1,-1) on the bottom left of the form surface, and (1,1) on the top right. The pixels per model unit are kept the same for x-axis and y-axis (see Canvas.Scale).
Now for the drawing, also use am Entity class to hold the drawing geometry, and various other properties such as color, and if the shape is closed or not. Note that if it defines a Render(Graphics g, Canvas canvas) method, then it can be called by the form and each entity can handle its own drawing (for the most modular design)
Here is an example:
public class Entity
{
Entity(bool closed, Color color, params Vector2[] path)
{
Color = color;
Path = new List<Vector2>(path);
Closed = closed;
}
public Color Color { get; set; }
public List<Vector2> Path { get; }
public bool Closed { get; set; }
public void Render(Graphics g, Canvas canvas)
{
using (Pen pen = new Pen(Color, 1))
{
var points = Path.Select(pt => canvas.ToPixel(pt)).ToArray();
if (Closed)
{
g.DrawPolygon(pen, points);
}
else
{
g.DrawLines(pen, points);
}
}
}
public static Entity Triangle(Color color, Vector2 center, float width, float height)
{
return new Entity(true, color, new Vector2[] {
new Vector2(center.X-width/2, center.Y - height/3),
new Vector2(center.X+width/2, center.Y - height/3),
new Vector2(center.X, center.Y+2*height/3) });
}
public static Entity Rectange(Color color, Vector2 center, float width, float height)
{
return new Entity(true, color, new Vector2[] {
new Vector2(center.X-width/2, center.Y-height/2),
new Vector2(center.X+width/2, center.Y-height/2),
new Vector2(center.X+width/2, center.Y+height/2),
new Vector2(center.X-width/2, center.Y+height/2) });
}
public static Entity Polygon(Color color, params Vector2[] path)
=> new Entity(true, color, path);
public static Entity Polyline(Color color, params Vector2[] path)
=> new Entity(false, color, path);
}
The above is used in the form as follows
public partial class Form1 : Form
{
public Canvas Canvas { get; }
public List<Entity> Entities { get; }
public Form1()
{
InitializeComponent();
Entities = new List<Entity>();
Canvas = new Canvas();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Resize += (s, ev) => Invalidate();
this.Paint += (s, ev) =>
{
ev.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Canvas.Target = ClientRectangle;
Canvas.SetModelBounds(2f, 2f);
foreach (var item in Entities)
{
item.Render(ev.Graphics, Canvas);
}
};
Entities.Add( .. )
}
}
As you can see the Paint event calls the Render() method for each entity.
In fact, you can generalize this model using an interface IRender. In the example below besides Entity that implements the interface, I define a class to draw the coordinate axis called Axes.
public interface IRender
{
void Render(Graphics g, Canvas canvas);
}
public class Axes : IRender
{
public void Render(Graphics g, Canvas canvas)
{
PointF origin = canvas.ToPixel(Vector2.Zero);
PointF xpoint = canvas.ToPixel(Vector2.UnitX);
PointF ypoint = canvas.ToPixel(Vector2.UnitY);
using (Pen pen = new Pen(Color.Black, 0))
{
pen.CustomEndCap = new AdjustableArrowCap(2f, 5f);
g.DrawLine(pen, origin, xpoint);
g.DrawLine(pen, origin, ypoint);
}
}
}
public class Entity : IRender
{
...
}
and now you can draw with quite a bit of varying things on the screen with the above framework. Here is an example that draws the axis (of unit size) and a few sample entities.
Entities.Add(Entity.Polygon(Color.Orange, Vector2.Zero, -Vector2.UnitX, -Vector2.One));
Entities.Add(Entity.Rectange(Color.Blue, new Vector2(0.5f, 0.5f), 0.25f, 0.25f));
Entities.Add(Entity.Triangle(Color.Red, new Vector2(0.5f, 0.75f), 0.25f, 0.25f));
Entities.Add(Entity.Polyline(Color.Magenta,
new Vector2(0f, -0.2f), new Vector2(0f, -0.4f), new Vector2(0.2f, -0.4f),
new Vector2(0.2f, -0.2f), new Vector2(0.4f, -0.2f), new Vector2(0.4f, -0.4f)));
I would like to have a Panel with a Background that shows a repeated pattern (e.g. dots, evenly separated of 30 pixels).
So far, I managed to create a subclass of XamlCompositionBrushBase that allows we to create my own shape (e.g. a single dot). but I am failing to understand how to repeat this pattern.
This is my custom Brush:
public sealed class DottedBackgroundBrush : XamlCompositionBrushBase
{
public DottedBackgroundBrush()
{
}
protected override void OnConnected()
{
// Delay creating composition resources until they're required.
if (CompositionBrush == null)
{
var compositor = Window.Current.Compositor;
// Actual Width/Height are going to be returned in effective pixels which
// is going to differ from the size of the bitmap that we'll render from the XAML.
var width = 400;
var height = 400;
// Make our visual:
var spriteVisual = compositor.CreateSpriteVisual();
spriteVisual.Size = new Vector2(width, height);
CanvasDevice device = CanvasDevice.GetSharedDevice();
var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, device);
CompositionSurfaceBrush drawingBrush = compositor.CreateSurfaceBrush();
var drawingSurface = graphicsDevice.CreateDrawingSurface(
new Size(width, height),
DirectXPixelFormat.B8G8R8A8UIntNormalized,
DirectXAlphaMode.Premultiplied);
using (var ds = CanvasComposition.CreateDrawingSession(drawingSurface))
{
ds.Clear(Colors.Transparent);
ds.DrawCircle(new Vector2(10, 10), 5, Colors.Black, 3);
}
drawingBrush.Surface = drawingSurface;
CompositionBrush = drawingBrush;
}
}
protected override void OnDisconnected()
{
// Dispose of composition resources when no longer in use.
if (CompositionBrush != null)
{
CompositionBrush.Dispose();
CompositionBrush = null;
}
}
}
How can I enable the circle to be replicated indefinitely, instead of having as single instance?
For this, you want to create a CompositionEffectBrush as your main brush, using a Win2D BorderEffect - which does the actual tiling - and set it's source to be your SurfaceBrush.
Example (adapted from a repo of mine so it might be a bit roundabouts)
public class TilingBrush : XamlCompositionBrushBase
{
protected Compositor _compositor => Window.Current.Compositor;
protected CompositionBrush _imageBrush = null;
protected IDisposable _surfaceSource = null;
protected override void OnConnected()
{
base.OnConnected();
if (CompositionBrush == null)
{
CreateEffectBrush();
Render();
}
}
protected override void OnDisconnected()
{
base.OnDisconnected();
this.CompositionBrush?.Dispose();
this.CompositionBrush = null;
ClearResources();
}
private void ClearResources()
{
_imageBrush?.Dispose();
_imageBrush = null;
_surfaceSource?.Dispose();
_surfaceSource = null;
}
private void UpdateBrush()
{
if (CompositionBrush != null && _imageBrush != null)
{
((CompositionEffectBrush)CompositionBrush).SetSourceParameter(nameof(BorderEffect.Source), _imageBrush);
}
}
protected ICompositionSurface CreateSurface()
{
double width = 20;
double height = 20;
CanvasDevice device = CanvasDevice.GetSharedDevice();
var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(_compositor, device);
var drawingSurface = graphicsDevice.CreateDrawingSurface(
new Size(width, height),
DirectXPixelFormat.B8G8R8A8UIntNormalized,
DirectXAlphaMode.Premultiplied);
/* Create Drawing Session is not thread safe - only one can ever be active at a time per app */
using (var ds = CanvasComposition.CreateDrawingSession(drawingSurface))
{
ds.Clear(Colors.Transparent);
ds.DrawCircle(new Vector2(10, 10), 5, Colors.Black, 3);
}
return drawingSurface;
}
private void Render()
{
ClearResources();
try
{
var src = CreateSurface();
_surfaceSource = src as IDisposable;
var surfaceBrush = _compositor.CreateSurfaceBrush(src);
surfaceBrush.VerticalAlignmentRatio = 0.0f;
surfaceBrush.HorizontalAlignmentRatio = 0.0f;
surfaceBrush.Stretch = CompositionStretch.None;
_imageBrush = surfaceBrush;
UpdateBrush();
}
catch
{
// no image for you, soz.
}
}
private void CreateEffectBrush()
{
using (var effect = new BorderEffect
{
Name = nameof(BorderEffect),
ExtendY = CanvasEdgeBehavior.Wrap,
ExtendX = CanvasEdgeBehavior.Wrap,
Source = new CompositionEffectSourceParameter(nameof(BorderEffect.Source))
})
using (var _effectFactory = _compositor.CreateEffectFactory(effect))
{
this.CompositionBrush = _effectFactory.CreateBrush();
}
}
}
For the longest time I've meant to add it to the WindowsCommunityToolkit, but for the longest time I've been slamming into bugs with the Visual Layer that stop me. This particular case should work fine however.
Here's what I used to solve this problem:
public class Snake {
List<Rectangle> bodyParts = new List<Rectangle>();
}
Snake snk = new Snake();
snk.bodyParts.Add(new Rectangle(760,25,8,8))
//the Exception Occurs here
//snk.bodyParts[0].Y = snk.bodyPArts[0].Y-10;
//Here's my approach
snk.bodyParts[0] = new Rectangle(bodyParts[0].X,bodyParts[0].Y-10,8,8);
Exception: Cannot modify the return value of 'System.Collections.Generic.List.this[int]'
My question is: Are they any alternative/better ways to manage this exception ?
Thanks.
EDIT: I got my answer, can you please close this question.
Rather than working directly with Rectangles, how about adding a BodyPart class, with some manipulation methods:
public class BodyPart
{
private Rectangle rectangle;
public BodyPart(Rectangle rectangle)
{
this.rectangle = rectangle;
}
public void MoveY(int value)
{
rectangle.Y += value;
}
public void MoveX(int value)
{
rectangle.X += value;
}
}
public class Snake
{
public List<BodyPart> Parts = new List<BodyPart>();
}
public class AppSnake
{
public void Run()
{
var snake = new Snake();
snake.Parts.Add(new BodyPart(new Rectangle(760, 25, 8, 8)));
snake.Parts[0].MoveY(-10);
}
}
Rectangle, RectangleF, Point, PointF, Size and SizeF are value types (struct type). This means that you can not/should not change an individual part of the structure.
The reason is that unlike classes each variable keeps its own copy of the structure. When you type list[0].X = 10, the indexer list[0] returns a copy of the rectangle and not the original value. The correct way is to assign a new rectangle list[0] = A which copies all the values from A into the array.
Please read more on value types and structs before attempting to write code that uses them.
The quickest way to fix your code without completely changing it around is by adding methods that manipulate all the body parts in predefined ways:
public class Snake
{
public List<Rectangle> Parts { get; } = new List<Rectangle>();
public void MoveX(int delta)
{
for(int i = 0; i < Parts.Count; i++)
{
// Read the current rectangle from the list
var rect = Parts[i];
// Change the coordinate.
rect.X += delta;
// Write the modified rectangle back into the list
Parts[i] = rect;
}
}
public void MoveY(int delta)
{
for(int i = 0; i < Parts.Count; i++)
{
// Read the current rectangle from the list
var rect = Parts[i];
// Change the coordinate.
rect.Y += delta;
// Write the modified rectangle back into the list
Parts[i] = rect;
}
}
}
Use Rectangle.Location and Rectanagle.Size properties e.g.:
snk.bodyParts[0].Location = new Point(newX, newY);
snk.bodyParts[0].Size = new Size(newWidth, newHeight);
Hope you can help. Stuck on a simple problem for some, I'm a noob. What in trying to do is get an image objects in Silverlight/C# to drop randomly from the top of the canvas, at the moment its going right to left.
This is the from the object class.
namespace LOLWordGame
{
public class LetterA : ContentControl, IGameEntity
{
private int speed = 0;
public LetterA()
{
Image LetterImage = new Image();
LetterImage.Height = 45;
LetterImage.Width = 45;
LetterImage.Source = new BitmapImage(new Uri("images/a.png", UriKind.RelativeOrAbsolute));
this.Content = LetterImage;
Random random = new Random();
Canvas.SetLeft(this, -20);
Canvas.SetTop(this, random.Next(250, 850)); //randomly
speed = random.Next(1, 5);
}
public void Update(Canvas c)
{
Move(Direction.Down);
if (Canvas.GetLeft(this) < 100)
{
c.Children.Remove(this);
}
}
public void Move(Direction direction)
{
Canvas.SetLeft(this, Canvas.GetLeft(this) - speed);
}
}
}
Thanks in advance.
For a solution: maybe you should use the Canvase.SetTop Method instead of the SetLeft method? Hope this helps.
Secondary.. I'm sure following code is not the solution to your problem but I refactored it a bit. Try using collection initializers. You have a method Move that you only call once and the method has only one line of code: no reason to make that a method in my opinion. Also the method takes in a parameter but you do not use it inside the method.
public class LetterA : ContentControl, IGameEntity
{
private int speed = 0;
public LetterA()
{
var letterImage = new Image()
{
Height = 45,
Width = 45,
Source = new BitmapImage(new Uri("images/a.png", UriKind.RelativeOrAbsolute))
};
Content = letterImage;
var random = new Random();
Canvas.SetLeft(this, -20);
Canvas.SetTop(this, random.Next(250, 850));
speed = random.Next(1, 5);
}
public void Update(Canvas c)
{
Canvas.SetLeft(this, Canvas.GetLeft(this) - speed);
if (Canvas.GetLeft(this) < 100)
c.Children.Remove(this);
}
}
I'm trying to make a custom ContentControl that takes on the shape of a Polyogon with rounded corners. For some reason when I set the Clip property on the Control, nothing shows up. Any help is appreciated...
PageHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Controls;
namespace DtcInvoicer.Controls
{
public class PageHost:UserControl
{
#region public ImageSource Icon;
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ImageSource), typeof(PageHost));
public ImageSource Icon
{
get { return GetValue(IconProperty) as ImageSource; }
set { SetValue(IconProperty, value); }
}
#endregion
#region public string Title;
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(PageHost));
public string Title
{
get { return GetValue(TitleProperty).ToString(); }
set { SetValue(TitleProperty, value); }
}
#endregion
#region public double Radius;
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(PageHost));
public double Radius
{
get { return (double)GetValue(RadiusProperty); }
set
{
SetValue(RadiusProperty, value);
DoClip();
}
}
#endregion
public PageHost()
{
Loaded += new RoutedEventHandler(PageHost_Loaded);
SizeChanged += new SizeChangedEventHandler(PageHost_SizeChanged);
}
#region Event Handlers
private void PageHost_Loaded(object sender, RoutedEventArgs e)
{
DoClip();
}
private void PageHost_SizeChanged(object sender, SizeChangedEventArgs e)
{
DoClip();
}
#endregion
private void DoClip()
{
Polygon p = new Polygon()
{
Points = new PointCollection()
{
new Point(0, 0),
new Point(ActualWidth - 30, 0),
new Point(ActualWidth, 30),
new Point(ActualWidth, ActualHeight),
new Point(0, ActualHeight)
}
};
Geometry g1 = new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight), Radius, Radius);
Geometry g2 = p.RenderedGeometry;
// Clip = g1; this works, the control shows up with the rounded corners
// Clip = g2; this does not work, nothing shows up
// this is what I want to do, I want to combine the two geometries
// but this does not work either
Clip = new CombinedGeometry(GeometryCombineMode.Intersect, g1, g2);
}
}
}
HomePage.xaml
<control:PageHost x:Class="DtcInvoicer.Pages.HomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:control="clr-namespace:DtcInvoicer.Controls"
Width="500" Height="250" Radius="20" Background="Aqua">
</control:PageHost>
Setting the clip to a RenderedGeometry fails in this case because the RenderedGeometry has not yet been actually rendered, and is thus not available. For regular geometries, use this in DoClip:
Dispatcher.BeginInvoke(DispatcherPriority.Background, new ThreadStart(delegate
{
Clip = new CombinedGeometry(GeometryCombineMode.Intersect, g1, g2);
}));
With your RenderedGeometry, you would need to add it to the visual tree somewhere and then use its Loaded event before you set the Clip region, which would be hard. Try using a regular Geometry instead of a RenderedGeometry, with the same points, such as A path geometry. Here's an example of where I draw a triangle using a PathGeometry:
double leftPoint = cellRect.Right - 12;
if (leftPoint < cellRect.Left)
leftPoint = cellRect.Left;
double topPoint = cellRect.Top + (cellRect.Height - 4.0) / 2;
if (topPoint < cellRect.Top)
topPoint = cellRect.Top;
double rightPoint = leftPoint + 7;
if (rightPoint > cellRect.Right)
rightPoint = cellRect.Right;
double bottomPoint = topPoint + 4;
if (bottomPoint > cellRect.Bottom)
bottomPoint = cellRect.Bottom;
double middlePoint = leftPoint + 3;
if (middlePoint > cellRect.Right)
middlePoint = cellRect.Right;
PathFigure figure = new PathFigure();
figure.StartPoint = new Point(middlePoint, bottomPoint);
PathFigureCollection figCollection = new PathFigureCollection();
figCollection.Add(figure);
PathSegmentCollection segCollection = new PathSegmentCollection();
LineSegment topSeg = new LineSegment();
topSeg.Point = new Point(rightPoint, topPoint);
segCollection.Add(topSeg);
LineSegment midRightSeg = new LineSegment();
midRightSeg.Point = new Point(leftPoint, topPoint);
segCollection.Add(midRightSeg);
LineSegment midLeftSeg = new LineSegment();
midLeftSeg.Point = new Point(middlePoint+1, bottomPoint);
segCollection.Add(midLeftSeg);
figure.Segments = segCollection;
PathGeometry geo = new PathGeometry();
geo.Figures = figCollection;