How to Bind to ListBox.ItemsSource inside UserControl

Summary

The post describes two ways of WPF data binding to elements inside WPF user control. Example shows how to establish WPF user control data binding by:

Complete source code is available for download here.

Problem

While working on my project, I needed identical controls and functionality on several windows. The solution was to create WPF user control and use it in all windows.

Control I created contains ListBox , button, textbox and few other controls (panel, border). Creating this user control was easy – define the layout and add standard WPF controls. Step two is using this custom control in windows and other user controls.

Adding user control to other window or user controls is simple:

  • add a reference to a namespace (and assembly if the user control is not in the same assembly as Window where it will be used)
  • add a control to XAML

So, I added my new user control to the window, and next step is to define binding. Type ItemsSource=”{Binding myList}” and the red squiggly line immediately appeared, notifying me that there is no  ItemsSource in my user control.

Binding to Dependency Property in User Control

My user control is derived from UserControl , which does not have ItemsSource property. The first idea is to create a property on user control that exposes ItemsSource property.  The user control will have dependency property of type IEnumerable for ItemsSource, and listbox will have data binding that binds to this property. Consumers of this user control will then be able to bind to this property in XAML, just like standard listbox.

Binding to DataContext of User Control

While reading through UserControl properties, I noticed that I forgot about DataContext property. Second way can be to assign DataContext to my control and then bind ListBox inside my user control. Consumer will bind list to user control DataContext and inside user control, listbox will bind to that DataContext

User Control Example

I decided to test both of solutions to see which one feels better. For test, I created simple user control that contains two listboxes, one will be bound to DataContext, other to dependency property. Here is the code:

<UserControl x:Class="ListboxInUserControlDemo.ListboxInUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="myUserControl"
Height="300" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="10"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Path=.}" Grid.Column="0"/>
<ListBox x:Name="listBox2"
ItemsSource="{Binding ElementName=myUserControl, Path=MyItemsSource}"
Grid.Column="2"/>
</Grid>
</UserControl>

In above code, first listbox have ItemsSource bound to current DataContext. Specification Path=. is used to bind to current source (see Binding.Path Property for details). Specifying Path=. is not mandatory, it could be written like this: ItemsSource="{Binding}". I put it here just to remind myself what Path=. actually means.
The second listbox gets ItemsSource from dependency property MyItemsSource. Since ItemsSource is bound to property of this user control, there must be specified element whose property is bound to listbox, so there is: ElementName=myUserControl.

Most of the code behind is code needed to define dependency property:

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

namespace ListboxInUserControlDemo
{
/// <summary>
/// Interaction logic for ListboxInUserControl.xaml
/// </summary>
public partial class ListboxInUserControl : UserControl
{
public static readonly DependencyProperty MyItemsSourceProperty;

static ListboxInUserControl()
{
ListboxInUserControl.MyItemsSourceProperty =
DependencyProperty.Register("MyItemsSource",
typeof(IEnumerable), typeof(ListboxInUserControl));
}

public IEnumerable MyItemsSource
{
get
{
return (IEnumerable)GetValue(ListboxInUserControl.MyItemsSourceProperty);
}
set
{
SetValue(ListboxInUserControl.MyItemsSourceProperty, value);
}
}

public ListboxInUserControl()
{
InitializeComponent();
}
}
}

Consuming User Control in Window

Now we have user control defined. Next step is to put it into window and test behavior. To see difference between binding methods, example will contain two copies of the user control. One will bind to DataContext, while second will bind to dependency property. On each copy of user control, one listbox will show data, while other will be empty.

<Window x:Class="ListboxInUserControlDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lbUserControl="clr-namespace:ListboxInUserControlDemo"
Title="Window1" Height="400" Width="550"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TabControl>
<TabItem Header="DataContext binding" >
<lbUserControl:ListboxInUserControl DataContext="{Binding TestList}"/>
</TabItem>
<TabItem Header="Dependency Property Binding">
<lbUserControl:ListboxInUserControl MyItemsSource="{Binding TestList}"/>
</TabItem>
</TabControl>
</Grid>
</Window>

Window contains TabControl with two TabItems. Each TabItem have one ListboxInUserControl.

There is nothing special in code behind of this window, standard constructor and initialization for TestList property:

using System.Collections.Generic;
using System.Windows;

namespace ListboxInUserControlDemo
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}

List<string> _testList;
public List<string> TestList
{
get
{
if (_testList == null)
InitList();
return _testList;
}
}

private void InitList()
{
_testList = new List<string>();
_testList.Add("Lorem ipsum dolor sit amet,");
_testList.Add("consectetur adipiscing elit. ");
_testList.Add("Duis mollis egestas ornare. ");
_testList.Add("Cras diam est, sollicitudin ");
_testList.Add("quis vulputate sed, lacinia.");
_testList.Add("Phasellus lorem nunc, fringilla eget posuere");
_testList.Add("sed, tincidunt nec felis. Ut non dui ut");
_testList.Add("metus auctor scelerisque quis ornare leo.");
_testList.Add("Integer eget purus tellus. Nullam ut urna dui.");
_testList.Add("Quisque sed enim magna, sed.");
}
}
}


So, let’s see what happens when I run this code.
The first user control on first TabItem binds to DataContext to TestList the property of a window. Left listbox in user control binds to that DataContext and it will show contents of TestList. Right listbox will be empty since there is no binding to the dependency property MyItemsSource.
Second user control binds dependency property MyItemsSource to TestList property of the window but does not bind DataContext. On this tab left listbox will be empty, and right listbox will show contents of TestList.

There it is. Two ways to databind to ListBox inside the user control. This, of course, works for all types of controls.
I prefer databinding with DataContext. Defining DependencyProperty adds lots of code in codebehind but does not save anything when you consume UserControl.

