Binding image source through property in wpf - wpf

I am trying to display an icon using an image source(.jpg). I create a Icon property in view model and try to assign it the path of the image but I do not see any image in the view. I tried converting the path to Bitmap image but doesn't work. Is there anything I am missing here?
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}"/>
<Image Source="{Binding Path=Icon}"></Image>
</StackPanel>
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
img.UriSource = new Uri("C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg", UriKind.Absolute);
img.EndInit();
Icon = img;

I ran into this myself once and, though maybe not the best solution, the following worked for me.
1. Add the images to your project, for example:
Create a folder images/icons to your project and add the images there.
Set build action of images to Content (copy if newer)
2. Create an ImageSource property:
public ImageSource YourImage
{
get { return _yourImage; }
set
{
_yourImage = value;
NotifyOfPropertyChange(() => YourImage);
}
}
(Note: I use caliburn micro to assist in binding)
3. Update the the ImageSource like this:
if(!string.IsNullOrEmpty("TheImageYouWantToShow"))
{
var yourImage = new BitmapImage(new Uri(String.Format("Images/Icons/{0}.jpg", TheImageYouWantToShow), UriKind.Relative));
yourImage.Freeze(); // -> to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
YourImage = yourImage;
}
else
{
YourImage = null;
}
4. Bind source attribute to YourImage property:
(you already did this)

Related

Why leaps exception: "The calling thread must be STA, because many UI components require this"?

Why leaps exception in the load method? If it is to make simultaneous , everything works well .
Tell me, please , what is the problem and how to fix it ?
public class MainViewModel : ViewModelBase
{
private readonly List<string> _listImageUri;
private int _currentIndex;
private ImageSource _currentImage;
public ObservableCollection<Image> CustomControls { get; set; } = new ObservableCollection<Image>();
public MainViewModel()
{
_listImageUri = new List<string>();
Load("путь к папке");
}
private async void Load(string s)
{
await Task.Run(() =>
{
foreach (var fileInfo in new DirectoryInfo(s).GetFiles())
{
_listImageUri.Add(fileInfo.FullName);
Image img = new Image(); // exception
img.Height = 100;
img.Margin = new Thickness(5);
img.Width = 100;
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri(fileInfo.FullName);
bmp.DecodePixelWidth = 100;
bmp.EndInit();
img.Source = bmp;
CustomControls.Add(img);
OnPropertyChanged(nameof(CustomControls));
}
});
}
}
Exception
An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Additional information: The calling thread must be STA, because many UI components require this.
Do not create Image controls in your view model, especially not when you load images asynchronously.
Change your view model to have an ObservableCollection of ImageSource:
public ObservableCollection<ImageSource> Images { get; set; }
= new ObservableCollection<ImageSource>();
Then in your load method make sure that adding images to the collection is done in the UI thread. To make the BitmapImages cross-thread accessible, you also have to freeze them:
var bitmap = new BitmapImage(new Uri(fileInfo.FullName));
bitmap.Freeze();
Application.Current.Dispatcher.BeginInvoke(new Action(() => Images.Add(bitmap)));
Finally display the images by means of an ItemsControl with an appropriate ItemTemplate:
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That said, you may also replace the ObservableCollection<ImageSource> by an ObservableCollection<string>, which would hold the image file paths. This is possible because WPF provides built-in conversion from string (and Uri) to ImageSource. Consequently, you would then also not need any asynchronous task anymore.
You run your code in a thread. But updating UI components need to run on the WPF UI thread.
So invoke the critical line(s) using Dispatcher.Invoke so that your code is using the UI-Thread

Dynamically Added Image to WrapPanel in WPF

