简体   繁体   中英

Listbox Trouble with Binding to ItemSource using a ObservableCollection

I am having trouble binding to the ItemsSource of a List box control. I would like to be able to add text lines to the List box when the user preforms certain actions.

The SystemControls.xmal Code:

<ListBox Grid.Column="4"  Grid.Row="1" Grid.RowSpan="9" ItemsSource="{Binding ListBoxInput}" Height="165" HorizontalAlignment="Left" Name="listBox1" VerticalAlignment="Top" Width="250" ></ListBox>

The SystemControls.xmal.cs code snippet:

public partial class SystemControls : UserControl, ISystemControls
{
    IDriver _Driver;
    ISystemControls_VM _VM;
    public SystemControls(IDriver InDriver, ISystemControls_VM InVM)
    {
        _VM = InVM;
        _Driver = InDriver;
        DataContext = new SystemControls_VM(_Driver);
        InitializeComponent();
    }

The SystemControls_VM.cs This should be where the heart of the problem is. I have gotten it to work in the constructor, when i try to add lines later in the code, for example when a user press a button, it does nothing:

public class SystemControls_VM:ViewModelBase, ISystemControls_VM
{
    IDriver _Driver;
    public ObservableCollection<string> _ListBoxInput = new ObservableCollection<string>();


    public SystemControls_VM(IDriver InDriver)
    {
        _Driver = InDriver;

        ListBoxInput.Add("test");//Works here
    }

    public ObservableCollection<string> ListBoxInput
    {
        get 
        { 
            return _ListBoxInput; 
        }
        set
        {
            _ListBoxInput = value;
            //OnPropertyChanged("ListBoxInput");
        }
    }




    public void OnButtonClickGetNextError()
    {
        ListBoxInput.Add("NextErrorClicked");//Does not work here                
    }

    public void OnButtonClickClear()
    {
        ListBoxInput.Clear();//Or Here
    }

Also in case it's needed the OnPropertyChangedEventHandler:

namespace XXX.BaseClasses.BaseViewModels
{
    /// <summary>
    /// Provides common functionality for ViewModel classes
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
    public event PropertyChangedEventHandler PropertyChanged = delegate{};

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    }    
}

1) Your public property is called _ListBoxInput but you're binding to ListBoxInput (no underscore) . Make _ListBoxInput private.

2) Because the collection is already observable, you don't need the OnPropertyChanged for your listbox to update.

3) It looks like something might be off with the way you're managing your public vs private ListBoxInput collections. You're calling .Add on your public property (which will immediately raise an event on the observable collection) but then you'll end up adding it to the private collection as well, and then you're calling PropertyChanged on the public property. It's confusing: try my code below and see how it works. (Note in your constructor you add to _ListBoxInput but in your button click event you add to ListBoxInput.)

4) Try adding this.DataContext = this in your constructor

 public partial class MainWindow : Window { public ObservableCollection<string> ListBoxInput { get; private set; } public MainWindow() { InitializeComponent(); this.ListBoxInput = new ObservableCollection<string>(); this.DataContext = this; } private void AddListBoxEntry_Click(object sender, RoutedEventArgs e) { this.ListBoxInput.Add("Hello " + DateTime.Now.ToString()); } } 

and in the xaml, take a look at the binding Mode .

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <ListBox ItemsSource="{Binding ListBoxInput, Mode=OneWay}"
             Height="165" HorizontalAlignment="Left" 
             Name="listBox1" VerticalAlignment="Top" Width="250"  />

    <Button Grid.Column="1" Grid.Row="0" Name="AddListBoxEntry" 
             Margin="0,0,0,158" Click="AddListBoxEntry_Click" >
             <TextBlock>Add</TextBlock>
   </Button>
</Grid>

5) On a separate note, here's another way you could do your INotifyPropertyChanged (I find this cleaner)

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate{};

    protected void OnPropertyChanged(string propertyName)
    {
          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

So got the answer from another source, but figured I would post it here for referance.

So what was happening was that I was setting the data context to one instance of SystemControls_VM while my _VM referance which was handling the button click was going to another instance of SystemControls_VM. That was also why it looked like the button click was working and the List was being populated but no data was getting to the Control itself

I changed the following section of code and it works:

public partial class SystemControls : UserControl, ISystemControls
{
    IDriver _Driver;
    SystemControls_VM _VM;
        public SystemControls(IDriver InDriver, SystemControls_VM InVM)
        {
            _VM = InVM;
            _Driver = InDriver;
            DataContext = InVM;//new SystemControls_VM(_Driver);
            InitializeComponent();
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM