81

I am trying to bind a list of string values to a listbox so that their values are listed line by line. Right now I use this:

<ListBox Margin="20" ItemsSource="{Binding Path=PersonNames}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Id}"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

But I don't know what I am supposed to put into the textblock, instead of Id, since they are all string values, not custom classes.

Also it complains not having to find the PersonNames when I have it inside MainPage, as MainPage.PersonNames.

I set the data context to:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

I am doing it wrong?

Joan Venge
  • 315,713
  • 212
  • 479
  • 689

4 Answers4

168

If simply put that your ItemsSource is bound like this:

YourListBox.ItemsSource = new List<String> { "One", "Two", "Three" };

Your XAML should look like:

<ListBox Margin="20" Name="YourListBox">
    <ListBox.ItemTemplate> 
        <DataTemplate> 
            <StackPanel Orientation="Horizontal"> 
                <TextBlock Text="{Binding}" /> 
            </StackPanel> 
        </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

Update:

This is a solution when using a DataContext. Following code is the viewmodel you will be passing to the DataContext of the page and the setting of the DataContext:

public class MyViewModel
{
    public List<String> Items
    {
        get { return new List<String> { "One", "Two", "Three" }; }
    }
}

//This can be done in the Loaded event of the page:
DataContext = new MyViewModel();

Your XAML now looks like this:

<ListBox Margin="20" ItemsSource="{Binding Items}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

The advantage of this approach is that you can put a lot more properties or complex objects in the MyViewModel class and extract them in the XAML. For example to pass a List of Person objects:

public class ViewModel
{
    public List<Person> Items
    {
        get
        {
            return new List<Person>
            {
                new Person { Name = "P1", Age = 1 },
                new Person { Name = "P2", Age = 2 }
            };
        }
    }
}

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

And the XAML:

<ListBox Margin="20" ItemsSource="{Binding Items}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Name}" />
                <TextBlock Text="{Binding Path=Age}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
starball
  • 20,030
  • 7
  • 43
  • 238
Abbas
  • 14,186
  • 6
  • 41
  • 72
  • 1
    Thanks, I am confused by YourListBox. Is it the name of the control? Do you have to the first line in code for the xaml to work? – Joan Venge Feb 22 '12 at 09:07
  • 1
    That was a typo, it is indeed the Name of the Control. You can also bind the items through a DataContext on your page or through Resources. The binding I gave was for demo-purposes. :) – Abbas Feb 22 '12 at 09:09
  • Thanks, can you please give me an example for the DataContext? I am having a hard time understanding it. – Joan Venge Feb 22 '12 at 09:13
  • Helpful answer :) but as far as I noticed it is not necessary to use `ListBox.ItemTemplate` at all if you use only string instead of complex class. default behaviour just prints it. – pt12lol Feb 18 '15 at 10:08
  • @pt12lol You're right but with expansion (like using a complex object) in mind I used the template. – Abbas Feb 18 '15 at 11:03
  • It's not intuitive and cannot be selected at design time. I've yet to develop anything with xaml without having to come on to SO to find out why it not working. – Paul McCarthy Mar 20 '20 at 15:14
21

You should show us the code for PersonNames, and I am not sure I understand your question, but maybe you want to bind it like this:

<TextBlock Text="{Binding Path=.}"/>

or

<TextBlock Text="{Binding}"/>

This will bind to the current element in the list (assuming PersonNames is a list of strings). Otherwise, you will see the class name in the list.

Ifthel
  • 23
  • 1
  • 1
  • 5
mindandmedia
  • 6,800
  • 1
  • 24
  • 33
9

If the items source is enumerable as string-entries, use the following:

<TextBlock Text="{Binding}"></TextBlock> 

You can use this syntax on any object. Generally, the ToString() -method will then called to get the value. This is in many cases very handy. But beware that no change notification will occur.

HCL
  • 36,053
  • 27
  • 163
  • 213
  • Thanks, I will try this but I am still having an issue with the binding part where it complains not finding PersonNames in xaml. – Joan Venge Feb 22 '12 at 09:10
  • @Joan Venge: THe binding issue is another problem and not explained rapidly in a comment. I assume that the DataContext of your UserControl or Window is not set to the PersonNames-exposing instance. If it is declared in the Window or UserControl, you can add for a fast solution the following into the binding {Binding PersonNames,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}} where you have to replace "Window" through "UserControl" if your ListBox is within a UserControl. Check out some posts about DataContext and MVVM, this will help you understand the DataContext – HCL Feb 22 '12 at 09:21
6

You can do this without having to explicitly define the TextBlock control as a part of your ListBox (unless you want better formatting). The trick to getting the binding to trigger is using an ObservableCollection<string> instead of List<string>

Window1.xaml

<ListView Width="250" Height="50" ItemsSource="{Binding MyListViewBinding}"/>

Window1.xaml.cs

public Window1()
{
   InitializeComponent();
   DataContext = this;

   // Need to initialize this, otherwise you get a null exception
   MyListViewBinding = new ObservableCollection<string>();
}

public ObservableCollection<string> MyListViewBinding { get; set; }

// Add an item to the list        
private void Button_Click_Add(object sender, RoutedEventArgs e)
{
   // Custom control for entering a single string
   SingleEntryDialog _Dlg = new SingleEntryDialog();

   // OutputBox is a string property of the custom control
   if ((bool)_Dlg.ShowDialog())
      MyListViewBinding.Add(_Dlg.OutputBox.Trim());
}
Kevin K
  • 375
  • 6
  • 8