Follow the previous post, we continue to explore the custom drawing visuals. In this chapter, I will show you three classes to draw a simple ellipse with some text information.
#1How to draw graphics by overriding the OnRender.
#2 How to draw graphics using DrawingVisual
#3 create a class with Child property.
First of all. Let have a overall expression of outputs that produces by the three approaches.
The following is the output of the BetterEllipse class instance. Which override the OnRender method to draw graphics.
The following is the output of the BetterEllipseClone instance. Which use DrawingVisual to draw the shape not by override the OnRender method.
Run the EllipseWithChild will produce the diagram below.which derives from BetterEllipse class. you can set a UIElement instance to its Child property.
Just make thing simple to understand, in the first class BetterEllipse. We just need to override the OnRender method and using the DrawingContext class to draw a ellipse and a string text:
protected override void OnRender(DrawingContext drawingContext)
{
//base.OnRender(drawingContext);
Size size = RenderSize;
if (Stroke != null)
{
size.Width = Math.Max(0, size.Width - Stroke.Thickness);
size.Height = Math.Max(0, size.Height - Stroke.Thickness);
}
drawingContext.DrawEllipse(Fill, Stroke, new Point(RenderSize.Width / 2, RenderSize.Height / 2),
size.Width / 2, size.Height / 2);
//draw text
FormattedText formatText = new FormattedText("I am a ellipse", CultureInfo.CurrentCulture,
FlowDirection.LeftToRight, new Typeface("Times New Roman Italic"), 24, Brushes.Red);
Point pointText = new Point((RenderSize.Width - formatText.Width) / 2, (RenderSize.Height - formatText.Height) / 2);
drawingContext.DrawText(formatText, pointText);
//you can clip the area which is expose the ellipse
drawingContext.PushClip(new RectangleGeometry(new Rect(new Point(0, 0), RenderSize)));
}
Next, in the BetterEllipseClone. We create a property named visualCollection, which is of type VisualCollection. Similar like the preceding class. we create two DrawignVisual instances and add them to the collection.
public BetterEllipseClone()
{
visualCollection = new VisualCollection(this);
visualCollection.Add(DrawingEllipse());
visualCollection.Add(DrawingText());
}
Finally. we create a class that can support UIElement as its Child property
public UIElement Child
{
set
{
if (child != null)
{
RemoveVisualChild(child);
RemoveLogicalChild(child);
}
if ((child = value) != null)
{
AddVisualChild(child);
AddLogicalChild(child);
}
}
get { return child; }
}
In this topic, one notable thing need to be considered is the priority of the method.
DerivedClass. MeasureOverride--> DerivedClass.ArrangeOverride--> DerivedClass.OnRender-->BaseClass.OnRender
The following is the full code version:
//use the DrawingVisual, not use OnRender
public class BetterEllipseClone : FrameworkElement
{
private VisualCollection visualCollection;
public BetterEllipseClone()
{
visualCollection = new VisualCollection(this);
visualCollection.Add(DrawingEllipse());
visualCollection.Add(DrawingText());
}
public DrawingVisual DrawingEllipse()
{
DrawingVisual visualEllipse = new DrawingVisual();
DrawingContext context = visualEllipse.RenderOpen();
context.DrawEllipse(Brushes.Green, new Pen(Brushes.Yellow, 1), new Point(10, 20), 50, 20);
context.Close();
return visualEllipse;
}
public DrawingVisual DrawingText()
{
DrawingVisual visualText = new DrawingVisual();
DrawingContext context = visualText.RenderOpen();
FormattedText text = new FormattedText("Testing Test", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Times New Roman"), 20, Brushes.LightBlue);
context.DrawText(text, new Point(0, 0));
context.Close();
return visualText;
}
protected override Visual GetVisualChild(int index)
{
//return base.GetVisualChild(index);
if (index < 0 || index >= visualCollection.Count)
{
throw new ArgumentOutOfRangeException("Index");
}
return visualCollection[index];
}
protected override int VisualChildrenCount
{
get
{
return visualCollection.Count;
}
}
}
//use the OnRender
public class BetterEllipse : FrameworkElement
{
public static readonly DependencyProperty FillProperty;
public static readonly DependencyProperty StrokeProperty;
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public Pen Stroke
{
get { return (Pen)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
static BetterEllipse()
{
FillProperty = DependencyProperty.Register("Fill", typeof(Brush), typeof(BetterEllipse),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
StrokeProperty = DependencyProperty.Register("Stroke", typeof(Pen), typeof(BetterEllipse),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure));
}
//MeasureOverride
protected override Size MeasureOverride(Size availableSize) //the availableSize is assigned in the initialization.
{
//return base.MeasureOverride(availableSize);
Size sizeDesired = base.MeasureOverride(availableSize);
if (Stroke != null)
{
sizeDesired = new Size(Stroke.Thickness, Stroke.Thickness);
}
return sizeDesired;
}
protected override Size ArrangeOverride(Size finalSize)
{
return base.ArrangeOverride(finalSize);
}
protected override void OnRender(DrawingContext drawingContext)
{
//base.OnRender(drawingContext);
Size size = RenderSize;
if (Stroke != null)
{
size.Width = Math.Max(0, size.Width - Stroke.Thickness);
size.Height = Math.Max(0, size.Height - Stroke.Thickness);
}
drawingContext.DrawEllipse(Fill, Stroke, new Point(RenderSize.Width / 2, RenderSize.Height / 2),
size.Width / 2, size.Height / 2);
//draw text
FormattedText formatText = new FormattedText("I am a ellipse", CultureInfo.CurrentCulture,
FlowDirection.LeftToRight, new Typeface("Times New Roman Italic"), 24, Brushes.Red);
Point pointText = new Point((RenderSize.Width - formatText.Width) / 2, (RenderSize.Height - formatText.Height) / 2);
drawingContext.DrawText(formatText, pointText);
//you can clip the area which is expose the ellipse
drawingContext.PushClip(new RectangleGeometry(new Rect(new Point(0, 0), RenderSize)));
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Space || e.Key == Key.Enter)
{
e.Handled = true;
}
}
protected virtual void OnKnock()
{
RoutedEventArgs args = new RoutedEventArgs();
//args.RoutedEvent +=//;
args.Source = this;
RaiseEvent(args);
}
}
public class EllipseWithChild : BetterEllipse
{
UIElement child;
public UIElement Child
{
set
{
if (child != null)
{
RemoveVisualChild(child);
RemoveLogicalChild(child);
}
if ((child = value) != null)
{
AddVisualChild(child);
AddLogicalChild(child);
}
}
get { return child; }
}
//override VisualChildrenCount
protected override int VisualChildrenCount
{
get
{
//return base.VisualChildrenCount;
return child != null ? 1 : 0;
}
}
//override GetVisualChildren
protected override Visual GetVisualChild(int index)
{
//return base.GetVisualChild(index);
if (index > 0 || Child == null)
{
throw new ArgumentOutOfRangeException("index");
}
return Child;
}
//override MeasureOverride(invoke its children's Measure method)
protected override Size MeasureOverride(Size availableSize)
{
//return base.MeasureOverride(availableSize);
Size sizeDesired = new Size(0, 0);
if (Stroke != null)
{
sizeDesired.Width += 2 * Stroke.Thickness;
sizeDesired.Height += 2 * Stroke.Thickness;
//
availableSize.Width = Math.Max(0, availableSize.Width - 2 * Stroke.Thickness);
availableSize.Height = Math.Max(0, availableSize.Height - 2 * Stroke.Thickness);
}
//Child
if (Child != null)
{
Child.Measure(availableSize);
sizeDesired.Width += Child.DesiredSize.Width;
sizeDesired.Height += Child.DesiredSize.Height;
}
return sizeDesired;
}
//Override ArrangeOverride (invoke the children's Arrange method)
protected override Size ArrangeOverride(Size finalSize)
{
//return base.ArrangeOverride(finalSize);
if (Child != null)
{
//(finalSize.Width - Child.DesiredSize.Width) / 2,
// (finalSize.Height - Child.DesiredSize.Height) / 2
Rect rect = new Rect(new Point((finalSize.Width - Child.DesiredSize.Width) / 2, (finalSize.Height - Child.DesiredSize.Height) / 2), Child.DesiredSize);
Child.Arrange(rect);
}
return finalSize;
}
protected override void OnRender(DrawingContext drawingContext)
{
//you can determine whether to call the base.OnRender method
//true: will get the base content. otherwise false, will not get the base content.
//by default, will call the base.OnRender method.
base.OnRender(drawingContext);
}
}
Thanks for any comments.
0 comments:
Post a Comment