Ralt
Ralt

Reputation: 2206

How To Accommodate ItemsSource and Items

Controls such as the ListBox have an ItemsSource property which you can bind to

<ListBox x:Name="ListBoxColours" ItemsSource="{Binding Colours}" />

It also has an Items property however, which can be used to add items in the code behind

ListBoxColours.Items.Add("Red");

I'm creating a CustomControl which has a ListBox in. I've exposed the ItemSource in my control to allow the user to bind the items to a property in their ViewModel.

<ListBox
     x:Name="PART_ListBox"
     ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=local:TextBoxComboControl}}"
     SelectionMode="Single" />

...

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
    "ItemsSource", typeof(IEnumerable), typeof(TextBoxComboControl), new PropertyMetadata(default(IEnumerable)));

public IEnumerable ItemsSource
{
    get { return (IEnumerable) GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

...

<local:TextBoxComboControl ItemsSource="{Binding Colours}" />

I would like to add the ability for a user to add items in the code behind as well though, incase they don't want to use a binding. I was wondering how the ItemSource / Items properties interact with eachother. In order to allow them to use either peoprty, I would have to bind the ListBox items to both properties within my control.

Upvotes: 0

Views: 131

Answers (1)

Colin Smith
Colin Smith

Reputation: 12550

The ListBox derives from Selector which derives from ItemsControl .

If you look at the source code to ItemsControl:

You can see:

/// <summary> 
///     ItemsSource specifies a collection used to generate the content of
/// this control.  This provides a simple way to use exactly one collection 
/// as the source of content for this control. 
/// </summary>
/// <remarks> 
///     Any existing contents of the Items collection is replaced when this
/// property is set. The Items collection will be made ReadOnly and FixedSize.
///     When ItemsSource is in use, setting this property to null will remove
/// the collection and restore use to Items (which will be an empty ItemCollection). 
///     When ItemsSource is not in use, the value of this property is null, and
/// setting it to null has no effect. 
/// </remarks>
    [Bindable(true), CustomCategory("Content")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
    public IEnumerable ItemsSource
    {
        get { return Items.ItemsSource; }
        set
        {
            if (value == null) 
            { 
                ClearValue(ItemsSourceProperty);
            } 
            else
            {
                SetValue(ItemsSourceProperty, value);
            } 
        }
    } 

If you look at the Items property which is of type ItemsCollection ... it can be in one of two modes (ItemsSource mode or "direct" mode).

/// <summary>
/// ItemCollection will contain items shaped as strings, objects, xml nodes,
/// elements, as well as other collections.  (It will not promote elements from
/// contained collections; to "flatten" contained collections, assign a
/// <seealso cref="System.Windows.Data.CompositeCollection"/> to
/// the ItemsSource property on the ItemsControl.)
/// A <seealso cref="System.Windows.Controls.ItemsControl"/> uses the data
/// in the ItemCollection to generate its content according to its ItemTemplate.
/// </summary>
/// <remarks>
/// When first created, ItemCollection is in an uninitialized state, neither
/// ItemsSource-mode nor direct-mode.  It will hold settings like SortDescriptions and Filter
/// until the mode is determined, then assign the settings to the active view.
/// When uninitialized, calls to the list-modifying members will put the
/// ItemCollection in direct mode, and setting the ItemsSource will put the
/// ItemCollection in ItemsSource mode.
/// </remarks>

There's an internal member called _isUsingItemsSource which gets set/cleared to track which mode it is in - this makes various method/properties behave differently depending on the mode.

"Items" are accessed via a CollectionView (which is kept in the _collectionView member) - this either points to an InnerItemCollectionView which wraps access to the direct items , or a CollectionView created with CollectionViewSource.GetDefaultCollectionView when SetItemsSource is called due to an "itemssource" being set.

You are most probably deriving from Control so you need to provide the similar behaviour. Maybe you can derive from ItemsControl to gain that behaviour....depends on your control of course if that is suitable.

Upvotes: 2

Related Questions