tags:

views:

342

answers:

2

I'm trying to do something very basic here, something I wouldn't have expected to give me this many problems. I have a public property on my main Window class called ItemList which is of type List<string>. I add to this list throughout the life of the program, and would like the ListBox control I have on my form to automatically update when I add new items to the ItemList property.

So far, I have the following XAML:

<Window x:Class="ElserBackupGUI.Main"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Backup Profile Editor [New Profile]" Height="480" Width="640">
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Open"/>
            </MenuItem>
        </Menu>
        <StackPanel Orientation="Vertical" DockPanel.Dock="Top" Margin="10 10 10 3">
            <TextBlock>Items to backup:</TextBlock>
        </StackPanel>
        <DockPanel DockPanel.Dock="Bottom" Margin="10 0 10 10">
            <StackPanel Orientation="Horizontal">
                <Button Name="AddDirectoryButton" Height="22.725" Width="120" Margin="0 0 6 0" Click="AddDirectoryButton_Click">Add Directory...</Button>
                <Button Name="AddFileButton" Height="22.725" Width="90" Margin="0 0 6 0" Click="AddFileButton_Click">Add File...</Button>
                <Button Name="RemoveDirectoriesButton" Height="22.725" Width="75.447" Margin="0 0 6 0">Remove</Button>
            </StackPanel>
        </DockPanel>
        <ListBox Name="SelectedItems" Margin="10 0 10 10" ItemsSource="{Binding Path=ItemList}"/>
    </DockPanel>
</Window>

The relevant code-behind code looks like:

public partial class Main : Window
{
    private string _lastFolder = string.Empty;
    private ObservableCollection<string> _itemList = new ObservableCollection<string>();

    public ObservableCollection<string> ItemList {
        get { return _itemList ?? (_itemList = new ObservableCollection<string>()); }
        set { _itemList = value; }
    }

    public Main()
    {
        InitializeComponent();
        ItemList.Add("test item");
        DataContext = this;
    }

    private void AddDirectoryButton_Click(object sender, RoutedEventArgs e)
    {
        FolderBrowserDialog dialog = new FolderBrowserDialog();
        if (!string.IsNullOrEmpty(_lastFolder))
            dialog.SelectedPath = _lastFolder;

        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            _lastFolder = dialog.SelectedPath;
            ItemList.Add(dialog.SelectedPath);
        }
    }

    private void AddFileButton_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog();
        if (!string.IsNullOrEmpty(_lastFolder))
            dialog.InitialDirectory = _lastFolder;

        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            _lastFolder = System.IO.Path.GetDirectoryName(dialog.FileName);
            SelectedItems.ItemsSource = null;
            ItemList.Add(dialog.FileName);
        }
    }
}

I'm relatively new to WPF and most of the tutorials seem overly complicated for such a simple problem. I can't seem to get anywhere here - what am I missing?

+4  A: 

You should use BindingList<T> or ObservableCollection<T> instead of List<T>.

The problem is that, for binding to work the way you want, it needs to implement INotifyCollectionChanged or IBindingList. List<T> does not support this.


Edit:

After reviewing your changes, there is still one problem.

In the AddFileButton_Click event handler, remove the following line:

SelectedItems.ItemsSource = null;

It is explicitly removing your binding, and causing the list box to clear. If you remove that, your code should work as-is.

However, I do recommend changing your collection definition and constructor to something more like:

    // No need for the null checking every time, or public setter.  They just cause issues
    public ObservableCollection<string> ItemList
    {
        get;
        private set;
    }

    // Add construction here, now that we're using an auto-prop
    public Main()
    {
        this.ItemList = new ObservableCollection<string>();
        InitializeComponent();
        ItemList.Add("test item");
        DataContext = this;
    }
Reed Copsey
use ObservableCollection rather than BindingList, since its specifically designed for WPF binding scenarios
Thomas Levesque
I mention both, and believe they both provide advantages. I usually agree and recommend ObservableCollection, but if your collection may be used by non-WPF apps, as well, BindingList can provide advantages.
Reed Copsey
I tried changing List<string> to ObservableCollection<string> and nothing changed. It ran fine, but adding items to the collection did not update the listbox control
Chris
Take out the dep. property specifier, as well - it's actually not hooked up to the property correctly, so you're "binding" to the wrong collection. If you use ObservableCollection, you can bind directly to it without the DP.
Reed Copsey
Still no dice. I'm starting to wonder if I don't have something else screwed up somewhere.
Chris
Is the list showing up at all? If so, I recommend editing your question and adding your "current" code (with ObservableCollection<T> and no DP), including the XAML. We can take a look, and probably figure it out. (It's tough to see what's wrong since we're not looking at the current version).
Reed Copsey
If I initialize the collection with some test data, it shows up, but the minute I call a function that tries to add something to the collection, the listbox is cleared. Any thoughts?
Chris
I updated my code in the OP to the current code.
Chris
Any idea why the listbox is emptied when I add 1 or more items to the collection?
Chris
Chris: I just updated my answer - you just need to comment out/delete one line, and your current will work. I added other notes, too, though.
Reed Copsey
DOH! I totally missed that line. That was from earlier when I was troubleshooting and absolutely shouldn't be there. Removing the one line fixed it. Thanks!
Chris
+1  A: 

I don't like this getter... personally. Looks conspicuous.

get { return _itemList ?? (_itemList = new ObservableCollection<string>()); }

You're already initializing the _itemList in the declaration. If you need to do it double time to feel safe, do it in the constructor.

Jacob
I agree - I mentioned that in my latest edit, as well. THat's not causing his problem, though - it's just not helping anything.
Reed Copsey
Yeah the additional code was before I was initializing the list in the declaration. I've removed the redundant code now.
Chris