I have an icon on resources that it key is: xxx
I want to bind it to an image in xaml..
1:
<Image Source="{x:Static p:Resources.xxx}"></Image>
2:
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding x:Static p:Resources.xxx}"/>
</Image.Source>
</Image>
3:
<Image Source=" {Binding x:Static p:Resources.xxx,Converter={StaticResource IconToBitmap_Converter}}"></Image>
4:
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding x:Static p:Resources.xxx,Converter={StaticResource IconToBitmap_Converter}}"/>
</Image.Source>
</Image>
The above ways does not work, how am I supposed to do that?
First you must add your Image into a Resource File in the Solution Explorer. Next you must set the Build Action of your Image to Resource and then you can use it in the XAML Code like this:
<UserControl>
<UserControl.Resources>
<ResourceDictionary>
<BitmapImage x:Key="name" UriSource="Resources/yourimage.bmp" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Image Source="{StaticResource name}"/>
</Grid>
</UserControl>
First:
Add resources rsx
then:
add images as images to the resource file and set the image build action to Resource.
Now you can access the images like this:
<Image Source="pack://application:,,,/Resources/image.png"/>
You may implement your own Markup extension and use it as follows:
<Image Source="{ex:ImageSource {x:Static p:Resources.xxx}}"></Image>
The MarkupExtension ImageSource may look like this:
public class ImageSource : MarkupExtension
{
private readonly object source;
public ImageSource(object source) => this.source = source;
public override object ProvideValue(IServiceProvider serviceProvider)
{
Binding sourceBinding = source is Binding binding ? binding : new Binding() { Source = source };
MultiBinding converterBinding = new MultiBinding() { Converter = new SourceConverter() };
converterBinding.Bindings.Add(sourceBinding);
return converterBinding.ProvideValue(serviceProvider);
}
private class SourceConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture) => BitmapToSource(value[0] as Bitmap);
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
private BitmapImage BitmapToSource(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
memory.Position = 0;
BitmapImage bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
return bitmapimage;
}
}
}
}
Related
I intend to create Usercontrol with boolean dependency property called IsPowerOn, When I change it True the PowerOn image load to Image.source and when I set IsPowerOn to Fals, the PowerOff image load to Image.source.
Here is my UserControl:
<UserControl x:Class="...UcPower"
...
<UserControl.Resources>
<local:PowerBoolean2Image x:Key="PowerBoolean2Image"/>
</UserControl.Resources>
<Grid>
<Image x:Name="imgPower" Source="{Binding Source, Converter={StaticResource PowerBoolean2Image}, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UcPower}}}" />
</Grid>
And Code behind:
public static readonly DependencyProperty IsPowerOnProperty = DependencyProperty.Register("IsPowerOn", typeof(bool), typeof(UcPower),
new FrameworkPropertyMetadata(false) { BindsTwoWayByDefault = true });
public bool IsPowerOn
{
get
{
return (bool)GetValue(IsPowerOnProperty);
}
set
{
SetValue(IsPowerOnProperty, value);
}
}
And IValueConverter:
public class PowerBoolean2Image : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool))
{
return null;
}
if (value.Equals(true))
{
// Power On
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"));
}
else
{
// Power Off
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But it doesn't work I expect, whats the wrong with me?
You should bind to the IsPowerOn property:
<Image Source="{Binding IsPowerOn, ...}" />
instead of
<Image Source="{Binding Source, ...}" />
Besides that, the expression if (value.Equals(true)) looks rather strange. You could replace that by
if ((bool)value)
{
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"));
}
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
or shorter:
return (bool)value
? new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"))
: new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
When I use this code in IValueConverter I get Error: IOException: Cannot locate resource 'resources/power-on.png'. and cant see my form in design mode:
Uri("pack://application:,,,/Resources/Power-On.png")
But I can use Assembly name to solve the problem like this code:
Uri("pack://application:,,,/ReferencedAssembly;component/Resources/Power-On.png")
Wpf Canvas Background image does not display selected image from local path
XAML Code
<Canvas x:Name="LayoutRoot" Margin="485,24,0,0" HorizontalAlignment="Left" Width="341" Height="506" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush ImageSource="{Binding BGImage}"/>
</Canvas.Background>
</Canvas>
MVVM code
private String _BGImage = #"C:/Users/sam/Desktop/photo-5.jpg";
public String BGImage
{
get
{
return this._BGImage;
}
set
{
this._BGImage = value;
NotifyPropertyChanged("BGImage");
}
}
Why this image not display on canvas background
or you can try using a converter
<UserControl.Resources>
<local:StringToImageConverter x:Key="StringToImageConverter" />
</UserControl.Resources>
...
<Canvas x:Name="LayoutRoot" Margin="485,24,0,0" HorizontalAlignment="Left" Width="341" Height="506" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush ImageSource="{Binding Path=BGImage, Converter={StaticResource StringToImageConverter}}"/>
</Canvas.Background>
</Canvas>
and this is the converter
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(string))
{
throw new InvalidOperationException("The value must be a string");
}
return new BitmapImage(new Uri((string)value));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
of course you would still need to check if the string is a valid URI
Your viewmodel code for BGImage should look something like this:
private ImageSource _BGImage = new BitmapImage(new Uri(#"C:\Users\sam\Desktop\photo-5.jpg", UriKind.Absolute))
public ImageSource BGImage
{
get { return _BGImage; }
set
{
_BGImage= value;
NotifyPropertyChanged("BGImage");
}
}
Well you need to have BGImage as BitmapImage rather than string
public BitmapImage BGImage
{
get
{
return new BitmapImage((new Uri(this._BGImage, UriKind.Absolute)));
}
}
If you are changing image dynamically then you have to raise property changed to notify UI
I have an ItemsControl control. In its items I show a lot of things: two images in each of the items, some textblocks, etc.
One of the images that are shown in each of the items is the same for all of the items. It's in the Resources.resx file in my project and here's how I load it:
<Image Width="60" Height="60" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="2" Grid.RowSpan="2">
<Image.Source>
<Binding Source="{x:Static properties:Resources.myImageName}" Converter="{StaticResource BitmapToImageConverter}" />
</Image.Source>
</Image>
The Convert method of my BitmapToImageConverter converter class looks like this:
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
using (MemoryStream stream = new MemoryStream())
{
((System.Drawing.Bitmap)value).Save(stream, ImageFormat.Png);
stream.Position = 0;
BitmapImage resultImage = new BitmapImage();
resultImage.BeginInit();
resultImage.CacheOption = BitmapCacheOption.OnLoad;
resultImage.StreamSource = stream;
resultImage.EndInit();
resultImage.Freeze();
return resultImage;
}
}
Now, the binding takes a lot of time and I want to shorten it somehow. The Convert method gets called once for every item in my itemscontrol, but with the same image (the same parameters). How can I call it just once for all of the items?
The Converter extends the IValueConverter class.
If I havent misunderstood your question Try this
public class ImageConverter : ValueConverter
{
static BitmapImage resultImage = null;
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (resultImage != null)
return resultImage;
using (MemoryStream stream = new MemoryStream())
{
((System.Drawing.Bitmap)value).Save(stream, ImageFormat.Png);
stream.Position = 0;
resultImage = new BitmapImage();
resultImage.BeginInit();
resultImage.CacheOption = BitmapCacheOption.OnLoad;
resultImage.StreamSource = stream;
resultImage.EndInit();
resultImage.Freeze();
return resultImage;
}
}
Create a static variable and assign it the value first time and return it for next times
I am displaying some image in my wpf app using following code:
<Image Source="{Binding Path=TemplateImagePath, Mode=TwoWay}" Grid.Row="3" Grid.Column="2" Width="400" Height="200"/>
and setting it's binding property inside code behind's constructor by navigating through some directory, below is the code:
DirectoryInfo Dir = new DirectoryInfo(#"D:/Template");
if (Dir.Exists)
{
if (Dir.GetFiles().Count() > 0)
{
foreach (FileInfo item in Dir.GetFiles())
{
TemplateImagePath = item.FullName;
}
}
}
but if user upload some other image then I need to delete this old image which is I am doing in the following way and setting image binding to null:
DirectoryInfo Dir = new DirectoryInfo(#"D:/Template");
if (Dir.Exists)
{
if (Dir.GetFiles().Count() > 0)
{
foreach (FileInfo item in Dir.GetFiles())
{
TemplateImagePath= null;
File.Delete(item.FullName);
}
}
}
But Iam getting exception that Cannot delete file used by some other process.
How can I delete it?
In order to be able to delete the image while it is displayed in an ImageControl, you have to create a new BitmapImage or BitmapFrame object that has BitmapCacheOption.OnLoad set. The bitmap will then be loaded from file immediately and the file is not locked afterwards.
Change your property from string TemplateImagePath to ImageSource TemplateImage and bind like this:
<Image Source="{Binding TemplateImage}"/>
The set the TemplateImage property like this:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(item.FullName);
image.EndInit();
TemplateImage = image;
or this:
TemplateImage = BitmapFrame.Create(
new Uri(item.FullName),
BitmapCreateOptions.None,
BitmapCacheOption.OnLoad);
If you want to keep binding to your TemplateImagePath property you may instead use a binding converter that converts the string to an ImageSource as shown above.
According to Clemens suggestion, here is the binding converter to have a good code-reuse:
namespace Controls
{
[ValueConversion(typeof(String), typeof(ImageSource))]
public class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is string valueString))
{
return null;
}
try
{
ImageSource image = BitmapFrame.Create(new Uri(valueString), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
return image;
}
catch { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And there is a string for binding, for example
public string MyImageString { get; set; } = #"C:\test.jpg"
And in the UI the converter is used, in my case from the Library named "Controls"
<Window x:Class="MainFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Controls;assembly=Controls">
<Window.Resources>
<controls:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />
</Window.Resources>
<Grid>
<Image Source="{Binding MyImageString, Converter={StaticResource StringToImageSourceConverter}}" />
</Grid>
</Window>
I have a collection
private ObservableCollection<ImageData> imageDataList = new ObservableCollection<ImageData>(); where ImageData is a custom object. It has an attribute called fileName a string that stores full path of an image file. In my XAML code, I have a listbox with datatemplate as the following.
<ListBox Name="listBox_ImageList" Grid.ColumnSpan="3" Grid.Row="2" SelectionChanged="listBox_ImageList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding fileName}" Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox, AncestorLevel=1}, Path=ActualHeight}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After populating ImagaData objects into imageDataList, I set this.listBox_ImageList.ItemsSource = imageDataList;
However, when I run it, I don't see any images. Can you please tell me how to databind properly to a string member of an object to WPF image source?
Checkout this http://social.msdn.microsoft.com/Forums/en-AU/wpf/thread/f94cc770-8d86-4a9f-a5f9-2ee2ea146c1a
Set the DataContext to where the object where the ObservableCollection is located
DateContext = this;
Also instead of fileName bind it to a ImageSource Property or a BitmapImage Property and this is created using the fileName.
To answer your question: You cannot bind the ImageSource property to a string. It works in XAML because WPF uses a default converter from string to ImageSource when you set the value in XAML. If you want to set the value with a binding or from code you need to provide an ImageSource object.
There are 2 ways to do it via binding:
The first one is presented here (the link Juan Carlos mentioned), and it involves creating a IValueConverter that will take your string and transform it to a ImageSource. I would modify the converter code presented there with this:
public sealed class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return DependencyProperty.UnsetValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The second option is to create your ImageSource in your ImageData class and bind directly to it.
private ImageSource _imageSource
public ImageSource ImageSource
{
get
{
if (_imageSource == null)
{
_imageSource = new BitmapImage(new Uri(fileName), UriKind.RelativeOrAbsolute);
}
return _imageSource;
}
}