Tuesday, June 2, 2009

How to draw the lightweight elements –use OnRender or DrawingVisual(2)

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.

clip_image002

The following is the output of the BetterEllipseClone instance. Which use DrawingVisual to draw the shape not by override the OnRender method.

clip_image004

Run the EllipseWithChild will produce the diagram below.which derives from BetterEllipse class. you can set a UIElement instance to its Child property.

clip_image006

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