0

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.
5
  • 1
    Does the URI of the image change? Or how do you change that image? In case the URI changes it needs to raise a RaisePropertyChanged. Commented Dec 8, 2015 at 17:48
  • The uri does change. ResourceHelper.LoadBitmapURIFromResource("another image") just changes the uri to "pack://application:,,,/ ... another image. Also, I have a RaisePropertyChanged (now included), but how can the URI changing raise a RaisePropertyChanged? Commented Dec 8, 2015 at 17:58
  • 1
    ComputerInfo need to implement INotifyChanged and you should add set get on CurrentImage and add the RaisePropertyChanged. Commented Dec 8, 2015 at 18:01
  • This doesn't seem to be changing the images either. Commented Dec 8, 2015 at 20:22
  • Alex, were you able to get the binding to work? Commented Dec 11, 2015 at 19:16

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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");
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.