This chapter is taken from book "Programming Windows Phone 7" by Charles Petzold published by Microsoft press. http://www.charlespetzold.com/phone/index.html
WriteableBitmap and UIElement
WriteableBitmap does not including any facility to save bitmaps. However, the WriteableBitmap class does give you access to all the pixels that define the bitmap. Only one pixel format is supported, where each pixel is a 32-bit value. You can obtain the pixel bits from an existing bitmap, or set new pixel bits on a WriteableBitmap to define the image. Access to these pixel bits allows you a great deal of flexibility in how you save or load bitmaps. You can provide your own bitmap "encoder" to save pixel bits in a particular bitmap format, or your own "decoder" to access a file of a particular format and convert to the uncompressed pixel bits.
WriteableBitmap has two ways to get the visuals of a UIElement onto a bitmap. The first uses one of the constructors:
WriteableBitmap writeableBitmap = new WriteableBitmap(element, transform);
The element argument is of type UIElement and the transform argument is of type Transform. This constructor creates a bitmap based on the size of the UIElement argument as possibly modified by the Transform argument (which you can set to null).
Here's a simple sample program. The content grid is given a background based on the current accent color. It contains a TextBlock and an Image element:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0"
Background="{StaticResource
PhoneAccentBrush}">
<TextBlock
Text="Tap anywhere to
capture page"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
<Image
Name="img"
Stretch="Fill" />
</Grid>
The Image element has no bitmap to display but when it does, it will ignore the bitmap's aspect ratio to fill the content grid and obscure the TextBlock.
When the screen is tapped, the code-behind file simply sets the Image element source to a new WriteableBitmap based on the page itself:
namespace
RecursivePageCaptures
{
public partial
class MainPage
: PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
}
protected
override void OnManipulationStarted(ManipulationStartedEventArgs
args)
{
img.Source = new
WriteableBitmap(this,
null);
args.Complete();
args.Handled = true;
base.OnManipulationStarted(args);
}
}
}
When you first run the program, the screen looks like this:

Tap once, and the whole page becomes the bitmap displayed by the Image element:

Keep in mind that the PhoneApplicationPage object being captured has its Background property set to the default value of null, so that's why you see the original background of the content panel behind the captured titles. You can continue tapping the screen to recapture the page content, now including the previous Image element:

The Pixel Bits
The Pixels property of WritableBitmap is an array of int, which means that each pixel is 32 bits wide. The Pixels property itself is get-only so you can't replace the actual array, but you can set and get elements of that array.
When you first create a WriteableBitmap, all the pixels are zero, which you can think of as "transparent black" or "transparent white" or "transparent chartreuse."
By directly writing into the Pixels array of a WriteableBitmap you can create any type of image you can conceive. Comparatively simple algorithms let you create styles of brushes that are not supported by the standard Brush derivatives. The content area of the CircularGradient project consists solely of an Image element waiting for a bitmap:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Image
Name="img"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
The code-behind file for MainPage defines a rather arbitrary radius value and makes a square WriteableBitmap twice that value. The two for loops for x and y touch every pixel in that bitmap:
namespace
CircularGradient
{
public partial
class MainPage
: PhoneApplicationPage
{
const int
RADIUS = 200;
public MainPage()
{
InitializeComponent();
WriteableBitmap writeableBitmap =
new WriteableBitmap(2
* RADIUS, 2 * RADIUS);
for (int
y = 0; y < writeableBitmap.PixelWidth; y++)
{
if (Math.Sqrt(Math.Pow(x
- RADIUS, 2) + Math.Pow(y - RADIUS, 2)) <
RADIUS)
{
double angle = Math.Atan2(y -
RADIUS, x - RADIUS);
byte R = (byte)(255 *
Math.Abs(angle) /
Math.PI);
byte B = (byte)(255 - R);
int color = 255 << 24 | R << 16 | B;
writeableBitmap.Pixels[y * writeableBitmap.PixelWidth +
x] = color;
}
}
writeableBitmap.Invalidate();
img.Source = writeableBitmap;
}
}
}
The center of the WriteableBitmap is the point (200, 200). The code within the nested for loops begins by skipping every pixel that is more than 200 pixels in length from that center. Within the square bitmap, only a circle will have non-transparent pixels.