Added Image Controls to WPF WrapPanel from a list of images defined in xml.
Everything seems to be in place. I even inspected in debug but nothing is visual.
Is there a step I am missing?
_printImages.ReadXml(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Images.xml"));
if (_printImages.Tables.Contains("image") && _printImages.Tables["image"].Rows.Count > 0)
{
foreach (DataRow row in _printImages.Tables["image"].Rows)
{
// build info object
ImageInfo imgInfo = new ImageInfo();
imgInfo.Source = row["Source"].ToString();
imgInfo.Custom = bool.Parse(row["Custom"].ToString());
imgInfo.Font = row["Font"].ToString();
imgInfo.FontSize = int.Parse(row["FontSize"].ToString());
imgInfo.CharacterLimit = int.Parse(row["Characterlimit"].ToString());
imgInfo.CustomType = row["Customtype"].ToString();
_images.Add(imgInfo);
//create control
Image imgControl = new Image();
BitmapImage imgFile = new BitmapImage();
try
{
imgFile.BeginInit();
imgFile.StreamSource = new FileStream(imgInfo.Source, FileMode.Open);
imgControl.Source = imgFile;
imgControl.Tag = _images.Count - 1;
imgControl.Height = Properties.Settings.Default.ImageHeight;
imgControl.Width = Properties.Settings.Default.ImageWidth;
imgControl.MouseDown += new MouseButtonEventHandler(image_MouseDown);
imgControl.Visibility = System.Windows.Visibility.Visible;
imageSelectionPanel.Children.Add(imgControl);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message.ToString(), "Unable to create image");
}
}
}
Your code is missing an EndInit call after setting the StreamSource property of the BitmapImage.
Moreover, the stream should be closed after loading the bitmap, which is usually done by a using block and which also requires to set BitmapCacheOption.OnLoad:
using (var stream = new FileStream(imgInfo.Source, FileMode.Open))
{
imgFile.BeginInit();
imgFile.StreamSource = stream;
imgFile.CacheOption = BitmapCacheOption.OnLoad;
imgFile.EndInit();
}
Alternatively, the BitmapImages could also be loaded directly from the image file paths without using a FileStream:
var imgFile = new BitmapImage(new Uri(imgInfo.Source, UriKind.RelativeOrAbsolute));
You might also create a view model with a collection of ImageInfo objects and bind an ItemsControl to this collection. The ItemsControl would have the WrapPanel as its ItemsPanel, and an ItemTemplate with the Image control:
<ItemsControl ItemsSource="{Binding ImageInfos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Source}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
See the Data Templating Overview article on MSDN for details.

Convert a Grid element to Image in WPF

I have a code snippet which is currently returned as Grid.
private Grid GetImage(PlacemarkList locationDetail)
{
Grid gridPushPin = new Grid();
ImageBrush img = new ImageBrush();
img.ImageSource = locationDetail.preferredCashback.Equals("1") ? new BitmapImage {
UriSource = Constants.CashbackIconUri,
DecodePixelWidth = 36,
DecodePixelHeight = 59
} : new BitmapImage {
UriSource = Constants.ATMIconUri,
DecodePixelWidth = 36, DecodePixelHeight = 59
};
TextBlock IndexText = new TextBlock();
IndexText.TextAlignment = TextAlignment.Center;
IndexText.Text = locationDetail.IndexNum.ToString();
gridPushPin.Background = img;
gridPushPin.Tag = locationDetail.bankAddress;
gridPushPin.Tap += grid_Tap;
return gridPushPin;
}
But I want to return the Grid as a Image(Convert the Grid I am generating to Image). Can anybody please help how to accomplish that.
You can use a VisualBrush to paint a copy of any UIElement onto any other. How about something like this:
<Rectangle Width="150" Height="150">
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=NameOfYourGrid}" />
</Rectangle.Fill>
</Rectangle>
Hey did you check this subject. I think what you need is here.
How do I save all content of a WPF ScrollViewer as an image
public class MyWrapperClass
{
private DataGrid dataGrid;
public MyWrapperClass(DataGrid grid)
{
this.dataGrid = grid;
}
public DataGrid MyGrid
{
get
{
return this.grid;
}
set
{
this.grid = value;
}
}
public ImageBrush MyImageBrush
{
get
{
this.grid.Background as ImageBrush;
}
set
{
this.grid.Background = value;
}
}
}
You are creating a Grid, so you will return a Grid.
Create an image instead. Something like:
Image image = new Image();
image.Source = "myImageURL";
This is from memory so actual code may vary depending on what you need to do exactly.
Basically the Source of the image needs to be set similarly to how you're setting ImageSource in your current code, but you may opt to use pack URLs to use an image stored as a resource instead.
Edit after clarifications in comments: So you want to get the grid control as an image. This answer should help.

