This question already has answers here:
How do you convert a byte array to a hexadecimal string, and vice versa?
(53 answers)
Closed 7 years ago.
I have a string value in cs code from [Image] column of mytable in mydatabase that type is varbinary(Max), now want to set value to source of Image control.
I wrote this code, but not set image to source :
string strImage = "0xFFD8FFE000104A46494600010....."
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
In constructor :
var bytes = GetBytes(strImage);
var myImage = new MyImageModel {Content = bytes};
MyImg.DataContext = myImage;
In Xaml:
<Image x:Name="MyImg" Source="{Binding Path=Content}"/>
The above code does not work and is not an error.
You can use a converter and bind your byte string to the Source property of the image. (So Content is your byte string)
<converters:BytesToImageConverter x:Key="BytesToImageConverter" />
<Image x:Name="MyImg" Source="{Binding Path=Content, Converter={StaticResource BytesToImageConverter}}"/>
Converter code:
public class BytesToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
if (!(value is byte[]))
throw new ArgumentException("Only byte[] values are supported.");
if (((byte[])value).Length == 0)
return null;
// return new ImageSourceConverter().ConvertFrom(value);
BitmapImage image = new BitmapImage();
using (MemoryStream ms = new MemoryStream((byte[])value))
{
ms.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = ms;
image.EndInit();
}
image.Freeze();
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
For converting string code to Image File Use the conversion Like this
public BitmapImage Base64ToImage( byte[] byteArray)
{
byteArray; // your bytes
BitmapImage img = new BitmapImage();
using (MemoryStream stream = new MemoryStream(byteArray))
{
img.BeginInit();
img.StreamSource = stream;
img.CacheOption = BitmapCacheOption.OnLoad;
img.EndInit();
img.Freeze();
}
return img;
}
It will Give you a Image you can Directly Bind this Image to UI
Related
I have a view that contains an image control that is binded to this property:
private System.Drawing.Image _sigImage;
public System.Drawing.Image sigImage
{
get { return _sigImage; }
set { _sigImage = value; RaisePropertyChanged(); }
}
I am busy implementing a a signature pad using mvvm, and want the signature to display in the image control. However i cant get it to display.
The code for die signature pad is:
DynamicCapture dc = new DynamicCaptureClass();
DynamicCaptureResult res = dc.Capture(sigCtl, "Who", "Why", null, null);
if (res == DynamicCaptureResult.DynCaptOK)
{
sigObj = (SigObj)sigCtl.Signature;
sigObj.set_ExtraData("AdditionalData", "C# test: Additional data");
try
{
byte[] binaryData = sigObj.RenderBitmap("sign", 200, 150, "image/png", 0.5f, 0xff0000, 0xffffff, 10.0f, 10.0f, RBFlags.RenderOutputBinary | RBFlags.RenderColor32BPP) as byte[];
using (MemoryStream memStream = new MemoryStream(binaryData))
{
System.Drawing.Image newImage = System.Drawing.Image.FromStream(memStream);
sigImage = newImage;
// work with image here.
// You'll need to keep the MemoryStream open for
// as long as you want to work with your new image.
memStream.Dispose();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The image is stored as a bitmap in the variable newImage.
How can i bind that image to the image control of sigImage?
System.Drawing.Image is not an appropriate type for the Source property of an Image element. It is WinForms, not WPF.
Use System.Windows.Media.ImageSource instead
private ImageSource sigImage;
public ImageSource SigImage
{
get { return sigImage; }
set { sigImage = value; RaisePropertyChanged(); }
}
and assign a BitmapImage or BitmapFrame to the property, which is directly created from the MemoryStream. BitmapCacheOption.OnLoad has to be set in order to enable closing the stream immediately after decoding the bitmap.
var bitmapImage = new BitmapImage();
using (var memStream = new MemoryStream(binaryData))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memStream;
bitmapImage.EndInit();
}
bitmapImage.Freeze();
SigImage = bitmapImage;
The Binding would look like shown below, provided an instance of the class with the SigImage property is assigned to the DataContext of the view.
<Image Source="{Binding SigImage}"/>
Since WPF has built-in type conversion from string, Uri and byte[] to ImageSource, you may as well declare the source property as byte[]
private byte[] sigImage;
public byte[] SigImage
{
get { return sigImage; }
set { sigImage = value; RaisePropertyChanged(); }
}
and assign a value like
SigImage = binaryData;
without manually creating a BitmapImage or BitmapFrame, or changing the Binding.
I would like to save a VisualBrush to a file. I've tried many different solutions but nothing worked.
I've tried to write a simple Converter:
public class BrushToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Brush)
{
var brush = (Brush)value;
Size size = new Size(1000, 1000);
if (parameter is Size)
size = (Size)parameter;
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
if (brush is VisualBrush)
{
bitmap.Render(((VisualBrush)brush).Visual);
}
else
{
var drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
context.DrawRectangle(brush, null, new Rect(0, 0, bitmap.Width, bitmap.Height));
}
bitmap.Render(drawingVisual);
}
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(File.OpenWrite(ViewModel.MainViewModel.Random.Next().ToString() + ".png"));
return (ImageSource)bitmap;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
All in all the result is a black image. But if I change the source brush to something like Brushes.Red, the result is a red image. So I would guess that the rendering to the bitmap fails, but the saveing to a file works.
Does anyone has an idea what I am doing wrong?
I have a image control in WPF window and its xmls is as follows:
<Image Source="{Binding Path=ImageUrl,Converter={StaticResource ImageSourceConverter}}"
Height="150" HorizontalAlignment="Left" Margin="100,15,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="200" />
At run time I change the source of Image by changing the value of bounded property. This code works fine in normal scenario. Issue appears only after internet is disconnected. If there is disconnection while downloading the image then image is not shown. Thats fine. But when internet comes back and image source is changed to another image url, image doesn't get downloaded. ImageConverter is not even called. After this point there is no way to display image in the control. Image control gets stuck.
public class ImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string url;
try
{
if (value is string)
url = (string)value;
else
url = ((Uri)value).AbsolutePath;
}
catch (Exception)
{
url = string.Empty;
return value;
}
BitmapImage src = new BitmapImage();
if (targetType == typeof(ImageSource))
{
if (value is string)
{
string str = (string)value;
src.BeginInit();
src.CacheOption = BitmapCacheOption.OnLoad;
src.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
src.UriSource = new Uri(str, UriKind.RelativeOrAbsolute);
src.EndInit();
return src;
}
else if (value is Uri)
{
Uri uri = (Uri)value;
return new BitmapImage(uri);
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Any help will be highly appreciated.
In article about memory leaks discusses leak caused by use of downloaded BitmapImage as Image Source[7]:
Quote: "This leak is triggered because WPF does not remove internal reference to certain objects (such as LateBoundBitmapDecoder, BitmapFrameDecode, etc) which are used during web download and causes the leak.
This leak only happens when you download an image from the internet. (E.g. it does not appear when you load images from your local machine)"
...
The Fix/Workaround
The workaround is to consider downloading the BitmapImage first in other means to a temporary folder or to memory and then use the local BitmapImage . (See WebClient.DownloadFile & WebClient.DownloadData APIs)
Maybe it you will approach.
I fixed issue by downloading the image first and if it succeeds then only assign it to Image control. Posting it here in case anyone needs it.
private bool ValidImage(string url, out BitmapImage image)
{
try
{
System.Net.WebRequest request = System.Net.WebRequest.Create(url);
System.Net.WebResponse response = request.GetResponse();
System.IO.Stream responseStream = response.GetResponseStream();
Bitmap bitmap = new Bitmap();
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
BitmapImage result = new BitmapImage();
result.BeginInit();
// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
// Force the bitmap to load right now so we can dispose the stream.
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
image = result;
}
}
catch (Exception ex)
{
logger.Error(ex, "error downlading image");
image = null;
return false;
}
return true;
}
i get image from client convert it to byte[] and send it to server. And convert byte[] to Base64String and insert into database.
And i do reverse to show image. But i cant see the image. Why???
//Convert to byte array
public static byte[] ImageToByteArray(WriteableBitmap bitmap)
{
int[] p = bitmap.Pixels;
int len = p.Length << 2;
byte[] result = new byte[len];
Buffer.BlockCopy(p, 0, result, 0, len);
return result;
}
//Converter
public object Convert(object value, Type targetType, object parameter,System.Globalization.CultureInfo culture)
{
if (value == null)
{
return null;
}
BitmapImage image = new BitmapImage();
MemoryStream stream = new MemoryStream();
stream.Write((byte[])value, 0, ((byte[])value).Length);
image.SetSource(stream);
return image;
}
//While writing to database
else if (value.GetType() == typeof(byte[]))
{
return "'" + Convert.ToBase64String((byte[])value) + "'";
}
else if ((type == typeof(byte[])))
{
return Convert.FromBase64String((string)value);
}
I have the following code to convert a byte array directly to an image:
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(new MemoryStream(imageData));
newImage.Source = bitmapImage;
So as long as the conversion to and from the Base64String is working this should be all you need.
As an aside, you don't need to convert to a Base64String to store in the database. You just need to set the column type to image (assuming you are using MS SQL Server)
public class Base64StringToImageConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (!(value is string)) return DependencyProperty.UnsetValue;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(new MemoryStream(System.Convert.FromBase64String((string)value)));
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
// TODO: Implement this method
throw new NotImplementedException();
}
}
I used BinaryReader and solved the problem.
My problem was not reading the image correct. iused BinaryReader to read the image and solved the problem.
BinaryReader reader = new BinaryReader(fileInfo.OpenRead());
byte[] tempImage = new byte[reader.BaseStream.Length];
reader.Read(tempImage, 0, tempImage.Length);
I have problem with converter from Uri to BitmapImage. Uri is url of image on web. I use this converter on item in listbox.
I download image from webpage and create from this stream BitampImage
Problem is if listbox consist about 100 - 250 items, app freeze, I try call WebRequestMethod in another thread but it don’t work.
Here is root part of code:
private static BitmapImage GetImgFromAzet(int sex, Uri imgUri)
{
try
{
if (imgUri == null)
{
if (sex == (int)Sex.Man)
{
return new BitmapImage(new Uri(#"pack://application:,,,/Spirit;Component/images/DefaultAvatars/man.jpg",
UriKind.RelativeOrAbsolute));
}
else
{
return new BitmapImage(new Uri(#"pack://application:,,,/Spirit;Component/images/DefaultAvatars/woman.jpg",
UriKind.RelativeOrAbsolute));
}
}
else
{
BitmapImage image = null;
Task.Factory.StartNew(() =>
{
WebRequest webRequest = WebRequest.CreateDefault(imgUri);
webRequest.ContentType = "image/jpeg";
WebResponse webResponse = webRequest.GetResponse();
image = new BitmapImage();
image.CreateOptions = BitmapCreateOptions.None;
image.CacheOption = BitmapCacheOption.OnLoad;
image.BeginInit();
image.StreamSource = webResponse.GetResponseStream();
image.EndInit();
return image;
//((System.Action)(() =>
//{
// //webResponse.Close();
//})).OnUIThread();
});
return image;
}
}
catch (Exception)
{
//default
return
new BitmapImage(new Uri(PokecUrl.Avatar,UriKind.RelativeOrAbsolute));
}
}
My aim is download image from web, create BitamImage object from him and return as Source of Image control, but I need avoid app freezing. Also problem is if I close webResponse it broke all code.
EDITED:
I try this:
BitmapImage image;
WebRequest req = WebRequest.CreateDefault(imgUri);
req.ContentType = "image/jpeg";
using (var res = req.GetResponse())
{
image = new BitmapImage();
image.CreateOptions = BitmapCreateOptions.None;
image.CacheOption = BitmapCacheOption.OnLoad;
image.BeginInit();
image.UriSource = imgUri;
image.StreamSource = res.GetResponseStream();
image.EndInit();
}
but somewhere must be bug, code is broken.
Any advice?
Binding converter is always executed on UI thread. So you could start other thread in Convert method but eventually (as you need feedback from this thread) you have to wait until it completes, thereby you're blocking your app.
In order to solve this problem, for example, you could use Binding.IsAsync property:
public class ListItemViewData
{
private readonly Uri _uri;
private readonly Sex _sex;
ListItemViewData(Uri uri, Sex sex)
{
this._uri = uri;
this._sex = sex;
}
public BitmapSource Image
{
get
{
// Do synchronous WebRequest
}
}
}
Usage in xaml (inside DataTemplate of listbox item):
<Image Source="{Binding Path=Image, IsAsync=True}"/>
EDITED
I've dived into BitmapImage class and have found out that it has pretty ctor with Uri parameter, that works asynchronously.
So you shouldn't execute WebRequest by yourself. Do just like this:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var uri = (Uri)value;
return new BitmapImage(uri) { CacheOption = BitmapCacheOption.None };
}
EDITED 2
Your view data class.
public class ListItemViewData : INotifyPropertyChanged
{
public ListItemViewData(Uri uri)
{
this._uri = uri;
}
private readonly Uri _uri;
public Uri Uri
{
get
{
return this._uri;
}
}
private BitmapSource _source = null;
public BitmapSource Image
{
get
{
return this._source;
}
set
{
this._source = value;
this.OnPropertyChanged("Image");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string p)
{
var pc = this.PropertyChanged;
if (pc!=null)
{
pc(this, new PropertyChangedEventArgs(p));
}
}
}
Helper, that executes images downloading:
public static class WebHelper
{
public static Stream DownloadImage(Uri uri, string savePath)
{
var request = WebRequest.Create(uri);
var response = request.GetResponse();
using (var stream = response.GetResponseStream())
{
Byte[] buffer = new Byte[response.ContentLength];
int offset = 0, actuallyRead = 0;
do
{
actuallyRead = stream.Read(buffer, offset, buffer.Length - offset);
offset += actuallyRead;
}
while (actuallyRead > 0);
File.WriteAllBytes(savePath, buffer);
return new MemoryStream(buffer);
}
}
}
When you are filling model - you should start separate thread, which will download files and set up images source.
this._listItems.Add(new ListItemViewData(new Uri(#"http://lifeboat.com/images/blue.ocean.jpg")));
//...
var sc = SynchronizationContext.Current;
new Thread(() =>
{
foreach (var item in this._listItems)
{
var path = "c:\\folder\\"+item.Uri.Segments.Last();
var stream = WebHelper.DownloadImage(item.Uri, path);
sc.Send(p =>
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = (Stream)p;
bi.EndInit();
item.Image = bi;
}, stream);
}
}).Start();