Why is my ImageSource Binding not updated? - wpf

I'm trying to dynamically bind an Image Source in XAML to an URI in ViewModel (MVVM). This works fine for the initial URI, the picture "C:\tmp\Test.png" is shown. But if I set another URI to ImageURI in ViewModel the picture is not updated. Can anyone help me?
XAML:
<Image x:Name="UserImage" Stretch="Fill" Grid.Row="0">
<Image.Source>
<BitmapImage CreateOptions="IgnoreImageCache" UriSource="{Binding ImageURI, UpdateSourceTrigger=Explicit, Mode=TwoWay}"/>
</Image.Source>
</Image>
ViewModel:
public string imageURI = "C:\\tmp\\Test.png";
public string ImageURI
{
get
{
return imageURI;
}
set
{
imageURI = value;
this.OnPropertyChanged("ImageURI");
}
}

BitmapImage implements ISupportInitialize. This means that property changes are ignored after initialization. Changing the Binding's source property has no effect.
You should directly bind the Image's Source property. Built-in automatic type conversion will create a BitmapFrame behind the scenes.
<Image Source="{Binding ImageURI}" .../>
Setting UpdateSourceTrigger=Explicit and Mode=TwoWay on the Binding is pointless.
If you need to explicitly create a BitmapImage (e.g. one where the IgnoreImageCache option is set), write an appropriate Binding Converter.

Pretty old question here with no solution. Sooo since i had a similar problem and used a slightly different approach, here is my solution:
Use a converter which returns a BitmapImage in this way:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
BitmapImage error = new();
error.BeginInit();
// OnLoad will give you errors just with the start of your application and
// and won't hide somewhere in your log
error.CacheOption = BitmapCacheOption.OnLoad;
error.UriSource = new Uri(#"pack://application:,,,/Images/error.png");
error.EndInit();
error.Freeze();
return error;
}
The binding:
<Image Source="{Binding Status, Converter={StaticResource enumAnalyzerStatusConverter}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
The OnLoad option will give an error on startup if something is bad with your image like the path. Remember to set the image in your project explorer to Ressource.

Related

Set BitmapImage dynamically via Binding using a converter in XAML

I have the following xaml (Note this is used in a Microsoft Workflow Foundation activity and is contained within a <sap:ActivityDesigner.Icon>. Hopefully, it won't matter but I thought I'd mention it.)
<DrawingBrush>
<DrawingBrush.Drawing>
<ImageDrawing>
<ImageDrawing.Rect>
<Rect Location="0,0" Size="16,16" ></Rect>
</ImageDrawing.Rect>
<ImageDrawing.ImageSource>
<BitmapImage UriSource="MyImage.png"/>
</ImageDrawing.ImageSource>
</ImageDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
I need to change the UriSource at run-time so I thought I'd use a converter as such:
<DrawingBrush>
<DrawingBrush.Drawing>
<ImageDrawing>
<ImageDrawing.Rect>
<Rect Location="0,0" Size="16,16" ></Rect>
</ImageDrawing.Rect>
<ImageDrawing.ImageSource>
<BitmapImage UriSource="{Binding Path=MyObject, Converter={StaticResource NameToImageConverter}}"/>
</ImageDrawing.ImageSource>
</ImageDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
but if try to bind it to my object and use a converter, I'm getting the following error:
The provided DependencyObject is not a context for this Freezable
Note that it doesn't hit my converter.
I found The provided DependencyObject is not a context for this Freezable WPF c# which I thought would help but to no avail.
When setting a name to the BitmapImage object
<BitmapImage Name="MyBitmapImage"/>
I thought I'd be able to set this via code but I still get the same error.
I don't know what I've done since originally looking at this but originally I got an error saying something along the lines that I should use a beginInit and endInit. Sorry don't have the exact error since I can't replicate it.
Any ideas on how this can be achieved?
Thanks.
A BitmapImage implements the ISupportInitialize interface and can only be initialized once, and not changed later. If your MyObject is supposed to change its value during runtime and notify the UI, that won't work.
You can change the Binding to
<ImageDrawing
ImageSource="{Path=MyObject, Converter={StaticResource NameToImageConverter}}"
Rect="0,0,16,16" />
and make the Binding Converter return a BitmapImage instead of an Uri:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
...
string uri = ...;
return new BitmapImage(new Uri(uri));
}

