Reputation: 699
I'm new to WPF, and I'm encountering a problem. I have a ListBox with images arranged in a floorplan, the image source is bound to a uri in the code. uri is to an embedded resource in the project. This all works fine on startup, until I have to change an image. No exceptions, but the images do not change. When I run in debug, I can see that even though the ObservableCollection items change, the mainwindow images do not.
Is this due to caching, or is it something else? I try to disable caching in the xaml, but I get the error The TypeConverter for "CacheMode" does not support converting from a string.
I'll do my best to be brief with the code and only include relevant information:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public ObservableCollection<ComputerInfo> CompListObs;
public ObservableCollection<ComputerInfo> compListObsNotifier
{
get { return CompListObs; }
set
{
CompListObs = value;
RaisePropertyChanged("compListObsNotifier");
}
}
//...
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
// This correctly loads the images from the xml and displays them on the main window:
var items = XDocument.Load(@"path to xml")
.Descendants("Computer")
.Select(i => new ComputerInfo {
MachineName = (string)i.Element("MachineName"),
Lat = (double)i.Element("Long"),
Lon = (double)i.Element("Lat"),
CurrentImage = ResourceHelper.LoadBitmapURIFromResource((string)i.Element("Img"))
}).ToList();
CompListObs = new ObservableCollection<ComputerInfo>(items);
}
public void MainTimer_Tick(object sender, EventArgs e)
{
// This runs fine and I can see the members of CompListObs are changing,
// but the images on the mainwindow are not changing.
foreach(var comp in CompListObs) { comp.CurrentImage = ResourceHelper.LoadBitmapURIFromResource("another image");
}
// INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public class ComputerInfo : INotifyPropertyChanged
{
public ClientInfo ClientsInfo { get; set; }
public string MachineName { get; set; }
public Uri CurrentImage { set; get; }
public double Lat { get; set; }
public double Lon { get; set; }
public Uri currentImageNotifier
{
get { return CurrentImage; }
set
{
CurrentImage = value;
RaisePropertyChanged("compListObsNotifier");
}
}
// INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Here is the xaml:
<ListBox ItemsSource="{Binding compListObsNotifier}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Path=CurrentImage}" Height="65"></Image>
</DataTemplate>
</ListBox.ItemTemplate>
<!-- after this, I place <ListBox.ItemsPanel> <ItemsPanelTemplate> and <Canvas> in the ListBox.
Upvotes: 0
Views: 907
Reputation: 618
When you bind to an observable collection you are subscribing to changes in the collection (for instance, adding or removing items from the collection). This does not subscribe you to changes to properties on the individual items in the collection. As others have stated, if you want to bind to a property on the items, you will need to raise a PropertyChanged
event when the property changes.
private Uri currentImage;
public Uri CurrentImage
{
get { return currentImage; }
set
{
currentImage = value;
RaisePropertyChanged("CurrentImage");
}
}
Note: You may want to wrap the setter in an if
statement and use Uri.Compare()
to determine if the given value is actually different from the current value. This way, you only raise a PropertyChanged
event when the property actually changes.
Also, in your code example you are setting the CurrentImage
property in this foreach
loop:
foreach(var comp in CompListObs)
{
comp.CurrentImage = ResourceHelper.LoadBitmapURIFromResource("another image");
}
However, you're raising your PropertyChanged
event from the currentImageNotifier
property. It's okay to raise PropertyChanged
events from a location outside of the property being modified, but you either need to replace the CurrentImage
assignment in your foreach
loop with currentImageNotifier
, or modify your CurrentImage
property to raise its own PropertyChanged
event. As it stands, you're not actually raising a PropertyChanged
event on the property you're binding to.
Honestly, it doesn't look like you even need the currentImageNotifier
property. It's not doing anything you couldn't just do with the CurrentImage
property directly.
Upvotes: 1
Reputation: 4223
When you are binding to CurrentImage and the value of CurrentImage changes, you must raise the property changed event for CurrentImage.
Based on the supplied code, you also could do this:
public Uri currentImageNotifier
{
get { return CurrentImage; }
set
{
CurrentImage = value;
RaisePropertyChanged("CurrentImage");
}
}
Upvotes: 1