I have a Image (Frameworkelement) on my GUI.
There is a image in there. Now I'm performing a doubleclick at this image and I want, that
the Image saves itself and is going to be opened, with the default imageviewer.
My Code:
void image_MouseDown(object sender, MouseButtonEventArgs e)
{
//Wayaround, cause there is no DoubleClick Event on Image
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
{
SaveToPng(((Image)sender), "SavedPicture.png");
Process.Start("SavedPicture.png");
}
}
void SaveToPng(FrameworkElement visual, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
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);
}
}
Opening the picture works fine with Process.Start. The problem is the saving, well it saves the picture: SavedPicture.png but, Its just black, so theres no graphic.. Maybe someone could tell me, whats wrong in my code or knows a better way of saving a image in WPF.
It is necessary that the image is displayed before it is saved. So, if you want to use RenderTargetBitmap just set the Image.Source and load the Image before saving with SaveToPng (ActualWidth and ActualHeight must not be null).
Example:
If you have the Image inside a Panel:
<Grid x:Name="MyGrid">
<Image x:Name="MyImage"/>
</Grid>
I set Image.Source in my test class constructor, and only after the image was loaded i save it:
public MainWindow()
{
InitializeComponent();
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri("image.png", UriKind.RelativeOrAbsolute);
bmp.EndInit();
MyImage.Source = bmp;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
RenderTargetBitmap bmp = new RenderTargetBitmap((int)MyGrid.ActualWidth,
(int)MyGrid.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(MyImage);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (var stream = System.IO.File.Create("newimage.png"))
{ encoder.Save(stream); }
}
If you don't want to use Grid ActualWidth and ActualHeight just pass your with and height as arguments.
Depends on the type of the Image.Source, assuming that you have a BitmapSource as in the article it should be along those lines:
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)image.Source));
using (FileStream stream = new FileStream(filePath, FileMode.Create))
encoder.Save(stream);
by the way RenderTargetBitmap class is to Converts a Visual object into a bitmap. its recommended by the team
sample
http://msdn.microsoft.com/en-us/library/aa969819.aspx
Problem solved.
I used this: File.WriteAllBytes()
to save the image from binaryformat
Related
I'm doing an image viewer (in a screen saver so images change randomly) in WPF and i would like to rotate a picture and save it at the same time (so it's rotated and i dont need to bother anymore).
I'm using Doubleanimation to fade one picture to the next.
Here is the function to rotate the image :
private void RotateImage(string path, float rotationAngle)
{
BitmapImage CurrentImage = new BitmapImage();
TransformedBitmap newTransformedImage = new TransformedBitmap();
Image img = new Image();
CurrentImage.BeginInit();
CurrentImage.UriSource = new Uri(path);
CurrentImage.EndInit();
newTransformedImage.BeginInit();
newTransformedImage.Source = CurrentImage;
RotateTransform transform = new RotateTransform(90);
newTransformedImage.Transform = transform;
newTransformedImage.EndInit();
img.Source = newTransformedImage;
Savetojpg(img, path);
}
and here is the funtion to save:
private void Savetojpg(Image image, string filePath)
{
var encoder = new JpegBitmapEncoder();
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)image.ActualWidth, (int)image.ActualHeight, 96, 96, PixelFormats.Pbgra32);
Size visualSize = new Size(image.ActualWidth, image.ActualHeight);
image.Measure(visualSize);
image.Arrange(new Rect(visualSize));
bitmap.Render(image);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.QualityLevel = 100;
encoder.Frames.Add(frame);
using (var stream = File.Create(filePath))
{
encoder.Save(stream);
}
}
When i run this, i have a System.ArgumentOutOfRangeException at the first line of the Savetojpg function as it seems the image i'm sending to that function is empty.
Can anyone tell me what i am doing wrong ?
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'm using Xceed WPF Toolkit (Community Edition) DataGridControl, and I would like to create a bitmap from the control (either to put on the clipboard or save to a png).
I have tried using a RenderBitmapTarget, but it will only copy the control as it is rendered on the screen (my grid is bigger than the screen).
My RenderBitmapTarget code looks like this:
RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.ActualWidth, (int)control.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(control);
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream stream = new MemoryStream();
png.Save(stream);
Image image = Image.FromStream(stream);
I've tried specifying a larger size (both in the RenderTargetBitmap constructor, and specifying new width/height for the control, but both just yielded the same image on a larger canvas.
Any thoughts?
Ok, I figured it out....
The final key was to disable delayed loading on the DataGridControl.View...here is my final code:
XAML:
<xcdg:DataGridControl x:Name="CEGrid">
<xcdg:DataGridControl.View>
<xcdg:TableflowView IsDeferredLoadingEnabled="False"/>
</xcdg:DataGridControl.View>
</xcdg:DataGridControl>
C# code-behind:
double tempWidth = CEGrid.ActualWidth;
double tempHeight = CEGrid.ActualHeight;
CEGrid.Width = double.NaN;
CEGrid.Height = double.NaN;
CEGrid.UpdateLayout();
RenderTargetBitmap rtb = new RenderTargetBitmap((int)CEGrid.ActualWidth, (int)CEGrid.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(CEGrid);
PngBitmapEncoder pbe = new PngBitmapEncoder();
pbe.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream stream = new MemoryStream();
pbe.Save(stream);
System.Drawing.Bitmap image = (System.Drawing.Bitmap)System.Drawing.Image.FromStream(stream);
CEGrid.Width = tempWidth;
CEGrid.Height = tempHeight;
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);
}
}
}
}
I have posted several questions related to this problem I am having and I am starting to believe this cannot be done. Here is the back story.
I have an ASP.NET application from which I want to generate a .png image. This .png image needs to be constructed from either XAML or a WPF Visual Tree. Because of this, I must generate the .png image in an STA thread. Everything works fine until my XAML/WPF Visual Tree includes an Image (as in a System.Windows.Controls.Image). My .png file gets generated correctly except the Image element does not show the referenced picture. The referenced picture is located at a remote URL. No errors or exceptions are thrown.
How do I create a .png image from some XAML/WPF Visual Tree that includes a System.Windows.Controls.Image element? The resulting .png must include the picture referenced in the Image element. I have tried the following code in a variety of ways:
string address = "http://imgtops.sourceforge.net/bakeoff/o-png24.png";
WebClient webClient = new WebClient();
byte[] imageContent = webClient.DownloadData(address);
Image image = new Image();
using (MemoryStream memoryStream = new MemoryStream(imageContent))
{
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.StreamSource = memoryStream;
imageSource.EndInit();
image.Source = imageSource;
}
// Set the size
image.Height = 200;
image.Width = 300;
// Position the Image within a Canvas
image.SetValue(Canvas.TopProperty, 1.0);
image.SetValue(Canvas.LeftProperty, 1.0);
Canvas canvas = new Canvas();
canvas.Height = 200;
canvas.Width = 300;
canvas.Background = new SolidColorBrush(Colors.Purple);
canvas.Children.Add(image);
// Create the area
Size availableSize = new Size(300, 200);
frameworkElement.Measure(availableSize);
frameworkElement.Arrange(new Rect(availableSize));
// Convert the WPF representation to a PNG file
BitmapSource bitmap = RenderToBitmap(frameworkElement);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
// Generate the .png
FileStream fileStream = new FileStream(filename, FileMode.Create);
encoder.Save(fileStream);
public BitmapSource RenderToBitmap(FrameworkElement target)
{
int actualWidth = 300;
int actualHeight = 200;
Rect boundary = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(actualWidth, actualHeight, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
VisualBrush visualBrush = new VisualBrush(target);
context.DrawRectangle(visualBrush, null, new Rect(new Point(), boundary.Size));
}
renderBitmap.Render(drawingVisual);
return renderBitmap;
}
Thank you for your help.
You are rendering the output bitmap correctly, it is just the input bitmap you are screwwing up :).
BitmapImage requires access to the StreamSource property until it fires the DownloadCompleted event, but the 'using' block Dispose()s of the MemoryStream before it has a chance! You could simply unwrap the MemoryStream from the using block and let the GC handle it (if you do, I would recommend setting the BitmapImage.CacheOption to BitmapCacheOption.None, so it uses the stream directly, rather than a copy), but I would use the UriSource property and wait for the DownloadComplete event before rendering.