WPF Binding nested objects

I'm very new to WPF and I'm having difficulties binding a request object with nested objects which was derived from a WSDL to a XAML textbox. Programmatically I was able to bind to a textbox but I would like to understand the syntax needed to bind via XAML. Once I have some direction It'll make it much easier to research a full solution. Thanks
The ResultSet and Message Object will always be [0].
Code
MainWindow()
{
InitializeComponent();
GetMarketingMessagesResponse request = new GetMarketingMessagesResponse();
request = (GetMarketingMessagesResponse)XMLSerializerHelper.Load(request, #"C:\SSAResponse.xml");
DataContext = request;
Binding bind = new Binding();
bind.Source = request.ResultSet[0].Message[0];
bind.Path = new PropertyPath("SubjectName");
this.txtbSubject.SetBinding(TextBox.TextProperty, bind);
}
The return value in the Visual Studio Watch bind.Source = request.ResultSet[0].Message[0];
is
bind.Source = {GetMarketingMessagesResponseResultSetMessage} which is the class name.
XAML
I'm looking for direction on how to bind to this class and the properties inside
<TextBox Name="txtbMessageDetails" HorizontalAlignment="Right" Margin="0,50.08,8,0" TextWrapping="Wrap" Text="{Binding Source=ResultSet[0].Message[0], Path=SubjectName}" VerticalAlignment="Top" Height="87.96" Width="287.942"/>
Use a converter which will receive the request and extract the message.
<Window.Resources>
<local:MessageExtractorConverter x:Key="messageExtractorConverter" />
</Window.Resources>
<TextBox Name="txtbMessageDetails" HorizontalAlignment="Right" Margin="0,50.08,8,0" TextWrapping="Wrap" Text="{Binding Converter={StaticResource messageExtractorConverter}" VerticalAlignment="Top" Height="87.96" Width="287.942"/>
Converter implementation:
public class MessageExtractorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var val = value as GetMarketingMessagesResponse;
if (val != null)
{
// You can modify this code to extract whatever you want...
return val.ResultSet[0].Message[0];
}
else
{
return null;
}
}
You already put the request object in your DataContext, making it the default Source for all bindings. So, instead of specifying another Source (which would just override the DataContext), you use the binding's Path to make your way from the DataContext to the property you need:
<TextBox Name="txtbMessageDetails" Text="{Binding Path=ResultSet[0].Message[0].SubjectName}" />
Here is an article explaining how the DataContext works, and how it is "inherited" from control to control in your Window: http://www.codeproject.com/Articles/321899/DataContext-in-WPF

multibind image source in xaml & wpf

In my project, i have a folder called Images, where all the images iam using in my application are saved in subfolders.All the images are set to "Resource" in the buildprocess.
myproject
|__Images
|__AppImages
|__StarOn.png
|__StarOff.png
Now, if i do set my image manually like this:
<Image Source="Images\AppImages\StarOn.png" width="32" height="32"/>
the image is correctly shown in the imagebox.
i would like to set the image using a converter and a binding like this:
<Image>
<Image.Source>
<Binding Path="Number" converter="{StaticResource GetImagePathConverter}"/>
</Image.Source>
</Image>
where the number is an integer
and my converter is:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int questionNr=int.parse(value.ToString());
if (questionNr>100)
{
return "Images\\AppImages\\StarOn.png";
}
return "Images\\AppImages\\starOff.png";
}
but this is not changing the image ?..
what iam doing wrong ?
how can i set the image source correctly from the converter ?
thanks in advance
Your way of using converter is incorrect. You need to create an instance of your converter use it in your binding through StaticResource. local: is the local namespace which you need to declare in your xaml -
<Image>
<Image.Resources>
<local:GetImagePathConverter x:Key="GetImagePathConverter"/>
</Image.Resources>
<Image.Source>
<Binding Path="Number" Converter="{StaticResource GetImagePathConverter}"/>
</Image.Source>
</Image>
Also, Source property is not of type string but instead ImageSource so you need something in your converter -
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int questionNr=int.parse(value.ToString());
if (questionNr>100)
{
return new BitmapImage(new Uri("Images\\AppImages\\StarOn.png", UriKind.Relative));
}
return new BitmapImage(new Uri("Images\\AppImages\\StarOff.png", UriKind.Relative));
}
See this answer.
Basically, you have to take care of the type of object that you return in your converter, you cannot return string to a property of ImageSource type.
I'm not on my dev machine, but the code is something like this:
return new BitmapImage(new Uri(the/path/to/image.png)).Source; //or '*.ImageSource', can't remember

