Billy Pilgrim
Billy Pilgrim

Reputation: 1852

WPF ListBox Binding yields one character per line

I am trying to pound data binding and wpf into my (apparently thick) head. I have a very simple program where I have an ObservableCollection of objects that contain a single property -- a string. When I run it, it presents the listbox as one character per line, and only of the first item.

This must be simple, but I'm stumped. If I remove the 'binding.Path = ...", then it will display "expOne.FNode" on each line (thrice).

Thank you!

Code below: MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace expOne
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    ObservableCollection<FNode> MyFileList = new ObservableCollection<FNode>();
    public MainWindow()
    {
        MyFileList.Add ( new FNode ( "alpha" ) );
        MyFileList.Add ( new FNode ( "bravo" ) );
        MyFileList.Add ( new FNode ( "charlie" ) );
        InitializeComponent();
        Binding binding = new Binding();
        binding.Source = MyFileList;
        binding.Path = new PropertyPath ( "Name" );
        mylistbox.SetBinding ( ListBox.ItemsSourceProperty, binding );
    }
}
}

FNode.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace expOne
{
public class FNode
{
    private string m_name;

    public string Name
    {
        get { return ( m_name ); }
        set { m_name = value; }
    }

    public FNode ( string n )
    {
        m_name = n;
    }

    public FNode()
    {
        m_name = "Bob";
    }
}
}

MainWindow.xaml:

<Window x:Class="expOne.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox Name="mylistbox" HorizontalAlignment="Stretch" FontSize="15" >
            <!--<ListBoxItem Content="First or Last" />-->
        </ListBox> 
    </Grid>
</Window>

Upvotes: 3

Views: 1957

Answers (2)

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131374

That's because you bound the 'Name' property to the ItemsSource of you Listbox, essentially telling the listbox to bind to the characters in the string. The listbox binds to the combination of the Source and Path properties.

To bind the Listbox to the list, just set the source. To display the Name, set the ListBox's DisplayMemberPath property to "Name"

Actually, it's much easier to bind declaratively. You can set the list as the DataContext of the Listbox eg.

ObservableCollection<FNode> MyFileList = new ObservableCollection<FNode>();
public MainWindow()
{
    MyFileList.Add ( new FNode ( "alpha" ) );
    MyFileList.Add ( new FNode ( "bravo" ) );
    MyFileList.Add ( new FNode ( "charlie" ) );
    InitializeComponent();
    mylistbox.DataContext=MyFileList;
}

and then set the binding in XAML:

<ListBox Name="mylistbox" ItemsSource="{Binding}" DisplayMemberPath="Name" />

Data binding allows to you to completely separate the data from your window. In fact, the MVVM style prescribes that your presentation data (the ViewModel) should be a separate class from your window (your View). This way you can bind different data classes to the same view, or different views to the same object.

Assuming you created a class named MyProjectViewModel with Name and Files properties, you could write this:

public MyProjectViewModel TheProject {get;set;}
public MainWindow()
{
    TheProject=new MyProject();

    InitializeComponent();
    this.DataContext=TheProject;
}

and then bind any elements you need to this ViewModel's properties, eg:

<TextBox Name="projectName" Text="{Binding Name}" />
<ListBox Name="projectFiles" ItemsSource="{Binding Files}" 
                             DisplayMemberPath="Name" />

Upvotes: 4

Rohit Vats
Rohit Vats

Reputation: 81253

No need to set path on binding, just set Source for your binding. Remove this:

binding.Path = new PropertyPath ( "Name" );

OR

Simply omit the binding and directly set ItemsSource on listBox:

MyFileList.Add(new FNode("alpha"));
MyFileList.Add(new FNode("bravo"));
MyFileList.Add(new FNode("charlie"));
InitializeComponent();
mylistbox.ItemsSource = MyFileList;

and set DisplayMemberPath on ListBox to Name otherwise it will call ToString() on your class FNode and will print fully qualified name of your class that's why you seeing expOne.FNode to be print thrice.

<ListBox Name="mylistbox" HorizontalAlignment="Stretch"
         FontSize="15" DisplayMemberPath="Name"/>

Upvotes: 2

Related Questions