Displaying transparent image in a WPF DataGrid

I have been tasked with taking an existing list of transparent .png images (currently housed within an ImageList) and displaying them in a WPF DataGrid based on the ImageID column.
I have set up the DataGridColumn as follows:
_dataTemplateColumn = new DataGridTemplateColumn();
_dataTemplateColumn.Header = "";
FrameworkElementFactory _factory = new FrameworkElementFactory(typeof(Image));
Binding _binding = new Binding("Image");
_binding.Mode = BindingMode.TwoWay;
_factory.SetValue(Image.SourceProperty, _binding);
DataTemplate _cellTemplate = new DataTemplate();
_cellTemplate.VisualTree = _factory;
_dataTemplateColumn.CellTemplate = _cellTemplate;
Style _style = new Style();
_style.Setters.Add(new Setter(BackgroundProperty, Brushes.Transparent));
_dataTemplateColumn.CellStyle = _style;
I then create a Custom Object at runtime which includes the image for me and run the following 2 methods on the Image, the first to resize it and the second to convert it into a Bitmap rather than a BitmapImage (which is the only format I have managed to get it working in WPF with so far):
public static Bitmap ResizeImage(this Bitmap Bitmap, Size size)
{
try
{
Bitmap _bitmap = new Bitmap(size.Width, size.Height);
using (Graphics _graphic = Graphics.FromImage((Image)_bitmap))
{
_graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
_graphic.DrawImage(Bitmap, 0, 0, size.Width, size.Height);
}
_bitmap.MakeTransparent(Color.Magenta);
return _bitmap;
}
catch (Exception ex)
{
throw ex;
}
}
public static Bitmap ToBitmap(this BitmapImage BitmapImage)
{
using (MemoryStream _stream = new MemoryStream())
{
BitmapEncoder _encoder = new BmpBitmapEncoder();
_encoder.Frames.Add(BitmapFrame.Create(BitmapImage));
_encoder.Save(_stream);
System.Drawing.Bitmap _bitmap = new System.Drawing.Bitmap(_stream);
_bitmap.MakeTransparent(Color.Magenta);
return new Bitmap(_bitmap);
}
}
The Image is being displayed in the correct size and position in the DataGrid but the transparency is not preserved from the .png format. If anyone knows a better method for me (perhaps it is more correct to take the Image into a resource file first for example?) or a way to get the transparency working within my current code it would be most appreciated!
The following example gives you an idea of how it may look like:
XAML:
<Window ...>
<Window.Resources>
<DataTemplate x:Key="ImageCellTemplate">
<Image Source="{Binding Image}" Width="100"/>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False"/>
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var col = new DataGridTemplateColumn();
col.CellTemplate = (DataTemplate)Resources["ImageCellTemplate"];
dataGrid.Columns.Add(col);
foreach (var file in Directory.EnumerateFiles(#"C:\Users\Public\Pictures\Sample Pictures", "*.jpg"))
{
dataGrid.Items.Add(new DataItem { Image = file });
}
}
}
public class DataItem
{
public string Image { get; set; }
}

Providing an initial image placeholder for the WPF Image class

