I am trying to save a drawing from InkCanvas. I also implemented zoom capabilities and scrollbars to increase the size of the InkCanvas. The picture that is saved depends on the zoom, and, even when the size is the original, it does not save the complete InkCanvas.
XAML for InkCanvas:
<ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Border x:Name="brd_borger" MouseWheel="cnv_MouseWheel" Background="Aqua" Height="Auto" Width="Auto">
<InkCanvas x:Name="cnv" Width="Auto" Height="Auto" Background="Aqua"
PreviewMouseLeftButtonDown="cnv_MouseLeftButtonDown"
PreviewMouseRightButtonDown="cnv_MouseRightButtonDown"
SelectionChanged="cnv_SelectionChanged"
SelectionMoving="cnv_SelectionMoving"
SelectionMoved="cnv_SelectionMoved"
EditingMode="None">
</InkCanvas>
</Border>
</ScrollViewer>
Zooming Code:
private void cnv_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
var matrix = cnv.LayoutTransform.Value;
if (e.Delta > 0)
{
matrix.ScaleAt(1.5, 1.5, e.GetPosition(this).X, e.GetPosition(this).Y);
}
else
{
matrix.ScaleAt(1.0/1.5, 1.0/1.5, e.GetPosition(this).X, e.GetPosition(this).Y);
}
cnv.LayoutTransform = new MatrixTransform(matrix);
}
}
Saving Code:
private void Save_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = ".png";
dlg.Filter = ODLG_FILTER_IMAGES;
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
string filename = dlg.FileName;
MemoryStream ms = new MemoryStream();
FileStream fs = new FileStream(filename, FileMode.Create);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)cnv.Width, (int)cnv.Height, 96d, 96d, PixelFormats.Default);
rtb.Render(cnv);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fs);
fs.Close();
}
}
Since you don't want the size of the image to be the same as the actual size of the InkCanvas, you need to specify the actual target image size somwhere. You could then use a DrawingContext and a VisualBrush to create the image:
private void Save_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = ".png";
dlg.Filter = ODLG_FILTER_IMAGES;
if (dlg.ShowDialog() == true)
{
string filename = dlg.FileName;
MemoryStream ms = new MemoryStream();
FileStream fs = new FileStream(filename, FileMode.Create);
//define your image size here...
const int Width = 200;
const int Height = 200;
RenderTargetBitmap rtb = new RenderTargetBitmap(Width, Height, 96d, 96d, PixelFormats.Default);
Rect bounds = VisualTreeHelper.GetDescendantBounds(cnv);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(cnv);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv); ;
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fs);
fs.Close();
}
}
Related
with the code I gave, I can take a screenshot of the section where the canvas is located. However, I am able to get on the predetermined path. What I want to do is I want to save the image to the section I want via savefiledialog. How can I do that.
private void btnKaydet_Click(object sender, RoutedEventArgs e)
{
UIElement element = cnvs as UIElement;
Uri path = new Uri(#"c:\screenshot.png");
CaptureScreen(element, path);
}
public void CaptureScreen(UIElement source, Uri destination)
{
try
{
double Height, renderHeight, Width, renderWidth;
Height = renderHeight = source.RenderSize.Height;
Width = renderWidth = source.RenderSize.Width;
//Specification for target bitmap like width/height pixel etc.
RenderTargetBitmap renderTarget = new
RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96,
PixelFormats.Pbgra32);
//creates Visual Brush of UIElement
VisualBrush visualBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext =
drawingVisual.RenderOpen())
{
//draws image of element
drawingContext.DrawRectangle(visualBrush, null, new
Rect(new System.Windows.Point(0, 0), new System.Windows.Point(Width, Height)));
}
//renders image
renderTarget.Render(drawingVisual);
//PNG encoder for creating PNG file
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new
FileStream(destination.LocalPath, FileMode.Create,
FileAccess.Write))
{
encoder.Save(stream);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
We can use the FileSaveDialog class.
In your using statement place
using Microsoft.Win32;
using System.Windows;
You may have to add a reference to System.Windows.Forms
A popular WPF open source dialog library can be found here as well if you opt not use the Microsoft version.
https://github.com/ookii-dialogs/ookii-dialogs-wpf
private void BtnKaydet_OnClick(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
if (saveFileDialog.ShowDialog() == true)
{
UIElement element = this.cnvs as UIElement;
Uri path = new Uri(saveFileDialog.FileName);
CaptureScreen(element, path);
}
}
public void CaptureScreen(UIElement source, Uri destination)
{
try
{
double Height, renderHeight, Width, renderWidth;
Height = renderHeight = source.RenderSize.Height;
Width = renderWidth = source.RenderSize.Width;
//Specification for target bitmap like width/height pixel etc.
RenderTargetBitmap renderTarget =
new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32);
//creates Visual Brush of UIElement
VisualBrush visualBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
//draws image of element
drawingContext.DrawRectangle(visualBrush, null,
new Rect(new System.Windows.Point(0, 0), new System.Windows.Point(Width, Height)));
}
//renders image
renderTarget.Render(drawingVisual);
//PNG encoder for creating PNG file
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new FileStream(destination.LocalPath, FileMode.Create, FileAccess.Write))
{
encoder.Save(stream);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
You can also specify allowed file extension types(.png,.jpg etc.) with the filter parameters.
https://wpf-tutorial.com/dialogs/the-savefiledialog/
I am saving the rendered image using render target bitmap, and it is saved properly in the given size, but when I set background to the grid in which image is placed, I am getting different output. Can any one explain this behavior?
<Grid x:Name="grid1" Grid.Row="0" Background="Red">
<Image x:Name="image1" Source="Images/butterfly.jpg" >
</Image>
</Grid>
Code behind
RenderTargetBitmap result = GetImage(this.grid1);
Stream imageStream = new MemoryStream();
SaveAsPng(result, imageStream);
public static RenderTargetBitmap GetImage(Grid view)
{
Size size = new Size(1122, 750);
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(view), null, new Rect(new Point(), size));
context.Close();
}
result.Render(drawingvisual);
return result;
}
public static void SaveAsPng(RenderTargetBitmap src, Stream outputStream)
{
var saveFileDialog = new SaveFileDialog()
{
Filter = "Image Files (*.bmp, *.png, *.jpg)|*.bmp;*.png;*.jpg"
};
if (saveFileDialog.ShowDialog() == true)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
using (FileStream stream = new FileStream(saveFileDialog.FileName, FileMode.Create))
encoder.Save(stream);
}
}
Without Background
With Background
In order to retain the original element dimensions in the DrawingVisual, you should set the VisualBrush's Stretch to None. If necessary, you can also get precise control of the placement of the visual by setting the VisualBrush's Viewport, Viewbox, AlignmentX and AlignmentY properties.
Also consider passing the result size as an argument to your GetImage method, and use the most general type for the view argument:
public static BitmapSource GetImage(Visual view, Size size)
{
var bitmap = new RenderTargetBitmap(
(int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
var visualBrush = new VisualBrush
{
Visual = view,
Stretch = Stretch.None
};
var drawingvisual = new DrawingVisual();
using (var context = drawingvisual.RenderOpen())
{
context.DrawRectangle(visualBrush, null, new Rect(size));
}
bitmap.Render(drawingvisual);
return bitmap;
}
Also make the SaveAsPng method more flexible by changing the argument type. The outputStream argument isn't used at all, so remove it.
public static void SaveAsPng(BitmapSource src)
Then call both methods like this:
var result = GetImage(grid1, new Size(1122, 750));
SaveAsPng(result);
I have zip file in whick I store FlowDocument (Card.xaml) and folder with images (Media). Images in my FlowDocument have Tag, in which stores their path relative to FlowDocument. For image searching in FlowDocument (FindImages method): Finding all images in a FlowDocument
How I open this zip in RichTextBox. Please pay attention on how I create this images (bitmap), maybe problem there, but i can't understand what's wrong:
string nameOfXamlCardDefault = "Card.xaml";
private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == true)
{
//Open zip file
using (FileStream fullCardZipFile = File.Open(dlg.FileName, FileMode.Open, FileAccess.ReadWrite))
{
//Open zip by ZipArchive
using (ZipArchive archive = new ZipArchive(fullCardZipFile, ZipArchiveMode.Update))
{
//Get entry for xaml (FlowDocument)
ZipArchiveEntry xamlFileEntry = archive.GetEntry(nameOfXamlCardDefault);
//Open xaml
using (Stream xamlFileStreamInZip = xamlFileEntry.Open())
{
//Load FlowDocument into rtbEditor.Document
rtbEditor.Document = XamlReader.Load(xamlFileStreamInZip) as FlowDocument;
//Searching images
List<Image> images = FindImages(rtbEditor.Document).ToList();
foreach (var image in images)
{
var imageFileEntry = archive.GetEntry(image.Tag.ToString());
var bitmap = new BitmapImage();
using (Stream imageFileStream = imageFileEntry.Open())
{
var memoryStream = new MemoryStream();
imageFileStream.CopyTo(memoryStream);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
image.Source = bitmap;
}
}
}
}
}
}
return;
}
All images in RichTextBox displays well, but there is no StreamSource in BitmapImage. And it will lead to error later:
<FlowDocument xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" NumberSubstitution.CultureSource="User" AllowDrop="True" PagePadding="5,0,5,0">
<Paragraph>
<Image Tag="Media/image0.png">
<Image.Source>
<BitmapImage CacheOption="OnLoad" BaseUri="{x:Null}"/>
</Image.Source>
</Image>
<Image Tag="Media/image1.png">
<Image.Source>
<BitmapImage CacheOption="OnLoad" BaseUri="{x:Null}"/>
</Image.Source>
</Image>
</Paragraph>
If just copy image and paste in RichTextBox, then it looks like this and this is good:
<Image Height="400" Width="600">
<Image.Source>
<BitmapImage CacheOption="OnLoad" UriSource="./Image1.bmp"
BaseUri="pack://payload:,,wpf1,/Xaml/Document.xaml"/>
</Image.Source>
Is it possible to embed images from zip like copy them and paste? I tried to use Clipboard and worked with MemoryStream. But it didn't help.
You should rewind the MemoryStream after copying the bitmap data, by setting its Position property or calling its Seek() method.
var imageFileEntry = archive.GetEntry(image.Tag.ToString());
if (imageFileEntry != null)
{
using (var imageFileStream = imageFileEntry.Open())
using (var memoryStream = new MemoryStream())
{
imageFileStream.CopyTo(memoryStream);
memoryStream.Position = 0; // here
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
image.Source = bitmap;
}
}
Instead of a BitmapImage, you could also decode a BitmapFrame from the stream.
var imageFileEntry = archive.GetEntry(image.Tag.ToString());
if (imageFileEntry != null)
{
using (var imageFileStream = imageFileEntry.Open())
using (var memoryStream = new MemoryStream())
{
imageFileStream.CopyTo(memoryStream);
memoryStream.Position = 0;
image.Source = BitmapFrame.Create(
memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
I made a QR-Code Encoder (WPF, c#) by using ZXing.net
I am displaying the QR-Code in an Image-Control
var writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new ZXing.Common.EncodingOptions
{
Height = 200,
Width = 200,
Margin = 0
}
};
var image = writer.Write(qrtext.Text);
qrImg.Source = image;
After that I want to save the image. I was using this example Save Image in a Folder.
private void btnSaveImg_Click(object sender, RoutedEventArgs e)
{
string filePath = #"C:\Users\xxx\Desktop\image.png";
SaveToPng(qrImg, filePath);
}
void SaveToBmp(FrameworkElement visual, string fileName)
{
var encoder = new BmpBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
void SaveToPng(FrameworkElement visual, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
// and so on for other encoders (if you want)
void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
Unfortunately the image is not being saved. Furthermore, I get no exception. Hope you see my mistake.
Thx a lot
I found another solution, based on this post: How can I save the picture on image control in wpf?
So my solution, that works for me, is:
String filePath = #"C:\Users\xxx\Desktop\test.jpg";
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)qrImg.Source));
using (FileStream stream = new FileStream(filePath, FileMode.Create))
encoder.Save(stream);
I cannot export MS Chart (from WPF toolkit) to PNG. I following step from different forums, but after everything, my PNG is completely black. What am I doing wrong?
private void export_graf_Click(object sender, RoutedEventArgs e)
{
if (mcChart.Series[0] == null)
{
MessageBox.Show("there is nothing to export");
}
else
{
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)mcChart.ActualWidth, (int)mcChart.ActualHeight, 95d, 95d, PixelFormats.Pbgra32);
renderBitmap.Render(mcChart);
Microsoft.Win32.SaveFileDialog uloz_obr = new Microsoft.Win32.SaveFileDialog();
uloz_obr.FileName = "Graf";
uloz_obr.DefaultExt = "png";
Nullable<bool> result = uloz_obr.ShowDialog();
if (result == true)
{
string obr_cesta = uloz_obr.FileName; //cesta k souboru
using (FileStream outStream = new FileStream(obr_cesta, FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
encoder.Save(outStream);
}
}
I think you are encountering a layout issue. The RenderTargetBitmap class works on the visual layer, which includes offsets and transforms inherited from its visual parents. You should isolate the visual element when rendering it to a BitmapFrame. You can also specify a background color without affecting your window's visual tree, unless you want a transparent background. The PNG format supports alpha transparency and some image viewers display transparent pixels as black.
The default dpi for WPF is 96. I'm not sure why you specified 95. This isn't a zero bound index or anything like that. The sample below uses 96dpi.
private void export_graf_Click(object sender, RoutedEventArgs e)
{
if (mcChart.Series[0] == null)
{
MessageBox.Show("there is nothing to export");
}
else
{
Rect bounds = VisualTreeHelper.GetDescendantBounds(mcChart);
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)bounds.Width, (int)bounds.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual isolatedVisual = new DrawingVisual();
using (DrawingContext drawing = isolatedVisual.RenderOpen())
{
drawing.DrawRectangle(Brushes.White, null, new Rect(new Point(), bounds.Size)); // Optional Background
drawing.DrawRectangle(new VisualBrush(mcChart), null, new Rect(new Point(), bounds.Size));
}
renderBitmap.Render(isolatedVisual);
Microsoft.Win32.SaveFileDialog uloz_obr = new Microsoft.Win32.SaveFileDialog();
uloz_obr.FileName = "Graf";
uloz_obr.DefaultExt = "png";
Nullable<bool> result = uloz_obr.ShowDialog();
if (result == true)
{
string obr_cesta = uloz_obr.FileName;
using (FileStream outStream = new FileStream(obr_cesta, FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
encoder.Save(outStream);
}
}
}
}