Vector Graphics on a Bitmap
You can combine the two approaches of drawing on a WriteableBitmap. The next sample displays a Path on a WriteableBitmap against a gradient that uses transparency so that you can see how the premultiplied alphas work.
The Path Markup Syntax for the cat is defined in a
Path element in
the Resources
section of the MainPage.xaml file:
<phone:PhoneApplicationPage.Resources>
<Path
x:Key="catPath"
Data="M 160 140 L 150 50
220 103
M 320 140 L 330 50 260
103
M 215 230 L 40 200
M 215 240 L 40 240
M 215 250 L 40 280
M 265 230 L 440 200
M 265 240 L 440 240
M 265 250 L 440 280
M 240 100 A 100 100 0 0 1
240 300
A 100 100 0 0 1 240 100
M 180 170 A 40 40 0 0 1
220 170
A 40 40 0 0 1 180 170
M 300 170 A 40 40 0 0 1
260 170
A 40 40 0 0 1 300 170" />
</phone:PhoneApplicationPage.Resources>
I'm using this Path element solely to force the XAML parser to acknowledge this string as Path Markup Syntax; the Path element won't be used for any other purpose in the program.
The content area consists of just an Image element awaiting a bitmap:
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<Image
Name="img"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
Everything else happens in the constructor of the
MainPage class. It's a
little lengthy but well commented and I'll also walk you through the logic:
namespace
VectorToRaster
{
public partial
class MainPage
: PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
// Get PathGeometry from resource
Path catPath =
this.Resources["catPath"]
as Path;
PathGeometry pathGeometry =
catPath.Data as
PathGeometry;
catPath.Data = null;
// Get geometry bounds
Rect bounds =
pathGeometry.Bounds;
// Create new path for rendering on bitmap
Path newPath =
new Path
{
Stroke = this.Resources["PhoneForegroundBrush"]
as Brush,
StrokeThickness = 5,
Data = pathGeometry,
};
// Create the WriteableBitmap
WriteableBitmap
writeableBitmap =
new
WriteableBitmap((int)(bounds.Width +
newPath.StrokeThickness),
(int)(bounds.Height
+ newPath.StrokeThickness));
// Color the background of the bitmap
Color baseColor = (Color)this.Resources["PhoneAccentColor"];
// Treat the bitmap as an ellipse:
// radiusX and radiusY are also
the centers!
double radiusX =
writeableBitmap.PixelWidth / 2.0;
double radiusY =
writeableBitmap.PixelHeight / 2.0;
for (int
y = 0; y < writeableBitmap.PixelHeight; y++)
for (int
x = 0; x < writeableBitmap.PixelWidth; x++)
{
double angle =
Math.Atan2(y - radiusY, x - radiusX);
double ellipseX = radiusX *
(1 + Math.Cos(angle));
double ellipseY = radiusY *
(1 + Math.Sin(angle));
double ellipseToCenter =
Math.Sqrt(Math.Pow(ellipseX -
radiusX, 2) +
Math.Pow(ellipseY
- radiusY, 2));
double pointToCenter =
Math.Sqrt(Math.Pow(x -
radiusX, 2) + Math.Pow(y - radiusY, 2));
double opacity =
Math.Min(1, pointToCenter / ellipseToCenter);
byte A = (byte)(opacity
* 255);
byte R = (byte)(opacity
* baseColor.R);
byte G = (byte)(opacity
* baseColor.G);
byte B = (byte)(opacity
* baseColor.B);
int color = A << 24 | R <<
16 | G << 8 | B;
writeableBitmap.Pixels[y * writeableBitmap.PixelWidth + x] =
color;
}
writeableBitmap.Invalidate();
// Find transform to move Path to edges
TranslateTransform
translate = new
TranslateTransform
{
X = -bounds.X + newPath.StrokeThickness / 2,
Y = -bounds.Y + newPath.StrokeThickness / 2
};
writeableBitmap.Render(newPath, translate);
writeableBitmap.Invalidate();
// Set bitmap to Image element
img.Source = writeableBitmap;
}
}
}



Join the conversation! Your thoughts help the community grow.