title: Update ObservableCollecttion on objects property changed
body: <p>Hopefully someone can help me out with this. I am creating a Silverlight app which is used for editing images. A user has a project which contain layers which contain elements. (Elements are text and image elements).</p>
  
  <p>I have a class which represents the project. It contains an ObservableCollection and each Layer has an ObservableCollection. Element is an abstract class. There is a TextElement and ImageElement class which inherit from Element.</p>
  
  <p>My problem is the UI never gets updated when I change an element inside the collection. I am using INotifyPropertyChanged on all my properties and I am catching CollectionChanged on the collections but still no go. The CollectionChanged event for ObservableCollection never gets hit on an update of one of its elements.</p>
  
  <p>This is the code I had originally had:</p>
  
  <pre><code>void Elements_CollectionChanged(
      object sender,     
      System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
  { this.NotifyChange("Elements"); }
  </code></pre>
  
  <p>This is the binding:</p>
  
  <pre><code>&lt;!-- Workspace --&gt;
          &lt;ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="5" DataContext="{Binding Project}"&gt;
              &lt;Canvas x:Name="Canvas" Background="LightGray" Width="{Binding Path=CanvasWidth}" Height="{Binding Path=CanvasHeight}"&gt;
                  &lt;ItemsControl ItemsSource="{Binding Path=Layers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"&gt;
                      &lt;ItemsControl.ItemsPanel&gt;
                          &lt;ItemsPanelTemplate&gt;
                              &lt;Canvas /&gt;
                          &lt;/ItemsPanelTemplate&gt;
                      &lt;/ItemsControl.ItemsPanel&gt;
                      &lt;ItemsControl.ItemTemplate&gt;
  
                          &lt;DataTemplate&gt;                                
                              &lt;ItemsControl ItemsSource="{Binding Path=Elements, Mode=OneWay}"&gt;
                                  &lt;ItemsControl.ItemsPanel&gt;
                                      &lt;ItemsPanelTemplate&gt;
                                          &lt;Canvas Background="{Binding Path=BackgroundColor}"
                                                  Visibility="{Binding Path=Visible, Converter={StaticResource BoolConverter}}"
                                                  Opacity="{Binding Path=Opacity}"
                                                  Width="{Binding ElementName=Canvas, Path=DataContext.CanvasWidth}" 
                                                  Height="{Binding ElementName=Canvas, Path=DataContext.CanvasHeight}"&gt;
                                          &lt;/Canvas&gt;
                                      &lt;/ItemsPanelTemplate&gt;
                                  &lt;/ItemsControl.ItemsPanel&gt;
                                  &lt;ItemsControl.ItemTemplate&gt;
                                      &lt;DataTemplate&gt;
                                          &lt;Canvas&gt;
                                              &lt;ContentControl Content="{Binding Converter={StaticResource ElementConverter}}"
                                                              Canvas.Top="{Binding Path=Top, Mode=TwoWay}"
                                                              Canvas.Left="{Binding Path=Left, Mode=TwoWay}"
                                                              Opacity="{Binding Path=Opacity}"
                                                              MouseLeftButtonDown="ContentControl_MouseLeftButtonDown"&gt;
                                                  &lt;interactivity:Interaction.Behaviors&gt;
                                                      &lt;behaviors:DragInCanvasBehavior /&gt;
                                                  &lt;/interactivity:Interaction.Behaviors&gt;
                                              &lt;/ContentControl&gt;
                                          &lt;/Canvas&gt;
                                      &lt;/DataTemplate&gt;
                                  &lt;/ItemsControl.ItemTemplate&gt;
                              &lt;/ItemsControl&gt;                                
                          &lt;/DataTemplate&gt;
  
                      &lt;/ItemsControl.ItemTemplate&gt;
                  &lt;/ItemsControl&gt;
              &lt;/Canvas&gt;
          &lt;/ScrollViewer&gt;
  </code></pre>
  
  <p>This is my converter:</p>
  
  <pre><code>public class LayerElementToFrameworkElementConverter : IValueConverter
  {
      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
          Element element = value as Element;
          if (element is ImageEditor.Client.BLL.TextElement)
          {
              ImageEditor.Client.BLL.TextElement txt = element as ImageEditor.Client.BLL.TextElement;
              return CreateTextBlock(txt);
          }
          else if (element is ImageElement)
          {
              ImageElement imgEle = element as ImageElement;
              return CreateImage(imgEle);
          }
          else
              return null;
      }
  
      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
          FrameworkElement frameworkElement = value as FrameworkElement;
          if (frameworkElement is TextBlock)
          {
              TextBlock txtBlock = value as TextBlock;
              return CreateTextElement(txtBlock);
          }
          else if (frameworkElement is Image)
          {
              Image img = value as Image;
              return CreateImageElement(img);
          }
          else
              return null;
      }
  
      private TextBlock CreateTextBlock(TextElement textElement)
      {
          TextBlock text = new TextBlock();
          text.Text = textElement.Text;
          text.Cursor = Cursors.Hand;
          text.SetValue(Canvas.ZIndexProperty, textElement.Order);
  
          ColorHexToBrushConverter converter = new ColorHexToBrushConverter();
          SolidColorBrush brush = (SolidColorBrush)converter.Convert(textElement.Color, null, null, null);
          text.Foreground = brush;
  
          return text;
      }
  
      private TextElement CreateTextElement(TextBlock textBlock)
      {
          TextElement textElement = new TextElement();
          textElement.Text = textBlock.Text;
          textElement.Top = (double)textBlock.GetValue(Canvas.TopProperty);
          textElement.Left = (double)textBlock.GetValue(Canvas.LeftProperty);
          textElement.Order = (int)textBlock.GetValue(Canvas.ZIndexProperty);
  
          ColorHexToBrushConverter converter = new ColorHexToBrushConverter();
          textElement.Color = (string)converter.ConvertBack(textBlock.Foreground, null, null, null);
  
          return textElement;
      }
  
      private Image CreateImage(ImageElement imageElement)
      {
          Image img = new Image();
          img.Width = imageElement.Width;
          img.Height = imageElement.Height;
          img.Source = imageElement.Source;
          img.Opacity = imageElement.Opacity;
          img.Cursor = Cursors.Hand;
  
          img.SetValue(Canvas.ZIndexProperty, imageElement.Order);
          img.SetValue(Canvas.TopProperty, imageElement.Top);
          img.SetValue(Canvas.LeftProperty, imageElement.Left);
  
          return img;
      }
  
      private ImageElement CreateImageElement(Image image)
      {
          ImageElement imageElement = new ImageElement();
          imageElement.Width = image.Width;
          imageElement.Height = image.Height;
          imageElement.Source = (BitmapImage)image.Source;
          imageElement.Order = (int)image.GetValue(Canvas.ZIndexProperty);
  
          return imageElement;
      }
  }
  </code></pre>
  
  <p>This is the class which contains to collection:</p>
  
  <pre><code>public class Layer : BaseBLL
  {
      private int numberOfElements;
  
      #region Properties
      public int Order
      {
          get { return this.GetValue&lt;int&gt;("Order"); }
          set { this.SetValue&lt;int&gt;("Order", value); }
      }
  
      public string BackgroundColor
      {
          get { return this.GetValue&lt;string&gt;("BackgroundColor"); }
          set { this.SetValue&lt;string&gt;("BackgroundColor", value); }
      }
  
      public double Opacity
      {
          get { return this.GetValue&lt;double&gt;("Opacity"); }
          set { this.SetValue&lt;double&gt;("Opacity", value); }
      }
  
      public bool Visible
      {
          get { return this.GetValue&lt;bool&gt;("Visible"); }
          set { this.SetValue&lt;bool&gt;("Visible", value); }
      }
  
      public ObservableCollection&lt;Element&gt; Elements { get; set; }
  
      public Element SelectedElement
      {
          get { return this.GetValue&lt;Element&gt;("SelectedElement"); }
          set { this.SetValue&lt;Element&gt;("SelectedElement", value); }
      }
      #endregion
  
      #region Commands
      public ICommand AddTextElementCommand { get; set; }
      public ICommand AddImageElementCommand { get; set; }
      public ICommand DeleteElementCommand { get; set; }
      public ICommand SetSelectedElementCommand { get; set; }
      #endregion
  
      #region Methods
      public Layer()
          :this("Untitled", 0, "#ffffff", 1, true, new ObservableCollection&lt;Element&gt;())
      {
      } 
  
      public Layer(string name, int order, string backgroundColor, double opacity, bool visible, ObservableCollection&lt;Element&gt; elements)
          : base(name)
      {
          this.Order = order;
          this.BackgroundColor = backgroundColor;
          this.Opacity = opacity;
          this.Visible = visible;
          this.Elements = elements;
  
          this.Elements.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Elements_CollectionChanged);
  
          this.AddTextElementCommand = new DelegateCommand(AddTextElement, CanAddTextElement);
          this.AddImageElementCommand = new DelegateCommand(AddImageElement, CanAddImageElement);
          this.DeleteElementCommand = new DelegateCommand(DeleteElement, CanDeleteElement);
      }
  
      private void Elements_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
      {
          foreach (Element element in e.NewItems)
              element.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(element_PropertyChanged);
      }
  
      void element_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
      {
          this.NotifyChange("Elements");
      }
  
      private bool CanAddTextElement(object param) { return true; }
      private void AddTextElement(object param)
      {
          TextElement text = new TextElement();
          text.Order = this.numberOfElements;
          this.Elements.Add(text);
  
          numberOfElements++;
          this.SelectedElement = text;
      }       
  
      private bool CanAddImageElement(object param) { return true; }
      private void AddImageElement(object param)
      {
          var dialog = new OpenFileDialog()
          {
              Filter = "Image Files (*.bmp;*.jpg;*.gif;*.png;)|*.bmp;*.jpg;*.gif;*.png;",
              Multiselect = false
          };
  
          bool? userClickedOK = dialog.ShowDialog();
          if (userClickedOK == true)
          {
              string fileName = dialog.File.Name;
              FileStream stream = dialog.File.OpenRead();
  
              var imageSource = new BitmapImage();
              using (FileStream fileStream = stream)
              {
                  imageSource.SetSource(fileStream);
                  byte[] data = new byte[fileStream.Length];
                  fileStream.Read(data, 0, data.Length);
                  fileStream.Flush();
                  fileStream.Close();
              }
  
              ImageElement img = new ImageElement();
              img.Name = fileName;
              img.Order = this.numberOfElements;
              img.Source = imageSource;
              img.Height = imageSource.PixelHeight;
              img.Width = imageSource.PixelWidth;
              this.Elements.Add(img);
  
              this.numberOfElements++;
              this.SelectedElement = img;
          }
      }
  
      private bool CanDeleteElement(object param)
      {
          if (this.SelectedElement != null)
              return true;
          else
              return false;
      }
  
      private void DeleteElement(object param)
      {
          throw new NotImplementedException();
      }      
      #endregion
  }
  </code></pre>
  
  <p>This is the code for the one of the Elements:</p>
  
  <pre><code>public class TextElement : Element
  {
      public string Text
      {
          get { return this.GetValue&lt;string&gt;("Text"); }
          set { this.SetValue&lt;string&gt;("Text", value); }
      }
  
      public int FontSize
      {
          get { return this.GetValue&lt;int&gt;("FontSize"); }
          set { this.SetValue&lt;int&gt;("FontSize", value); }
      }
  
      public bool Bold
      {
          get { return this.GetValue&lt;bool&gt;("Bold"); }
          set { this.SetValue&lt;bool&gt;("Bold", value); }
      }
  
      public bool Italic
      {
          get { return this.GetValue&lt;bool&gt;("Italic"); }
          set { this.SetValue&lt;bool&gt;("Italic", value); }
      }
  
      public string Color
      {
          get { return this.GetValue&lt;string&gt;("Color"); }
          set { this.SetValue&lt;string&gt;("Color", value); }
      }
  
      public FontFamily Font
      {
          get { return this.GetValue&lt;FontFamily&gt;("Font"); }
          set { this.SetValue&lt;FontFamily&gt;("Font", value); }
      }
  
      public TextElement()
          : this("Untitled", 1, 5, 5, 0, 0, 0, "New text", 12, false, false, "#aaaaaa", new FontFamily("Arial"))
      {
      }
  
      public TextElement(string name, double opacity, double top, double left, double rotateAngle, double centerX, double centerY, 
          string text, int fontSize, bool bold, bool italic, string color, FontFamily font)
          : base(name, opacity, top, left, rotateAngle, centerX, centerY)
      {
          this.Text = text;
          this.FontSize = fontSize;
          this.Bold = bold;
          this.Italic = italic;
          this.Color = color;
          this.Font = font;
      }
  }
  </code></pre>
  
  <p>If anyone can help I would be very grateful.</p>