WPF Custom Control with Image.

I am quite new to WPF/XAML and I am currently facing a problem.
I have a solution with two projects in it, the first project is a Custom Control Library with a custom Window form control inside. The second project is a WPF application using my custom window form.
All work fine except for the form Icon. In the WPF application project I set my window icon property to /ProjectTwoNameSpace;component/Resources/Images/Film.ico, and in the WPF custom control I try to show that image that way :
<Image Grid.Column="0" Margin="3" Width="27" Height="27">
<Image.Source>
<BitmapImage UriSource="{Binding Path=Icon}" />
</Image.Source>
</Image>
But it doesn't work, I get a error at runtime saying that the property UriSource or StreamSource must be set for my <Image> tag.
Anyone can help me ? I think it's jsut a WPF newbie problem.
The UriSource property of a BitmapImage cannot be set as you have shown because it is of type Uri and you are trying to set it to a string. I'd say the easiest way to accomplish what you're doing is to bind your Image.Source to Icon, and then convert the string to a bitmap Image object. Assuming your control is in a window, this would look something like this
<Window.Resources>
<converters:StringToBitmapImageConverter x:Key="stringToBitmapImageConverter" />
</Window.Resources>
...
<Image Grid.Column="0" Margin="3" Width="27" Height="27"
Source="{Binding Path=Icon, Converter={StaticResource stringToBitmapImageConverter}}">
</Image>
And then the converter would look like:
class StringToBitmapImageConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(value as string);
image.EndInit();
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}

Problem with binding in converter in silverlight custom control

i got some xaml here and what i m trying to do it's simply bind a property call Property (not the real name) on the width of a rectangle and to convert the value of this property with the converter name Conv and it's working perfectly with {TemplateBinding Property} or DataContext={TemplateBinding Property} or with a relative source (like in the code sample).
My problem is that the converterParameter should also be a binding property, but i m not able to bind any property in the converterParameter. So the 30 in the sample should be something like {Binding Path=SecondProperty}. If anyone got that problem or maybe if anyone got some other way to bind stuff in custom control thanks a lot ;)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:RatingControl">
<Style TargetType="controls:Ctr">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:Ctr">
<Grid>
<Grid.Resources>
<controls:Converter x:Name="Conv" />
</Grid.Resources>
<Rectangle x:Name="rect" Width="{Binding Path=Property, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Conv}, ConverterParameter=30}" Height="20" />
It doesn't look like that's possible: http://msdn.microsoft.com/en-us/library/system.windows.data.binding.converterparameter(VS.95).aspx
You can add a property to the Converter class and bind to that.
You can't bind to a property of the Binding object, since it isn't a DependencyProperty in fact Binding isn't a DependencyObject. This is understandable can you imagine the complexity of managing dependency trees and the possiblity of recursive or circular bindings in bindings.
However you could use a Specialised converter for the task:-
public class MySpecialConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Ctr obj = (Ctr)value;
var val = obj.Property;
var param = obj.SecondProperty;
// Do your intended code with val and param here.
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This converter only works for one way binding");
}
}
now your Xaml looks like:-
<Rectangle x:Name="rect" Height="20"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Conv}" />
It's a really good solution but it's not working bcs my first property must be bind (twoWay) because if i got any change on it the converter must convert again the value so i get the result back and show the real result.

Resources