I would like to convert the WPF display into a PNG file.
This is being used to create an invoice for a holiday. The code I currently have creates the PNG file but it creates a blank document.
Here is what I am trying to do:
var screenCap = new RenderTargetBitmap((int)this.Width, (int)this.Height, 96d, 96d, PixelFormats.Default);
var encoder = new PngBitmapEncoder();
var outputScreen = BitmapFrame.Create(screenCap);
encoder.Frames.Add(outputScreen);
using (var file = File.OpenWrite(#"Invoice.png"))
{
encoder.Save(file);
}
You need to call screenCap.Render(this) after you've constructed the object, but before you call BitmapFrame.Create(screenCap)
The following code will capture a specified element from the display and write it to a specified path:
private void SaveImage(FrameworkElement targetElement, string savePath)
{
var bmp = new RenderTargetBitmap((int)targetElement.ActualWidth, (int)targetElement.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(targetElement);
using (var stream = new MemoryStream())
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.QualityLevel = 100;
encoder.Save(stream);
File.WriteAllBytes(savePath, stream.ToArray());
}
}
Related
So my program is fixing some image artefacts, it goes like this:
void FixFile(string path)
{
var bmp = new WriteableBitmap(new BitmapImage(new Uri(path)));
bmp.Lock();
// magick
bmp.Unlock();
using (var stream = new FileStream(path.Replace("DSC", "fix_DSC"), FileMode.Create))
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.Save(stream);
}
}
The problem is that fixed image contains no EXIF data. How do I transfer EXIF data from the original image?
Load the source bitmap as BitmapFrame, not BitmapImage. Then pass the Metadata property of the source to the new BitmapFrame that is added to the Frames collection of the encoder.
public void FixFile(string path)
{
var source = BitmapFrame.Create(new Uri(path));
var metadata = (BitmapMetadata)source.Metadata;
var bmp = new WriteableBitmap(source);
bmp.Lock();
// magick
bmp.Unlock();
var target = BitmapFrame.Create(bmp, null, metadata, null); // here
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(target);
using (var stream = File.OpenWrite(path.Replace("DSC", "fix_DSC")))
{
encoder.Save(stream);
}
}
Is there any way to do that ?
I can cast the object to ImageSource and assign to Image but I have to be able to store it in byte[]. All the methods I found use casting to BitMap and that won't work here.
Here is a solution I found. The key was the use of DrawingVisual to create temporary image internally.
public static byte[] ImageToBytes(ImageSource imageSource)
{
var bitmapSource = imageSource as BitmapSource;
if (bitmapSource == null)
{
var width = (int)imageSource.Width;
var height = (int)imageSource.Height;
var dv = new DrawingVisual();
using (var dc = dv.RenderOpen())
{
dc.DrawImage(imageSource, new Rect(0, 0, width, height));
}
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(dv);
bitmapSource = BitmapFrame.Create(rtb);
}
byte[] data;
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
I have this code:
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(525, 50, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(/* controlName */);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (Stream fileStream = File.Create(Environment.CurrentDirectory)
{
pngImage.Save(fileStream);
}
It supposed to take a control from my XAML and create a "screenshot" of it and then save it to an image file. But no matter what directory I try to pass to the File.Create method, I get a System.UnauthorizedAccessException.
How to fix it? Thanks.
Note: I have tried to run Visual Studio as an administrator, didn't work.
You'll have to pass a file name (optionally including a path) to File.Create, like:
File.Create("MyImage.png")
or
var path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "MyImage.png");
using (var fileStream = FileFile.Create(path))
{
pngImage.Save(fileStream);
}
To get rid of the black image try manually disposing the object after you've used it. Example shown below.
private void button_Click(object sender, RoutedEventArgs e)
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(525, 50, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(btn_StartStop);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
FileStream stream = new FileStream("screenshot.png", FileMode.Create);
pngImage.Save(stream);
stream.Dispose();
}
I have a WPF control in my PowerPoint add-in that hosts an image that I want to be able to drag & drop onto the active slide. I can get the image to appear on the slide, but the transparent areas are rendered in black.
My code to initialize the drag from my attached behavior:
var targetBitmap = new RenderTargetBitmap(
(int) MyWpfControl.ActualWidth,
(int) MyWpfControl.ActualHeight,
96d, 96d, PixelFormats.Default);
targetBitmap.Render(MyWpfControl);
var dataObject = new DataObject(
DataFormats.Bitmap,
targetBitmap);
DragDrop.DoDragDrop(MyWpfControl, dataObject, DragDropEffects.Copy)
Thinking that maybe I needed to pass a System.Drawing.Image, I attempted this modification, which only resulted in the transparent areas being rendered in gray:
var targetBitmap = new RenderTargetBitmap(
(int) MyWpfControl.ActualWidth,
(int) MyWpfControl.ActualHeight,
96d, 96d, PixelFormats.Default);
targetBitmap.Render(MyWpfControl);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(targetBitmap));
var ms = new MemoryStream();
encoder.Save(ms);
var dataObject = new DataObject(DataFormats.Bitmap, Image.FromStream(ms, true))
DragDrop.DoDragDrop(MyWpfControl, dataObject, DragDropEffects.Copy)
I did a test where I replaced the memory stream with a file stream, and the image that was written did indeed have the correct transparency.
So what am I missing here? How can I maintain transparency?
I was able to resolve this by following the instructions in this blog post. The solution was to use the EnhancedMetafile DataFormat in my DataObject.
Edit:
Here’s the code that initiates the drag operation.
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Bitmap bitmap = ImageToBitmap(e.Source as System.Windows.Controls.Image);
DataObject data = new DataObject(DataFormats.EnhancedMetafile, MakeMetafileStream(bitmap));
DragDrop.DoDragDrop((DependencyObject)e.Source, data, DragDropEffects.Copy);
}
This makes use of a utility function to convert the Image to a Bitmap:
private Bitmap ImageToBitmap(System.Windows.Controls.Image image)
{
RenderTargetBitmap rtBmp = new RenderTargetBitmap((int)image.ActualWidth, (int)image.ActualHeight,
96.0, 96.0, PixelFormats.Pbgra32);
image.Measure(new System.Windows.Size((int)image.ActualWidth, (int)image.ActualHeight));
image.Arrange(new Rect(new System.Windows.Size((int)image.ActualWidth, (int)image.ActualHeight)));
rtBmp.Render(image);
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);
return new System.Drawing.Bitmap(stream);
}
This also requires a utility function that converts a Bitmap to a stream containing a Metafile, taken from Stack Overflow.
// From Convert an image into WMF with .NET?
private MemoryStream MakeMetafileStream(Bitmap image)
{
Graphics graphics = null;
Metafile metafile = null;
var stream = new MemoryStream();
try
{
using (graphics = Graphics.FromImage(image))
{
var hdc = graphics.GetHdc();
metafile = new Metafile(stream, hdc);
graphics.ReleaseHdc(hdc);
}
using (graphics = Graphics.FromImage(metafile))
{ graphics.DrawImage(image, 0, 0); }
}
finally
{
if (graphics != null)
{ graphics.Dispose(); }
if (metafile != null)
{ metafile.Dispose(); }
}
return stream;
}
i'am making a programm where i want my RichTextBox
content (text+images) to be saved as an image (jpg/png). I tried to use this solution
but i get only black filled image from
SaveUIAsGraphicFile()
I also tried to create FormattedText from my rtb control, printing it works fine, but its not possible to insert images in there. Maybe it is possible to print FlowDocument somehow?
You could use something like the following method to create a bitmap from a FlowDocument:
public BitmapSource FlowDocumentToBitmap(FlowDocument document, Size size)
{
document = CloneDocument(document);
var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
paginator.PageSize = size;
var visual = new DrawingVisual();
using (var drawingContext = visual.RenderOpen())
{
// draw white background
drawingContext.DrawRectangle(Brushes.White, null, new Rect(size));
}
visual.Children.Add(paginator.GetPage(0).Visual);
var bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height,
96, 96, PixelFormats.Pbgra32);
bitmap.Render(visual);
return bitmap;
}
public FlowDocument CloneDocument(FlowDocument document)
{
var copy = new FlowDocument();
var sourceRange = new TextRange(document.ContentStart, document.ContentEnd);
var targetRange = new TextRange(copy.ContentStart, copy.ContentEnd);
using (var stream = new MemoryStream())
{
sourceRange.Save(stream, DataFormats.XamlPackage);
targetRange.Load(stream, DataFormats.XamlPackage);
}
return copy;
}
and then use it like shown below to save a RichTextBox's Document to an image file.
var doc = richTextBox.Document;
var bm = FlowDocumentToBitmap(doc, new Size(richTextBox.ActualWidth, richTextBox.ActualHeight));
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bm));
using (var stream = new FileStream("doc.jpg", FileMode.Create))
{
encoder.Save(stream);
}
You can spend HOURS chasing around trying to figure out why the width is wrong when in reality its trying to paginate in columns. Set the document's columnwidth to the full width of your output bitmap.
public Bitmap FlowDocumentToBitmap(FlowDocument document, Size size)
{
document = CloneDocument(document);
document.ColumnWidth = size.Width;// <- Add this line