#699 – Converting an Image Control to a Bitmap

You may want to work with the image from an Image control as a standard Windows bitmap.  You can convert the System.Windows.Controls.Image to a System.Drawing.Bitmap as follows (imgLife is an Image control):

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RenderTargetBitmap rtBmp = new RenderTargetBitmap((int)imgLife.ActualWidth, (int)imgLife.ActualHeight,
                96.0, 96.0, PixelFormats.Pbgra32);

            imgLife.Measure(new System.Windows.Size((int)imgLife.ActualWidth, (int)imgLife.ActualHeight));
            imgLife.Arrange(new Rect(new System.Windows.Size((int)imgLife.ActualWidth, (int)imgLife.ActualHeight)));

            rtBmp.Render(imgLife);

            PngBitmapEncoder encoder = new PngBitmapEncoder();
            MemoryStream stream = new MemoryStream();
            encoder.Frames.Add(BitmapFrame.Create(rtBmp));

            // Save to memory stream and create Bitamp from stream
            encoder.Save(stream);
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(stream);

            // Demonstrate that we can do something with the Bitmap
            bitmap.Save(@"D:\Temp\Life.png", ImageFormat.Png);

            // Optionally, if we didn't need Bitmap object, but
            // just wanted to render to file, we could:
            //encoder.Save(new FileStream(@"D:\Temp\Life-Other.png", FileMode.Create));
        }

#564 – Other Places to Get Third Party Effects

An effect in WPF changes the appearance of a visual element.  The .NET Framework comes with only a couple of effects, but you can find a few other third party effects on the web.

Here are some of the effects from the Pixel Shader Effects Library:

 

#563 – Additional Effects in Expression SDK

In addition to the two effects that come with the .NET Framework, you can get access to a number of other effects when you download and install the Expression Blend SDK for .NET 4.

Once you install the SDK, you’ll see the new effects show up in the Effects folder of the Assets panel.

The effects include:

  • Bloom – Make bright areas appear brighter
  • Color Tone – Render a visual using shades of two colors
  • Emboss – Make visual appear raised or stamped
  • Magnify – Magnify a circular area
  • Monochrome – Render using shades of a single color
  • Pixelate – Reduce resolution, rendering as large pixels
  • Ripple – Apply visual that looks like rippling liquid
  • Sharpen – Sharpen image edges
  • Swirl – Apply effect that twists entire image

 

#562 – Setting an Effect for an Element in Blend

You can set the value of the Effect property for any control deriving from UIElement.  An effect is a two-dimensional graphical effect applied to the control when it is rendered.

To apply an effect to a control in Blend, start by left-clicking on the Effects folder in the Assets panel.

The .NET Framework comes with two pre-defined effects, located in PresentationCore–BlurEffect and DropShadowEffect.  (The other effects shown above come from the Expression SDK for .NET 4).  You can drag either of these effects onto a control on the artboard, or a control listed in the Objects and Timeline panel.

Two Label controls, before adding effects:

After adding effects:

Corresponding XAML:

		<Label Content="Drop Shadow Effect" FontFamily="Cooper Black" FontSize="48"
			Margin="20,20,20,5" Foreground="#FF047800" HorizontalAlignment="Center">
			<Label.Effect>
				<DropShadowEffect/>
			</Label.Effect>
		</Label>
		<Label Content="Blur Effect" FontFamily="Cooper Black" FontSize="48"
			Margin="20,5,20,20" Foreground="#FF047800" HorizontalAlignment="Center">
			<Label.Effect>
				<BlurEffect/>
			</Label.Effect>
		</Label>

#561 – Drawing a 3D Donut Using a Radial Gradient

Here’s another visual effect that you can easily create in WPF and Blend, using a gradient.

Let’s say that you want to draw a ring or donut-shaped object that looks 3D.  You can start by using the Ellipse shape to draw a circle.

Next, set the Fill property of the Ellipse to use a radial gradient.  Define five gradient stops, as shown, and use the gradient tool to position them as shown.

For the hole in the middle of the donut, you can use an opacity mask.  Use a paint program to create an opaque circle that is the same size as the circle, but with a smaller transparent circle in its center.  (Trick: Run your app and screen capture what you have so far, then tweak it in a paint program).  Finally, set this image as the OpacityMask of the Ellipse.

<Ellipse.OpacityMask>
    <ImageBrush ImageSource="Images\Mask.png"/>
</Ellipse.OpacityMask>

Voila.

#242 – Drawing Text Using DrawGlyphRun

If you want an even lower-level mechanism for drawing text than provided by DrawingContext.DrawText, you can use DrawingContext.DrawGlyphRun.

DrawGlyphRun lets you draw text based on glyphs, which are individual characters within a font that typically represent a single character in a language.

Below is an example of rendering the word “Wow” using DrawGlyphRun, in a custom DrawingVisual.

    class MyDrawingVisual : DrawingVisual
    {
        public MyDrawingVisual()
        {
            using (DrawingContext dc = RenderOpen())
            {
                GlyphRun gr = new GlyphRun(
                    new GlyphTypeface(new Uri(@"C:\Windows\Fonts\BKANT.TTF")),
                    0,       // Bi-directional nesting level
                    false,   // isSideways
                    96,      // pt size
                    new ushort[] { 58, 82, 90 },   // glyphIndices
                    new Point(100.0, 100.0),           // baselineOrigin
                    new double[] { 80.0, 45.0, 0.0 },  // advanceWidths
                    null,    // glyphOffsets
                    null,    // characters
                    null,    // deviceFontName
                    null,    // clusterMap
                    null,    // caretStops
                    null);   // xmlLanguage

                // Draw the text at a location
                dc.DrawGlyphRun(Brushes.Black, gr);
            }
        }
    }

