When we used ListBox controls in our WPF project we faced the same problem as described in the previous post. Like DataGrid, ListBox control also has a property called SelectedItems, which isn’t a dependency property and therefore not bindable too. This post shows how ListBox control can be extended with a bindable version of the SelectedItems property.

First we have to create a new class, that derives from ListBox. Beside the dependency property for the selected items an event handler for selection changed events has to be defined. In the event handler the ListBox’s SelectedItems property gets assigned to the custom dependency property. To execute the event handler on every selection change, it has to be registered to the SelectionChanged event in the constructor of the class. Furthermore EndInit method could be overridden as follows to ensure that items based on the bound ViewModel property get selected in the View at the end of the initialisation.

CustomListBox.cs

/**
 * Copyright 2017 d-fens GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace biz.dfch.CS.ArbitraryWpfApplication.UI.Controls
{
    public class CustomListBox : ListBox
    {
        public CustomListBox()
        {
            SelectionChanged += CustomListBoxSelectionChanged;
        }

        void CustomListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedItemsList = SelectedItems;
        }

        public IList SelectedItemsList
        {
            get { return (IList)GetValue(SelectedItemsListProperty); }
            set { SetValue(SelectedItemsListProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemsListProperty =
            DependencyProperty.Register("SelectedItemsList", typeof(IList), typeof(CustomListBox), new PropertyMetadata(null));

        public override void EndInit()
        {
            if (null != SelectedItemsList)
            {
                SetSelectedItems(SelectedItemsList);
            }

            base.EndInit();
        }
    }
}

In the ViewModel we add a property of type IList to bind the SelectedItemsList dependency property to.

public IList EntryElements
{
    get
    {
        return ShaftProtocol.Data.EntryElements?.ToList();
    }
    set
    {
        ShaftProtocol.Data.EntryElements = null != value ? new List<EntryElement>(value.Cast<EntryElement>()) : new List<EntryElement>();
        RaisePropertyChangedEvent(nameof(EntryElements));
    }
}

In the corresponding View (XAML) the CustomListBox control can be used as follows.

<Page x:Class="biz.dfch.CS.ArbitraryWpfApplication.UI.Pages.EditShaftProtocol"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:ext="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.Extensions"
      xmlns:sew="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.Domain.Sewer"
      xmlns:cc="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.Controls"
      xmlns:p="clr-namespace:biz.dfch.CS.ArbitraryWpfApplication.UI.Properties"
      mc:Ignorable="d" 

      Title="{x:Static p:Resources.Page_EditShaftProtocol_Title}" >

    <DockPanel>
        <cc:CustomListBox Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" MinWidth="200" HorizontalAlignment="Left" VerticalAlignment="Top" SelectionMode="Multiple" ItemsSource="{Binding Source={ext:EnumBindingSource {x:Type sew:EntryElement}}}" SelectedItemsList="{Binding EntryElements, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />
    </DockPanel>
</Page>
« WPF Series -8- CustomDataGrid WPF Series -10- InkCanvas with initial StrokeCollection »

3 Comments »

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.