I'm writing an application for the surface that requires displaying data in a table (i.e. DataGrid). This is great, except the table captures the touch interactions for the ScatterViewItem control (basically a panel that can be spun, shrunk, and moved by the user). This prevents the user from easily manipulating the ScatterViewItem.
To solve this problem, I thought it would be easy to draw the control to an image and just put that up. It seems I was wrong. Here are all my attempts:
http://pastie.org/private/gfkkv9f6apgrqi1ucspwpa (no need to read this, unless you think it will be useful. That's why it's in pastie and not on here)
I'm putting the DataGrid inside of another Grid, because otherwise it won't measure properly:
Grid g = new Grid();
g.Children.Add(dataTable);
SurfaceScrollViewer viewer = new SurfaceScrollViewer();
viewer.Content = Utility.SaveWPFControlAsImage(g);
If we change that last line to
viewer.Content = g;
We get a good table:
If we don't, we get:
SaveWPFControlAsImage is as follows:
public static System.Windows.Controls.Image SaveWPFControlAsImage(FrameworkElement e)
{
e.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
RenderTargetBitmap targetBitmap =
new RenderTargetBitmap((int)e.DesiredSize.Width,
(int)e.DesiredSize.Height,
96d, 96d,
PixelFormats.Default);
targetBitmap.Render(e);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(targetBitmap));
MemoryStream stream = new MemoryStream();
encoder.Save(stream);
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.StreamSource = new MemoryStream(stream.ToArray());
bmp.EndInit();
return new System.Windows.Controls.Image()
{
Source = bmp,
};
}
So maybe I'm just not rendering it right, or, maybe, I'm just going about this at the wrong angle...
In WPF you have a VisualBrush which allows you to capture a live preview of a given control. Also if you don't want any input of a given control, you can always set IsHitTestVisible="False".
Related
I am currently converting a Silverlight application into WPF. In my silverlight application I have the code
WriteableBitmap sceneBitmap = new WriteableBitmap(scene, new TranslateTransform() { Y = 10 });
WriteableBitmap newone = TimelineMainHelper.CropImage(sceneBitmap, 0, 0, sceneBitmap.PixelWidth, sceneBitmap.PixelHeight - 25);
newone.Invalidate();
img.Source = newone;
Where scene is a control.
When putting this into WPF there are no overloads for the writeablebitmap class which take UIElement and Transform as the parameters. Firstly I was wondering why this is? and secondly I was wondering if there was a way getting a control to a writeablebitmap
Instead you will want to use RenderTargetBitmap and CroppedBitmap I believe:
RenderTargetBitmap rtb = new RenderTargetBitmap((int)scene.ActualWidth, (int)scene.ActualHeight, 96, 96, System.Windows.Media.PixelFormats.Pbgra32);
rtb.Render(this.sceneBitmap);
CroppedBitmap crop = new CroppedBitmap(sceneBitmap, new Int32Rect(0, 0, (int)sceneBitmap.ActualWidth, (int)sceneBitmap.ActualHeight));
Then you can do something like:
System.Windows.Controls.Image img = new Image();
img.Source = crop;
And go from there.
Disclaimer:
You may need to use different overloads and what not to do exactly what you wish. I just took a shot guessing what parameters to pass given your snippet.
This thing has been driving me crazy.
I have a Visiblox chart. which I'm currently exporting as a PNG using the following code:
var chart = this.CalibrationChartVisibility == Visibility.Visible ? this.calibrationChart : this.residualChart;
var transform = chart.LayoutTransform;
chart.LayoutTransform = null;
var width = (int)chart.ActualWidth;
var height = (int)chart.ActualHeight;
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(chart);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
var stream = new MemoryStream();
encoder.Save(stream);
stream.Position = 0;
chart.LayoutTransform = transform;
return stream.ToArray();
and I get something like this:
But now I need to also need to export it as a JPEG. I thought it would be simple, just change the encoder but this is what I get:
I've tried this:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/31ac62d4-399b-4f2e-a9b9-749efe7528b6/rendertargetbitmap-to-file-problem?forum=wpf
and this:
http://www.grumpydev.com/2009/01/03/taking-wpf-screenshots/
and this:
Get a bitmap image from a Control view
and ervey sugestion on this post:
How to save image using JpegBitmapEncoder
or this one:
saving WPF InkCanvas to a JPG - image is getting cropped
and everything else which crossed my mind, but the outcome is still the same.
There must be something I'm overlooking but I have no idea what it is.
To sum up comments this seems to be a background issue as PNG, attached to this question, has everything transparent apart from chart lines and since JPEG does not support transparency all that is transparent will be black.
Simpliest solution would be to set background of chart to some color
Disclaimer: I provided this answer for the System.Drawing.Image from ImageSource in Resources question and was going to vote to close this question as a duplicate of the other, but couldn't because the question author didn't accept the answer.
In WPF, every UI element extends the Visual Class which Provides rendering support in WPF. There is also a RenderTargetBitmap Class that has a Render Method that takes a Visual object as an input parameter. So you could set your ImageSource as the Source property of an Image and simply render the Image to a Bitmap image:
Image yourImageObject = new Image();
yourImageObject.Source = yourImageSource;
RenderTargetBitmap renderTargetBitmap =
new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
renderTargetBitmap.Render(yourImageObject);
// Save to .png file
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (Stream stream = File.Create(filepath))
{
pngBitmapEncoder.Save(stream);
}
As this is well documented on the internet, I won't bother to repeat the whole story here. To find out the full story, please see the How to Render Bitmap or to Print a Visual in WPF page from the Dot NET Tricks website, which will also help you with your printing requirement.
UPDATE >>>
Ok, so most of this applies to you in just the same way, except that you'd want to use a JpegBitmapEncoder object instead. This example from the linked page shows another way to save a JPEG image:
int width = 128;
int height = width;
int stride = width / 8;
byte[] pixels = new byte[height * stride];
// Define the image palette
BitmapPalette myPalette = BitmapPalettes.Halftone256;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
width,
height,
96,
96,
PixelFormats.Indexed1,
myPalette,
pixels,
stride);
FileStream stream = new FileStream("new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
TextBlock myTextBlock = new TextBlock();
myTextBlock.Text = "Codec Author is: " + encoder.CodecInfo.Author.ToString();
encoder.FlipHorizontal = true;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
encoder.Rotation = Rotation.Rotate90;
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
Please let me know if you have any problems.
Maybe it's a stupid question, but I have some problems with finding the proper answer:S
How to get frames as Bitmap's or Image's (or something similar) from DrawingGroup? I don't actually know how to bite it. I tried to look for it in the Internet, but had problems with finding something useful.
If you need an image to be used as the Source of an Image control, you could simply put the drawing into a DrawingImage:
var drawing = ...
var drawingImage = new DrawingImage(drawing);
image.Source = drawingImage;
If the question is about creating a BitmapSource (i.e. something that can be encoded by a BitmapEncoder via a BitmapFrame), there is no direct conversion. You have to put the image into an intermediate Image control and render that control into a RenderTargetBitmap, which is a BitmapSource:
var drawing = ...
var drawingImage = new DrawingImage(drawing);
var image = new Image { Source = drawingImage };
var bitmap = new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32);
image.Arrange(new Rect(0, 0, bitmap.Width, bitmap.Height));
bitmap.Render(image);
I have a situation where I want to convert some XAML to an image, so I created a RichTextBox and then took the image of it. Now problem is that words in image is blurred, any idea how I might be able to fix it?
public System.Drawing.Bitmap ConvertXamltoImage(string XamlString, int Width, int Height)
{
RichTextBox AdContentRichTextBox = new RichTextBox() { Width = Width, Height = Height };
AdContentRichTextBox.BorderThickness = new Thickness(0);
XmlReader _XmlReader = XmlReader.Create(new StringReader(XamlString));
AdContentRichTextBox.Document = XamlString;
var size = new Size(Width, Height);
AdContentRichTextBox.Measure(size);
AdContentRichTextBox.Arrange(new Rect(size));
RenderTargetBitmap bmp = new RenderTargetBitmap(Width, Height, 300, 300, PixelFormats.Pbgra32);
bmp.Render(AdContentRichTextBox);
DrawingVisual _drawingVisual = new DrawingVisual();
using (DrawingContext _drwaingContext = _drawingVisual.RenderOpen())
{
VisualBrush _visualBrush = new VisualBrush(AdContentRichTextBox);
}
PngBitmapEncoder _png = new PngBitmapEncoder();
_png.Frames.Add(BitmapFrame.Create(bmp));
System.Drawing.Bitmap _tempBitmap = null;
using (Stream _fileStream = new MemoryStream())
{
_png.Save(_fileStream);
_tempBitmap = new System.Drawing.Bitmap(_fileStream);
_fileStream.Flush();
}
return _tempBitmap;
}
Hmmmm..there could be lots of things all interacting here:
1st
"Grayscale fall back - if ClearType is disabled or one is rendering text in certain situations where the ClearType algorithm cannot be run, WPF will use a grayscale rendering algorithm to antialias the rendered text."
Rendering Text to a RenderTargetBitmap seems to be one of those situations....(the renderer switches from a hardware to a software path).
2nd
In addition NET 4 switched the default scaling algorithm from high-quality (Fant) to low-quality (Bi-Linear).....now that shouldn't come into play here as it doesn't look like you are scaling the bitmap in any way...but you never know what's going on inside. It's possible to switch the scaler back to the higher quality one.
http://www.olsonsoft.com/blogs/stefanolson/post/Workaround-for-low-quality-bitmap-resizing-in-WPF-4.aspx
3rd
You may need to take into account the parent container of the RichTextBox...see last link below, mentions it can distort the font rendering.
Problems with rendering text as bitmaps using WPF
Some ideas on how to work around this are:
render the RichTextBox at a higher resolution e.g. 600dpi, and then scale down the bitmap (probably will make no difference)
capture the screen....difficult or not practical if your visual is offscreen/obscured, etc.
See related links:
http://windowsclient.net/wpf/white-papers/wpftextclarity.aspx
WPF RenderTargetBitmap downscaling text ClearType to GreyScale
WPF RenderTargetBitmap downscaling TextRenderMode to GreyScale
WPF text rendering inconsistencies
I'm trying to create a JPG from part of my WPF Applications. Like a screenshot, only of individual UIElements. I started here: http://www.grumpydev.com/2009/01/03/taking-wpf-screenshots/
I am using his extension method, which essential allows you to get a byte[] with UIElement.GetJpgImage(). This can then be written using a filestream to a JPG image. If I make a JPG of the whole window, it looks just fine! However, this is not ideal because it just captures what the user sees. Things that are not visible because of the scrollviewer or because their parent was animated to a small size won't show up.
If I take a "screenshot" of, say, a grid that I use for layout:
alt text http://img697.imageshack.us/img697/4233/fullscreenshot2.jpg
I get this crap with a black background. I don't want that. Furthermore, if I've collapsed this grid's height using animation, I won't get anything at all. Those are actually templated checkboxes, they should have black text above them, and the background of the grid should be white. Here's the code that someone else wrote to return the byte[] array that gets written to a filestream:
public static byte[] GetJpgImage(this UIElement source, double scale, int quality)
{
double actualHeight = source.RenderSize.Height;
double actualWidth = source.RenderSize.Width;
double renderHeight = actualHeight * scale;
double renderWidth = actualWidth * scale;
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int) renderWidth, (int) renderHeight, 96, 96, PixelFormats.Pbgra32);
VisualBrush sourceBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.PushTransform(new ScaleTransform(scale, scale));
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight)));
}
renderTarget.Render(drawingVisual);
JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.QualityLevel = quality;
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget));
Byte[] _imageArray;
using (MemoryStream outputStream = new MemoryStream())
{
jpgEncoder.Save(outputStream);
_imageArray = outputStream.ToArray();
}
return _imageArray;
}
Somewhere in there, we're getting a black background. Any insight?
EDIT: If I set the grid's background property to white, the screenshot comes out as expected. However, it's not feasible to set everything's background that I need to take a screenshot of.
Just a guess, I would think that a black background would represent portions of the byte array that are not set to anything in this process. The initial zeros in the array would appear as black.
To avoid this, I suggest initializing the array with 0xFF (byte.MaxValue) values.
UPDATED:
From looking at this closer, I think you should draw a white rectangle onto the image before you render the UI element. That ought to work anyway.
Just before this line of code
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight)));
put something like this
drawingContext.DrawRectangle(Brushes.White, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight)));
Unfortunately the only thing that worked was just setting the element's background in the XAML. I didn't want to do this, but I guess it is what I need to do in this case. Thanks anyway for the suggestion.