-1

I have the following custom canvas that fires when the children are added or deleted.

public class CustomCanvas : Canvas, INotifyPropertyChanged
{
    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {        
        OnPropertyChanged(new PropertyChangedEventArgs(""));
    }
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }

    public event PropertyChangedEventHandler PropertyChanged;
}    

I also have a Listbox where i wish to display the canvas's children's names.

public void Button_Click_1(object sender, RoutedEventArgs e)
{
    shapes= new ObservableCollection<string>();
    foreach (FrameworkElement drawing in customCanvas.Children)
    {
        shapes.Add(drawing.Name);
    }
    listBox.ItemsSource = shapes;
}

The problem I have is, the listbox doesn't update when I add any Framework Element to my Custom Canvas. It requires me clicking Button_1. I thought ObservableCollection and InotifyPropertyChanged would automatically update my listbox. Please help.

FoggyFinder
  • 2,230
  • 2
  • 20
  • 34
  • 2
    This is the wrong approach. To display a set of shapes, use an ItemsControls with a Canvas as ItemsPanel. This answer may give you an idea: https://stackoverflow.com/a/40190793/1136211 – Clemens Jul 01 '18 at 06:16
  • @Clemens, thanks for this. I have your example working. I can get adding and removing to update but what if I want to update color, the event doesn't seem to fire. var rect = viewModel.Shapes[0] as ShapeData; rect.Fill = Brushes.Brown; – ALLofitsMATHS Jul 01 '18 at 11:22
  • In order to notify about changes, the ShapeData class should of course implement INotifyPropertyChanged and fire the PropertyChanged event from its property setters. You seem to know that already. – Clemens Jul 01 '18 at 11:25

1 Answers1

-1

You want to bind contents of a Listbox to names of CustomCanvas children. For that, you need a collection that implements INotifyCollectionChanged. ObservableCollection is the default one. Sadly, Children property of CustomCanvas is not implementing INotifyCollectionChanged.

A working workaround would be like this: you can store a separate ObservableCollection and update it in OnVisualChildrenChanged.

public class CustomCanvas : Canvas, INotifyPropertyChanged
{
    public ObservableCollection<string> Names { get; } = new ObservableCollection<string>();

    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        Names.Clear();
        foreach(FrameworkElement child in Children)
        {
            Names.Add(child.Name);
        }

        //Note: this is not needed for Binding to Names to work
        OnPropertyChanged(new PropertyChangedEventArgs(""));
    }

    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

In xaml, you will then have:

    <local:CustomCanvas
        x:Name="customCanvas"
        Width="100"
        Height="100"
        Margin="327,101,0,0"
        HorizontalAlignment="Left"
        VerticalAlignment="Top" />
    <ListBox
        Width="100"
        Height="100"
        Margin="638,201,0,0"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        ItemsSource="{Binding Names, ElementName=customCanvas}">
    </ListBox>

For information: INotifyPropertyChanged is not working in your example, as the Children collection itself is still the same object, and Bindings will not be updated. That it why you need the INotifyCollectionChanged implementing collection (ObservableCollection).

I also recommend you to use MVVM pattern, if you are going to use WPF with databindings. It makes it more streamlined.

  • Note that implementing INotifyPropertyChanged and firing PropertyChanged in OnVisualChildrenChanged is still pointless here. The Names property doesn't change when you add or remove elements. You should remove code parts that don't make sense. – Clemens Jul 01 '18 at 06:41
  • @Clemens I provided an example with minimal changes to the code from the author. He may or may not have other parts that were not listed in the example, that can require PropertyChanged to be fired. – Dmitri Baliev Jul 01 '18 at 06:48
  • There are obviously no other code parts that make use of the INotifyPropertyChanged implementation. Even if there were such parts, *calling* OnPropertyChanged in OnVisualChildrenChanged is still nonsense. A good answer would show how the method would look with only parts that make sense. – Clemens Jul 01 '18 at 07:17