How achieve Image.Clone() in WPF? - wpf

I obtain an array of byte (byte[]) from db and render into a Image Control using the following method :
public Image BinaryImageFromByteConverter(byte[] valueImage)
{
Image img = new Image();
byte[] bytes = valueImage as byte[];
MemoryStream stream = new MemoryStream(bytes);
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.EndInit();
img.Source = image;
img.Height = 240;
img.Width = 240;
return img;
}
So now that is rendered, I want to "copy" the Image.Source from Image (Control) to another element, for example : Paragraph..
paragraph1.Inlines.Add(new InlineUIContainer(ImageOne));
but nothings appears, I try to create a new Image using ImageOne.Source but I just found this example with Uri(#"path"), I cant apply this method cause my BitmapImage comes from a byte[] type
Image img = new Image();
img.Source = new BitmapImage(new Uri(#"c:\icons\A.png"));
Helps with this issue please, thanks!

Just create a new Image element and set its source to the same BitmapImage:
byte[] imageInfo = File.ReadAllBytes("IMG_0726.JPG");
BitmapImage image;
using (MemoryStream imageStream = new MemoryStream(imageInfo))
{
image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = imageStream;
image.EndInit();
}
this.mainImage.Source = image;
this.secondaryImage.Source = image;
It also works if you just copy one source to the other:
this.mainImage.Source = image;
this.secondaryImage.Source = this.mainImage.Source;

Related

MemoryStream throws System.ObjectDisposedException

I want to resize images for iOS so that I don't have to do it manually all the time. This should be done using WPF but it throws a ObjectDisposedException.
What it does is the user selects a csproj file and then an image which will be resized in 3 sizes. After resizing the file should be safed to disk but it throws an exception.
retrieving the bytes of the original BitmapImage
byte[] data = original.GetByteArray();
The used method:
public static byte[] GetByteArray(this BitmapImage bmi)
{
byte[] data;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmi));
using (Sys.MemoryStream ms = new Sys.MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
the resizing:
private BitmapImage ResizeImage(byte[] imageData, double dheight, double dwidth)
{
int height = (int)dheight;
int width = (int)dwidth;
BitmapImage resizedImage = new BitmapImage();
using (Sys.MemoryStream ms = new Sys.MemoryStream(imageData))
{
resizedImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
resizedImage.CacheOption = BitmapCacheOption.OnLoad;
resizedImage.DecodePixelHeight = height;
resizedImage.DecodePixelWidth = width;
resizedImage.BeginInit(); // Needed only so we can call EndInit()
resizedImage.StreamSource = ms;
resizedImage.EndInit();// This does the actual loading and resizing
}
return resizedImage;
}
then the saving of the files:
public static void Save(this BitmapImage image, string filePath)
{
//PngBitmapEncoder encoder = new PngBitmapEncoder();
//encoder.Frames.Add(BitmapFrame.Create(image));
//using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
//{
// encoder.Save(fileStream);
//}
File.WriteAllBytes(filePath, image.GetByteArray());
}
how I used it:
var bmi512 = ResizeImage(data, 512, 512);
var bmi256 = ResizeImage(data, 256, 256);
var bmi128 = ResizeImage(data, 128, 128);
bmi512.Save(Sys.Path.Combine(imageFolderPath, String.Format("{0}512{1}", imageName, imageExt)));
bmi256.Save(Sys.Path.Combine(imageFolderPath, String.Format("{0}256{1}", imageName, imageExt)));
bmi128.Save(Sys.Path.Combine(imageFolderPath, String.Format("{0}128{1}", imageName, imageExt)));
it works for retrieving the byte[] of the original but when it try it for bmi512 I get the exception.
Thanks in advance
You have to call BeginInit before you set any of the BitmapImage's properties:
using (var ms = new MemoryStream(imageData))
{
resizedImage.BeginInit();
resizedImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
resizedImage.CacheOption = BitmapCacheOption.OnLoad;
resizedImage.DecodePixelHeight = height;
resizedImage.DecodePixelWidth = width;
resizedImage.StreamSource = ms;
resizedImage.EndInit();
}

Creating bitmap in memory using RenderTargetBitmap fails in production WCF Service

I am using WPF objects to generate an bitmap image in memory. The program that does this resides in a WCF web service. The image renders correctly when I run on locally on IISExpress, and on a test IIS 7 server. However, when running on a server used by QA, the image is not rendered correctly. More specifically, only the top 22px lines of a 250px height image are rendered. The settings on both the test server and the QA server are supposed to be identical (insert skeptical face here).
Question: What possible settings in IIS could be effecting this image rendering? Also, I'm thinking there could possibly be a threading issue since RenderTargetBitmap renders asynchronously, and I do get a partial image.
Here is the code I'm using:
private byte[] RenderGauge(ViewData viewData)
{
double resolution = 4 * ReSize;
double dpi = 96 * resolution;
var view = new Gauge();
var vm = new GuageViewModel(viewData);
view.Measure(new Size(350, 70));
view.Arrange(new Rect(new Size(350, 70)));
var bounds = VisualTreeHelper.GetDescendantBounds(view);
if (bounds != Rect.Empty)
{
height = (int)(Math.Floor(bounds.Height) + 1);
width = (int)(Math.Floor(bounds.Width) + 1);
size = new Size(width, height);
}
var bitmap = new RenderTargetBitmap((int)(width * resolution), (int)(height * resolution), dpi, dpi, PixelFormats.Pbgra32);
var visual = new DrawingVisual();
using (var context = visual.RenderOpen())
{
var brush = new VisualBrush(view);
context.DrawRectangle(brush, null, new Rect(new Point(), bounds.Size));
}
bitmap.Render(visual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
byte[] img;
using (var MS = new MemoryStream())
{
encoder.Save(MS);
img = MS.ToArray();
}
img = img == null ? new byte[0] : img;
return img;
}
So, I'm doing exactly the same thing and I had a number of issues rendering files. I've found that using a binding to a bitmap in the XAML helps. The code from my view model that returns the image source is:
public Uri ImageUri
{
get { return new Uri(ImagePath, UriKind.Absolute); }
}
public BitmapImage ImageSource
{
get
{
try
{
if (string.IsNullOrEmpty(ImagePath) || !File.Exists(ImagePath))
return null;
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = ImageUri;
image.EndInit();
return image;
}
catch (Exception e)
{
var logger = LogManager.GetLogger(typeof(ImageDetails));
ExceptionHelper.LogExceptionMessage(logger, e);
}
return null;
}
}
Then in the XAML I bind to the ImageSource property.
I think that most problems with RenderTargetBitmap are related to asynchronous bindings in the XAML becauses the render method is synchronous.

Error converting clipboard image

Using WPF 4.5
private Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (var outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
}
and then later:
if (Clipboard.ContainsImage())
{
var bitmapSouce = Clipboard.GetImage();
var bitmap = BitmapFromSource(bitmapSouce);
var tmp = Path.GetTempFileName();
bitmap.Save(tmp, ImageFormat.Png);
...
bitmap.Save() throws an ExternalException, "A generic error in GDI+"
Is it really so hard to save a clipboard image to disk?
It is not necessary to create a System.Drawing.Bitmap (which is WinForms) from a WPF BitmapSource just for saving it.
You could as well directly save to a FileStream:
private void SaveBitmap(BitmapSource bitmapSource, string fileName)
{
var enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var outStream = new FileStream(fileName, FileMode.Create))
{
enc.Save(outStream);
}
}
...
var bitmapSource = Clipboard.GetImage();
var tmp = Path.GetTempFileName();
SaveBitmap(bitmapSource, tmp);

How to set the Source of a Image in Wpf?

// Create the image element.
Image simpleImage = new Image();
simpleImage.Width = 100;
simpleImage.Margin = new Thickness(5);
// Create source.
BitmapImage bi = new BitmapImage();
How should I set the Source for simpleImage to bi?
// Create the image element.
Image simpleImage = new Image();
simpleImage.Width = 200;
simpleImage.Margin = new Thickness(5);
// Create source.
BitmapImage bi = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block.
bi.BeginInit();
bi.UriSource = new Uri(#"/Images/1.jpg",UriKind.RelativeOrAbsolute);
bi.EndInit();
// Set the image source.
simpleImage.Source = bi;
var uri = new Uri(#"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg");
var bi = new BitmapImage(uri);
simpleImage.Source = bi;

Make WPF Image load async

I would like to load Gravatar-Images and set them from code behind to a WPF Image-Control.
So the code looks like
imgGravatar.Source = GetGravatarImage(email);
Where GetGravatarImage looks like:
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri( GravatarImage.GetURL( "http://www.gravatar.com/avatar.php?gravatar_id=" + email) , UriKind.Absolute );
bi.EndInit();
return bi;
Unfortunately this locks the GUI when the network connection is slow. Is there a way to assign the image-source and let it load the image in the background without blocking the UI?
Thanks!
I suggest you to use a Binding on your imgGravatar from XAML. Set IsAsync=true on it and WPF will automatically utilize a thread from the thread pool to pull your image. You could encapsulate the resolving logic into an IValueConverter and simply bind the email as Source
in XAML:
<Window.Resouces>
<local:ImgConverter x:Key="imgConverter" />
</Window.Resource>
...
<Image x:Name="imgGravatar"
Source="{Binding Path=Email,
Converter={StaticResource imgConverter},
IsAsync=true}" />
in Code:
public class ImgConverter : IValueConverter
{
public override object Convert(object value, ...)
{
if (value != null)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(
GravatarImage.GetURL(
"http://www.gravatar.com/avatar.php?gravatar_id=" +
value.ToString()) , UriKind.Absolute
);
bi.EndInit();
return bi;
}
else
{
return null;
}
}
}
I can't see why your code would block the UI, as BitmapImage supports downloading image data in the background. That's why it has an IsDownloading property and a DownloadCompleted event.
Anyway, the following code shows a straightforward way to download and create the image entirely in a separate thread (from the ThreadPool). It uses a WebClient instance to download the whole image buffer, before creating a BitmapImage from that buffer. After the BitmapImage is created it calls Freeze to make it accessible from the UI thread. Finally it assigns the Image control's Source property in the UI thread by means of a Dispatcher.BeginInvoke call.
ThreadPool.QueueUserWorkItem(
o =>
{
var url = GravatarImage.GetURL(
"http://www.gravatar.com/avatar.php?gravatar_id=" + email);
var webClient = new WebClient();
var buffer = webClient.DownloadData(url);
var bitmapImage = new BitmapImage();
using (var stream = new MemoryStream(buffer))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
}
Dispatcher.BeginInvoke((Action)(() => image.Source = bitmapImage));
});
EDIT: today you would just use async methods:
var url = GravatarImage.GetURL(
"http://www.gravatar.com/avatar.php?gravatar_id=" + email);
var httpClient = new HttpClient();
var responseStream = await httpClient.GetStreamAsync(url);
var bitmapImage = new BitmapImage();
using (var memoryStream = new MemoryStream())
{
await responseStream.CopyToAsync(memoryStream);
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
bitmapImage.Freeze();
}
image.Source = bitmapImage;

Resources