Shawn
Shawn

Reputation: 393

Binding ObservableCollection in Custom User Control

I have an ObservableCollection that is created in user control that contains a custom class. The custom class contains a few strings and booleans, nothing special.

public class StringLineInfo : INotifyPropertyChanged
{
    private string name { set; get; }
    private Color color { set; get; }
    private bool visible { set; get; }
    private bool follow { set; get; }

    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            NotifyPropertyChanged("Name");
        }
    }

    public Color Color
    {
        get { return color; }
        set
        {
            color = value;
            NotifyPropertyChanged("Color");
        }
    }

    public bool Visible
    {
        get { return visible; }
        set
        {
            visible = value;
            NotifyPropertyChanged("Visible");
        }
    }

    public bool Follow
    {
        get { return follow; }
        set
        {
            follow = value;
            NotifyPropertyChanged("Follow");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this,
                new PropertyChangedEventArgs(propertyName));
        }
    }
}

Inside the Xaml of the custom control I have added custom legend control. Inside the custom legend I have another ObservableCollection that an ItemsControl is databound to.

In the legend.xaml:

                                <!--<Image Name="imgMyImage" Grid.Column="1" Height="30" Width="30" Margin="5" Source="{Binding Name, Converter=NameToSrcConverter, ConverterParameter={StaticResource IconDictionary}}" />-->
                            <TextBlock Name="txtlineName" FontSize="12" Foreground="Black"  Text="{Binding Converter={StaticResource nameConverter}}"  Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"/>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

In the legend.xaml.cs:

    public ObservableCollection<StringLineInfo> allLines = new ObservableCollection<StringLineInfo>();

    public Dictionary<string, string> DvrIconDictionary = new Dictionary<string, string>();
    public Legend()
    {
        InitializeComponent();
        DataContext = this;
        itmCtrol.ItemsSource = allLines;
    }

    // to set the ItemsSource
    public void setItemsSource(ObservableCollection<StringLineInfo> source)
    {
        if (itmCtrl.ItemsSource == null)
        {
            itmCtrl.ItemsSource = source;
        }
    }

In the constructor of the main user control I set the ObservableCollection from the legend equal to the ObservableCollection in the main user control.

public MainControl()
    {
        InitializeComponent();
        MapLegend.SizeChanged += new SizeChangedEventHandler(MapLegend_SizeChanged);
        MapLegend.allLines = this.allLines;
    }

Even though both ObservableCollections always contain the same data (they're now the same object) the ItemsControl will not update or show anything. HOWEVER, if I set the ObservableCollections equal later in the code, say a button click, then it works just fine.

The button just calls the "setItemsSource" function.

Any ideas on why I can't set them equal at startup?

Upvotes: 0

Views: 2323

Answers (1)

Scott Munro
Scott Munro

Reputation: 13576

You actually have three references to the allLines collection to which you are trying to bind.

  1. allLines in the type that contains the MainControl method.
  2. allLines within legend.xaml.cs.
  3. itmCtrol.ItemsSource withing legend.xaml.cs.

I assume that the first reference is being populated correctly as you are eventually seeing the data. The second reference is initialized with an empty ObservableCollection that is not required (you could save yourself the overhead of initializing this object). The third is set to the same empty collection as the second.

Your code in the MainControl method sets the second reference to the correctly populated collection but does not affect the third refernce (itmCtrl.ItemsSource) in any way - it retains its reference to the empty collection.

My guess is that you are using code such as the following within legend.xaml.cs in the button click event that 'works just fine'.

itmCtrol.ItemsSource = allLines;

This would swap the itmCtrol.ItemsSource reference from the empty collection to the correctly populated one.

The simplest solution would be to replace the allLines field with a property that delegates straight to the itmCtrol.ItemSource property (use the itmCtrol.ItemsSource property as the backing field for the new property). It would look something like this.

    public ObservableCollection<StringLineInfo> AllLines
    {
        get
        {
            return (IObservableCollection<StringLineInfo>)itmCtrol.ItemsSource;
        }
        set
        {
            itmCtrol.ItemsSource = value;
        }
    }

Upvotes: 2

Related Questions