The complete code is available for download here.

How to Attach to MouseDown Event on ListBox

I wanted to create drag and drop functionality between two list boxes – user should be able to drag items from one ListBox to another.
For test, I created MouseDown event handler for list boxes. Here is XAML for window with two list boxes side by side:

<Window x:Class="ListBoxMouseEvents.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="316" Width="655">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="20"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox x:Name="listBox1" Mouse.MouseDown="ListBox_MouseDown">
            <ListBoxItem>Lorem ipsum dolor sit amet,</ListBoxItem> 
        </ListBox>
        <ListBox Grid.Column="2" x:Name="listBox2" Mouse.MouseDown="ListBox_MouseDown">
            <ListBoxItem>Lorem ipsum dolor sit amet,</ListBoxItem>
        </ListBox> 
    </Grid>
</Window>

In codebehind, I added simple handler for the MouseDown events on both listboxes. Handler should popup messagebox with name of the listbox that triggered the event.

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void ListBox_MouseDown(Object sender, MouseButtonEventArgs e)
        {
            ListBox listBox = (ListBox)sender;
            MessageBox.Show("MouseDown event on " + listBox.Name);
        }
    }

Press F5 and test. Clicked in the middle of the left listbox – MesasgeBox pops up with correct name. Same for right listbox. Then I accidentally clicked on that one item in the listbox and nothing happened. Item was selected, but no MessageBox. Obviously, ListBoxItem class handled event and it stopped propagating.

How routed events work in WPF

MouseDown is, like all mouse events in WPF, implemented as RoutedEvent. Mechanics of routed events is described in MSDN article: Routed Events OverView.
In WPF, events can be routed in three ways: bubbling, tunneling and direct routing. Direct routing is not interesting because event fires only in control where it originated. Bubbling and tunneling routing propagate event through the visual tree.

  • Bubbling routes event from control where event was invoked to successive parent elements until it reaches the root.
  • Tunneling routes event in opposite direction, starting with element tree root and propagates until it reaches control where it was invoked. These are called “Preview” events.

Bubbling and tunneling events are often implemented in pairs. Tunneling events are always invoked first, before corresponding bubbling event.
MouseDown is routed event that is routed by bubbling and his corresponding tunneling or preview event is PreviewMouseDown.

Routing MouseDown event in ListBox

ListBox control contains ItemsPresenter which contains ListBoxItems (there are other controls in between like border and scrollviewer, but they could be abstracted here because they by default handle MouseDown event same way as ItemsPresenter ). When I click inside ListBox, but not on ListBoxItem, MouseDown event is routed like this:

  • Event originates in ItemsPresenter which is first control to receive event. Default handler does not set Handled property to true and event is propagated to its parents, who also do not change Handled property, until it reaches the ListBox.
  • ListBox finds ListBox_MouseDown handler and it invokes it. Code shows MessageBox and does not change Handled property so event can be propagated further. If Window had MouseDown event handler, it would be invoked.

When I click on ListBoxItem inside ListBox following happens:

  • ListBoxItem is first control that receives event. It’s MouseDown handler have code that deals with the selected item inside ListBox (sets properties related to selection and changes visual appearance). Finally it sets Handled property to true. This suggests that event does not need further handling.

Work around: how to handle ‘handled’ routed events

Fortunately, there are two ways to work around this situation:

  • Use the “Preview” event which happens before the bubbling event. This event can be added in XAML. When using this approach, you should be careful not to set Handled property to true, because it will prevent bubbling events to be invoked.
  • Add handler by using the handledEventsToo signature of AddHandler(RoutedEvent, Delegate, Boolean). Limitation of this workaround is that it can be set only in code, not in XAML.

More details on this can be found in last section of MSDN article Marking Routed Events as Handled, and Class Handling.
Finally, here is the code that uses both approaches – one ListBox have preview event attached, while the other has handler added in code.
XAML:

<Window x:Class="ListBoxMouseEvents.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="316" Width="655">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="20"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox x:Name="listBox1" Mouse.PreviewMouseDown="ListBox_MouseDown">
            <ListBoxItem>Lorem ipsum dolor sit amet,</ListBoxItem> 
            <ListBoxItem>consectetur adipiscing elit. </ListBoxItem>
            <ListBoxItem>Duis mollis egestas ornare. </ListBoxItem>
        </ListBox>
        <ListBox Grid.Column="2" x:Name="listBox2" >
            <ListBoxItem>Lorem ipsum dolor sit amet,</ListBoxItem>
            <ListBoxItem>consectetur adipiscing elit.</ListBoxItem>
            <ListBoxItem>Duis mollis egestas ornare.</ListBoxItem>
        </ListBox> 
    </Grid>
</Window>

CodeBehind:

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            // Add handler. Note third parameter: handledEventsToo = true
            listBox2.AddHandler(UIElement.MouseDownEvent, 
                new MouseButtonEventHandler(ListBox_MouseDown), true);
        }

        private void ListBox_MouseDown(Object sender, MouseButtonEventArgs e)
        {
            ListBox listBox = (ListBox)sender;
            this.Title = "MouseDown event on " + listBox.Name;
        }
    }

Note that in this version text is displayed in the title of the window instead of MessageBox. I changed it because showing message box in PreviewMouseDown event was interfering with event propagation and item in left list box was not selected after click – looked like click never happened. It probably has something to do with the fact that there were other mouse events happening when I was closing message box. So, be careful with actions in preview event.

For drag/drop handling I will use second approach – adding handler that receives handled events. Drag and drop functionality needs information about selected item. I could get that myself in preview event, but why, when there is code in event handler of ListBoxItem.