When i run the project a runtime error ocure:
Error: Property 'UriSource' or property 'StreamSource' must be set.
because this.ImageUri is null , i don't know why this.ImageUri be null ! help me
I have been working with the WPF ListBox using images as my list box items. The sourced image path points to a server hosting those images. While on fast network, the images appeared without any noticeable delay. However it became apparent over a slow link that the user experience degraded and I really wanted to show a placeholder image while the image was downloaded and decoded.
Surprisingly, I didn't find a solution in the blogosphere for this issue so I coded up a derived class to address this.
The sample XAML below is from my item container style. I replaced Image with my local class implementation local:ImageLoader.
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyData}">
...
<StackPanel Grid.Column="0" Margin="5">
<Border BorderThickness="0">
<MyControl:ImageLoader Width="50" Height="50" ImageUri="{Binding Path=profile_image_url_https, FallbackValue=profile_image_url_https}" InitialImage="/MyProject;component/Images/nopic.png" HorizontalAlignment="Left"></imgz:ImageLoader>
</Border>
</StackPanel>
...
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source = {StaticResource MyData}}" />
</Grid>
The heart of the handling for the initial image is in the OnLoaded() method, where I use a BitmapImage as the source and set the UriSource to the derived class' ImageUri dependency property, which allows for data binding. The initial image is updated to the actual image when the download completes or when a failure event is received. The class also optionally allows you to specify a "LoadFailedImage".
public class ImageLoader : Image
{
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(null, null));
private BitmapImage loadedImage;
public ImageLoader()
{
this.Loaded += this.OnLoaded;
}
public string LoadFailedImage
{
get;
set;
}
public Uri ImageUri
{
get {return this.GetValue(ImageUriProperty) as Uri;}
set {this.SetValue(ImageUriProperty, value);}
}
public string InitialImage
{
get;
set;
}
private new ImageSource Source
{
get {return base.Source;}
set {base.Source = value;}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Loading the specified image
this.loadedImage = new BitmapImage();
this.loadedImage.BeginInit();
this.loadedImage.CacheOption = BitmapCacheOption.OnDemand;
this.loadedImage.DownloadCompleted += this.OnDownloadCompleted;
this.loadedImage.DownloadFailed += this.OnDownloadFailed;
this.loadedImage.UriSource = this.ImageUri;
this.loadedImage.EndInit();
// The image may be cached, in which case we will not use the initial image
if (!this.loadedImage.IsDownloading)
{
this.Source = this.loadedImage;
}
else
{
// Create InitialImage source if path is specified
if (!string.IsNullOrWhiteSpace(this.InitialImage))
{
BitmapImage initialImage = new BitmapImage();
// Load the initial bitmap from the local resource
initialImage.BeginInit();
initialImage.UriSource = new Uri(this.InitialImage, UriKind.Relative);
initialImage.DecodePixelWidth = (int)this.Width;
initialImage.EndInit();
// Set the initial image as the image source
this.Source = initialImage;
}
}
e.Handled = true;
}
private void OnDownloadFailed(object sender, ExceptionEventArgs e)
{
if (!string.IsNullOrWhiteSpace(this.LoadFailedImage))
{
BitmapImage failedImage = new BitmapImage();
// Load the initial bitmap from the local resource
failedImage.BeginInit();
failedImage.UriSource = new Uri(this.LoadFailedImage, UriKind.Relative);
failedImage.DecodePixelWidth = (int)this.Width;
failedImage.EndInit();
this.Source = failedImage;
}
}
private void OnDownloadCompleted(object sender, EventArgs e)
{
this.Source = this.loadedImage;
}
}
When i run the project a runtime error ocured:
Error: Property 'UriSource' or property 'StreamSource' must be set.
because this.ImageUri is null , i don't know why this.ImageUri be null ! help me
If it isn't the semicolon typo in InitialImage="/MyProject;component/Images/nopic.png",
maybe it's better to set your InitialImage as Default in ImageUri
public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
"ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(new Uri("/MyProject/component/Images/nopic.png"), null));
UPDATE:
You have to bind to Image.Source and you could use PriorityBinding to show a placeholder.
<Image.Source>
<PriorityBinding>
<!--highest priority sources are first in the list-->
<Binding Path="YourImageUri"
IsAsync="True" />
<Binding Path="InitialImageUri"
IsAsync="True" />
</PriorityBinding>
</Image.Source>
For a "LoadFailedImage" a would subsribe to Image.ImageFailed Event.
Hope this helps.

Resources