The glyphIndices parameter specifies a list of indexes into the font for the glyphs that are to be rendered.

#241 – Rendering Text Using DrawText

You can render text into a visual using the DrawText method of a DrawingContextDrawText renders text that is represented by an instance of FormattedText into the drawing context at a specified location.

Below is an example of drawing some text into a custom DrawingVisual.

    class MyDrawingVisual : DrawingVisual
    {
        public MyDrawingVisual()
        {
            using (DrawingContext dc = RenderOpen())
            {
                // Create formatted text--in a particular font at a particular size
                FormattedText ft = new FormattedText(
                    "When things are at their worst I find something always happens.",
                    CultureInfo.CurrentCulture,
                    FlowDirection.LeftToRight,
                    new Typeface(new FontFamily("Century"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal),
                    24,    // 36 pt type
                    Brushes.Black);

                // Draw the text at a location
                dc.DrawText(ft, new Point(10.0, 10.0));
            }
        }
    }

Once we host this DrawingVisual in an UIElement, we can see the text in a window:

#240 – Shape vs. DrawingVisual

We’ve seen two ways to render custom 2D geometries–by inheriting from DrawingVisual and hosting in an UIElement or by inheriting from Shape and instancing your object directly in XAML.

You might wonder which of these methods to use for drawing custom 2D objects.

Shape is at a higher level of abstraction than DrawingVisualShape provides the following functionality, beyond what you get with DrawingVisual:

  • Derives from FrameworkElement, so you can include your subclass directly into a logical tree as a child of a Panel
  • Takes care of things like the Pen used to render the geometry (Stroke and StrokeThickness) and the Brush used to fill the interior of the geometry

Below is an example of including several instances of a custom Shape and specifying different stroke/fill properties for each instance.

	<StackPanel Orientation="Horizontal">
		<local:MyWeirdShape Stroke="Black" StrokeThickness="2" Fill="Orange"/>
		<local:MyWeirdShape Stroke="Red" StrokeThickness="1" Fill="DimGray"/>
		<local:MyWeirdShape Stroke="Blue" StrokeThickness="10" Fill="White"/>
	</StackPanel>

#239 – Creating a Custom Shape by Overriding the Shape Class

If you need a specialized shape that you can’t use any of the Shape subclasses to draw, you can create your own custom class that inherits from Shape.

You define the shape to be drawn by overriding the Shape.DefiningGeometry property.  In the get accessor, you create and return an instance of a Geometry.  In the example below, we create a new Geometry and then use the StreamGeometryContext to draw the geometry.

    public class MyWeirdShape : Shape
    {
        protected override Geometry DefiningGeometry
        {
            get { return GenerateMyWeirdGeometry(); }
        }

        private Geometry GenerateMyWeirdGeometry()
        {
            StreamGeometry geom = new StreamGeometry();
            using (StreamGeometryContext gc = geom.Open())
            {
                // isFilled = false, isClosed = true
                gc.BeginFigure(new Point(50.0, 50.0), false, true);
                gc.ArcTo(new Point(75.0, 75.0), new Size(10.0, 20.0), 0.0, false, SweepDirection.Clockwise, true, true);
                gc.ArcTo(new Point(100.0, 100.0), new Size(10.0, 20.0), 0.0, false, SweepDirection.Clockwise, true, true);
            }

            return geom;
        }
    }

Using the new object in XAML:

	<StackPanel>
		<local:MyWeirdShape Height="150" Width="150" Stroke="Black" StrokeThickness="2"/>
	</StackPanel>

#237 – Drawing Shapes with the Shape Subclasses

You can include various geometric shapes directly in a WPF GUI using subclasses of the Shape class.  Since Shape derives from FrameworkElement, you can insert instances of the Shape subclasses directly into a Panel container.

The various shapes that you can draw are:

  • Ellipse - Draw an ellipse
  • Line – Draw a line segment
  • Path – Draw an arbitrary geometry consisting of a series of line segments
  • Polygon – Draw a closed polygon
  • Polyline – Draw a polyline–a series of connected line segments
  • Rectangle – Draw a rectangle

Here’s a simple example in XAML that demonstrates the various shapes.

		<Ellipse Height="100" Width="180" Fill="Blue" Stroke="Black" StrokeThickness="2"/>
		<Line X1="0" X2="180" Y1="20" Y2="80" Stroke="Brown" StrokeThickness="2"/>
		<Path Data="M 50,10 L 100,80 L 30,90 L 60,70 Z M 40,20 L 20,40 L 30,60" Stroke="DarkGreen" StrokeThickness="2"/>
		<Polygon Points="20,20 60,60 60,100 140,80 120,40" Stroke="DarkViolet" StrokeThickness="2"/>
		<Polyline Points="20,20 60,60 60,100 140,80 120,40" Stroke="DarkViolet" StrokeThickness="2"/>
		<Rectangle Height="100" Width="180" Stroke="Crimson" StrokeThickness="2"/>

Here’s how these shapes look when rendered:

Follow

Get every new post delivered to your Inbox.

Join 238 other followers