Binding an Image in WPF MVVM - c#

I am having some trouble binding in Image to my viewmodel. I finally got rid of the XamlParseException, but the image does not come up. I even hard coded the image in the ViewModel. Can someone see what I am doing wrong?
View:
<Image HorizontalAlignment="Left" Margin="0,0,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Bottom" Grid.Row="8" Width="200" Grid.ColumnSpan="2" >
<Image.Source>
<BitmapImage DecodePixelWidth="200" UriSource="{Binding Path=DisplayedImage, Mode=TwoWay}" />
</Image.Source>
ViewModel:
string _DisplayedImagePath = #"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg";//string.Empty;
int _DisplayedImageIndex;
BitmapImage _DisplayedImage = null;
public BitmapImage DisplayedImage
{
get
{
_DisplayedImage = new BitmapImage();
if (!string.IsNullOrEmpty(_DisplayedImagePath))
{
_Rail1DisplayedImage.BeginInit();
_Rail1DisplayedImage.CacheOption = BitmapCacheOption.OnLoad;
_Rail1DisplayedImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_Rail1DisplayedImage.UriSource = new Uri(_DisplayedImagePath);
_Rail1DisplayedImage.DecodePixelWidth = 200;
_Rail1DisplayedImage.EndInit();
}
return _Rail1DisplayedImage;
}
set
{
_Rail1DisplayedImage = value;
OnPropertyChanged("DisplayedImage");
}
}

