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?
Related
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
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 a Datagrid called previewTable.
I have a collection of collections of strings which represents the table data:
ObservableCollection<ObservableCollection<string>> tableData
I also have a collection of strings which represent columns headers:
ObservableCollection<string> columnsHeaders
I need to create columns using the columns headers collection, I achieve this here:
foreach (string columnName in columnsHeaders)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = columnName;
previewTable.Columns.Add(column);
}
Now I need to bind the table to the data.
The problem is:
previewTable.ItemsSource = table
Doesn't work.
I always have 9 column headers and each collection size in the data is 9.
Help would be appreciated
Some additional classes:
public class EntityDataRow
{
public List<string> Items { get; set; }
}
public class RowItemsConverter : IValueConverter //converter for binging
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
var list = (List<string>) value;
var index = (Int32) parameter;
return list[index];
}
catch(Exception)
{
return String.Empty;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And this is your code (data is a list of EntityRow)
var headers = data[0].Items;
data.RemoveAt(0);
PreviewGrid.Columns.Clear();
PreviewGrid.ItemsSource = data;
for (var i = 0; i < headers.Count; i++)
{
var column = new DataGridTextColumn { Header = headers[i] };
var binding = new Binding("Items");
var converter = new RowItemsConverter();
binding.Converter = converter;
binding.ConverterParameter = i;
column.Binding = binding;
PreviewGrid.Columns.Add(column);
}
You can use indexers in bindings so the following ought to work:-
int i = 0;
foreach (string columnName in columnsHeaders)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = columnName;
column.Binding = new Binding("[" + i.ToString(); + "]");
previewTable.Columns.Add(column);
i += 1;
}
I have an enum listing all possible settings:
public enum Settings
{
Settings1,
Settings2,
Settings3
}
In my user control i want to implement a new depedency property that holds a list of settings and be able to use it like this:
<my:Control Settings="Settings1, Settings2" />
How should i implement this?
In your UserControl, make your Dependency property a collection of Settings (perhaps rename your enum to Setting), and then you can populate it in XAML with:
<my:Control>
<my:Control.Settings>
<x:Static Member="my:Setting.Setting1" />
<x:Static Member="my:Setting.Setting2" />
</my:Control.Settings>
</my:Control>
I haven't tested this :)
If you want to stick with a comma separated list, then make your UserControl Settings DP a string, and then on the property changed event handler, split the string and use Enum.Parse on each result to store the settings as your Setting enum type.
public class StringDelimitedToEnumListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<Settings> retval = new List<Settings>();
if(value == null || value.GetType() != typeof(string) || targetType != typeof(List<Settings>) )
{
throw new ArgumentException("value must be of type string, target type must be of type List<Settings>");
}
string workingValue = value.ToString();
if (String.IsNullOrEmpty(workingValue))
{
throw new ArgumentException("value must not be an empty string");
}
if (workingValue.Contains(','))
{
string[] tokens = workingValue.Split(',');
foreach (string s in tokens)
{
retval.Add((Settings)Enum.Parse(typeof(Settings), s));
}
return retval;
}
else
{
//there was only 1 value
retval.Add((Settings)Enum.Parse(typeof(Settings),workingValue);
return retval;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//do we care?
throw new NotImplementedException();
}
}