Displaying an Image in WPF is much easier than that. Try this:
<Image Source="{Binding DisplayedImagePath}" HorizontalAlignment="Left"
Margin="0,0,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Bottom"
Grid.Row="8" Width="200" Grid.ColumnSpan="2" />
And the property can just be a string:
public string DisplayedImage
{
get { return #"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg"; }
}
Although you really should add your images to a folder named Images in the root of your project and set their Build Action to Resource in the Properties Window in Visual Studio... you could then access them using this format:
public string DisplayedImage
{
get { return "/AssemblyName;component/Images/ImageName.jpg"; }
}
UPDATE >>>
As a final tip... if you ever have a problem with a control not working as expected, simply type 'WPF', the name of that control and then the word 'class' into a search engine. In this case, you would have typed 'WPF Image Class'. The top result will always be MSDN and if you click on the link, you'll find out all about that control and most pages have code examples as well.
UPDATE 2 >>>
If you followed the examples from the link to MSDN and it's not working, then your problem is not the Image control. Using the string property that I suggested, try this:
<StackPanel>
<Image Source="{Binding DisplayedImagePath}" />
<TextBlock Text="{Binding DisplayedImagePath}" />
</StackPanel>
If you can't see the file path in the TextBlock, then you probably haven't set your DataContext to the instance of your view model. If you can see the text, then the problem is with your file path.
UPDATE 3 >>>
In .NET 4, the above Image.Source values would work. However, Microsoft made some horrible changes in .NET 4.5 that broke many different things and so in .NET 4.5, you'd need to use the full pack path like this:
<Image Source="pack://application:,,,/AssemblyName;component/Images/image_to_use.png">
For further information on pack URIs, please see the Pack URIs in WPF page on Microsoft Docs.

If you have a process that already generates and returns an Image type, you can alter the bind and not have to modify any additional image creation code.
Refer to the ".Source" of the image in the binding statement.
XAML
<Image Name="imgOpenClose" Source="{Binding ImageOpenClose.Source}"/>
View Model Field
private Image _imageOpenClose;
public Image ImageOpenClose
{
get
{
return _imageOpenClose;
}
set
{
_imageOpenClose = value;
OnPropertyChanged();
}
}

#Sheridan thx.. if I try your example with "DisplayedImagePath" on both sides, it works with absolute path as you show.
As for the relative paths, this is how I always connect relative paths, I first include the subdirectory (!) and the image file in my project.. then I use ~ character to denote the bin-path..
public string DisplayedImagePath
{
get { return #"~\..\images\osc.png"; }
}
This was tested, see below my Solution Explorer in VS2015..
)
Note: if you want a Click event, use the Button tag around the image,
<Button Click="image_Click" Width="128" Height="128" Grid.Row="2" VerticalAlignment="Top" HorizontalAlignment="Left">
<Image x:Name="image" Source="{Binding DisplayedImagePath}" Margin="0,0,0,0" />
</Button>

Related

Binding BitmapImage UriSource

I'm trying to develop my first Windows Store application.
I'm using the Hub Application template.
I want to display an Image from a Url in the first section of my HubPage:
<HubSection ... >
<DataTemplate>
<Grid ... >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
...
</Grid.RowDefinitions>
<Image Name="UserProfileImage" Margin="100, 0, 100, 0" Grid.Row="0" VerticalAlignment="Top">
<Image.Source>
<BitmapImage UriSource="{Binding ImageUrl}"></BitmapImage>
</Image.Source>
</Image>
</Grid>
</DataTemplate>
</HubSection>
And in my HubPage.xaml.cs:
[DefaultValue("http://images6.fanpop.com/image/photos/34200000/more-dumb-images-of-philip-j-fry-futurama-34257101-1440-900.png"")]
public string ImageUrl { get; set; }
But nothing is shown. If I set manually in the xaml file an image url it works fine...
The problem is, that the Binding mechanism does not know where to look for the ImageUrl property.
You can either set the DataSource of the tag or any of it's parents to an instance of the class, where the property is defined.
Or you use more information in each Binding statement. To bind to yourself, just use
UriSource="{Binding RelativeSource={RelativeSource Self}, Path=ImageUrl}"
or
UriSource="{Binding ImageUrl, RelativeSource={RelativeSource Self}}"
see also WPF Bind to itself
Edit:
You also need to have a notification mechanism on your variable you are binding to. Either make it a DependencyProperty or use a PropertyChanged event (either through INotifyPropertyChanged and call PropertyChanged on changes in the setter with the name of the property, or create an event called <name of property>Changed and invoke this event on changes.
Unfortunately it seems that BitmapSource.UriSource cannot be databound (or more specifically, it can only be bound once and further changes ignored). See discussion here.
In your code you're using <DataTemplate> which means the parent control of this will be either <ListView> or something like that.
Your code reflects very few things about your scenario.
I suggest you to bind ImageSource instead of string
Instead working on XAML part, I suggest you to edit your code-behind part.
I'm briefing the sample here. relate this, with your case and do needful.
Example:-
// BlankPage.xaml ----
// eg. I've a listView and i want to display
// image dynamically in DataTemplate
<ListView x:Name="lstView">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding bmp}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now, define a model class to give itemsSource to ListView.
// In BlankPage.xaml.cs file
public class model
{
public ImageSource bmp { get; set; }
}
Now, say for example I'm assigning itemsSource to ListView in Page_Loaded event.
// in BlankPage.xaml.cs file
// declare this BlankPage's Loaded event in BlankPage's Constructor
// and define the event handler like this
void BlankPage2_Loaded(object sender, RoutedEventArgs e)
{
model m1 = new model()
{
bmp = new BitmapImage(new Uri("ms-appx:///Assets/TechVista(300x300).png", UriKind.Absolute))
};
// here "ms-appx:///Assets/TechVista(300x300).png" should be the Full-System-Path where image is located.
// and according to that declare the UriKind as Relative or Absolute or other.
List<model> lstModels = new List<model>();
lstModels.Add(m1);
// you can add as many models as you want here.
// for reference I'm adding only one here.
lstView.ItemsSource = lstModels;
}
It will work for sure.
For more exact answer, elaborate little more here.
Hope that helps..!

How to display a placeholder image in a list box that have bonded data?

On windows phone 8 I have made a list box bandied with a web server data which are an image and a text sometimes the server returns only text as their are no image uploaded by the user. I need to display a placeholder image.
<ListBox Grid.Row="1" ItemsSource="{Binding places}" Name="mrx" Margin="0,10,0,0" Loaded="mrx_Loaded">
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal" Margin="10,0,10,8">
<StackPanel Margin="10,0,10,8">
<TextBlock Text="{Binding name}" Foreground="Black" FontWeight="Bold" FontSize="40"/>
<Image Source="{Binding url}" Width="100" Height="100"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here how I access url:
public BitmapImage h { get; set; }
public BitmapImage url
{
get { return h; }
set
{
if (h == null)
{
h.UriSource = new Uri("profile-placeholder.gif", UriKind.RelativeOrAbsolute);
}
}
}
If your h == null when there is no image returned then there is easier way to do what you want - use TargetNullValue in Binding:
<Image Source="{Binding url, TargetNullValue='/Example;component/Resources/placeholder.png'}" Width="100" Height="100"/>
In above case be sure to set BuildAction of placeholder.png as Resource. (Example here is your project name, Resources is a path to the file). You can also bind directly to your h in this case.
If you insist on using designed property then:
I'm not sure how the rest of your code looks like, but but when you have defined binding like this:
<Image Source="{Binding url}" Width="100" Height="100"/>
When the item is being loaded the Getter of url is fired. So using Setter to repleace null with Bitmapimage won't help here (of course if you are not running Setter somewhere else, but defining Setter without value - kind a weird, implement it different or provide separate method.
Check if that would help:
public BitmapImage url
{
get
{
if (h == null)
return new BitmapImage() { UriSource = new Uri("/Projectname;component/path/profile-placeholder.gif", UriKind.Relative) };
return h;
}
}
Setting UriSource depends also how your file is defined in your project (Build Action), you can take a look here.
according to me you can try 2 options
1) make a image failed event of the image and when its called then set the default image as source.
2) put the image view inside a grid with background as the default image so if url comes then it will overlap the default image and show you user image and if it fails then the background of the image will be there as default image.
I hope this might help ......
Add a default image in your assets. Set this image path string as a default value to the "url" that you use for binding. and when you get a web response and parse it, check if there is a valid url path string for an image. If there is a valid url string parsed for the image, then assign it to "url" field else If there is no valid url string for the image, do not set it to the url field. The"url" string field will any way contain the default image that you set.
Hope this helps you. Thanks.
See Conditional text formatting XAML WP8
You should bind to url and set conditional trigger if url is null
<Image Source = "{Binding url} " Width="100" Height="100"/>
Here, instead of binding image source to URL directly, bind it to property having URL as a global variable and initiate URL to placeholder URL.
Example:
private string yourURL= "/Resources/Images/placeholder.png";
public string YourURL
{
get
{
return yourURL;
}
set
{
if (value != yourURL)
{
yourURL= value;
}
}
}
and statement will be like:
<Image Source="{Binding YourURL}" Width="100" Height="100"/>

Load a user control in Wpf via binding

Currently I've got a single window and several user controls in a WPF application. My objective is to load them in the XAML via a binding to a property in the View Model. I've been looking for a way to do this for some time now, but most solutions hinge on using the code-behind instead of the View Model. Is there anyway to do this via a binding? Something like:
<StackPanel Height="500" HorizontalAlignment="Left" Margin="0,46,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="500" Content="{Binding SomeUserControl}" />
And in the code:
private Control _someUserControl;
public Control SomeUserControl
{
get { return _someUserControl; }
set { _someUserControl = value; }
}
You can use a ContentPresenter instead of the StackPanel, since there is only a single element:
<ContentPresenter Height="500" HorizontalAlignment="Left" Margin="0,46,0,0" VerticalAlignment="Top" Width="500" Content="{Binding SomeUserControl}" />

Why do my images show at designtime and not at runtime?

I have a WPF <Image> that I am trying to display on a <Button> inside of a <Toolbar> with the following code.
<ToolBarTray>
<ToolBar Height="26" Name="toolBar1" VerticalAlignment="Top" >
<Button Name="StampButton" Click="StampButton_Click">
<Image Source="/MyApp.Resources;component/Resources/MyImage.png"
Height="16" Width="16" Stretch="Fill"/>
</Button>
</ToolBar>
</ToolBarTray>
The image shows up just fine at design time. However, at runtime nothing is displayed. The resources are in another dll called MyApp.Resources. The button is actually created just fine and the click event works fine also.
Set your image build action to "Resource". Try to use full source path.
I was getting same problem - image displaying at design time, but not at runtime.
I had build action set to resource, tried different pack uris etc etc, but solution was simple
<Window.Resources>
<BitmapImage x:Key="your_image_key" UriSource="your_image.png" />
<Image Source="{StaticResource your_image_key}"/>
</Window.Resources>
This is what worked for me, from a site called wpf-tutorial.com:
<Image Source="/WpfTutorialSamples;component/Images/copy.png" />
Images is a subfolder of the project folder in my case, and I assume also in this sample.

How to set XamDataTree Icon to Bitmap instead of file name

I've set the XamDataTree image, but I don't want to copy the image folder to my debug folder every time. Instead I want to add the image to my project resources and use it from there. Currently no image is shown because it expects a path to an image where I give it an actual bitmap. The Icon property in the TreeNode is set in another part of the code.
This is my Xaml code:
<ig:XamDataTree
Grid.Row="1"
Name="MyTree"
ScrollViewer.CanContentScroll="True"
ItemsSource="{Binding ComparedContents}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
>
<ig:XamDataTree.CheckBoxSettings>
<ig:CheckBoxSettings CheckBoxVisibility="Visible" />
</ig:XamDataTree.CheckBoxSettings>
<ig:XamDataTree.CollapsedIconTemplate>
<DataTemplate>
<Image Source="{Binding Path=Icon}"/>
</DataTemplate>
</ig:XamDataTree.CollapsedIconTemplate>
<ig:XamDataTree.ExpandedIconTemplate>
<DataTemplate>
<Image Source="{Binding Path=Icon}"/>
</DataTemplate>
</ig:XamDataTree.ExpandedIconTemplate>
<ig:XamDataTree.GlobalNodeLayouts>
<ig:NodeLayout
Key="Children"
DisplayMemberPath="Text"
TargetTypeName="Model.TreeNode"
>
</ig:NodeLayout>
</ig:XamDataTree.GlobalNodeLayouts>
</ig:XamDataTree>
This is my model, each Property has a private store for the value and fires an event if it changes.
public class TreeNode : INotifyPropertyChanged
{
public Label Text;
public System.Drawing.Image Icon;
public ObservableCollection<TreeNode> Children;
}
Assuming you embedded the image into your application, you can use a pack uri to load the image into the node template. Instead of using an Image type on your Icon property you should use Uri and set it equal to something like this:
Icon = new Uri("pack://application:,,,/Resources/Images/icon.png");
You need to modify the binding in your templates a bit because the DataContext of this template is going to be a XamDataTreeNodeDataContext. This object has a Data property which will be your TreeNode object. Your binding should be updated to:
<Image Source="{Binding Path=Data.Icon}"/>

Categories

Resources