0

I am having trouble getting a ListBox binding to work as expected. I'm currently attempting to bind a ListBox to a singleton exposed ObservableCollection of items. The items are a separate class themselves. Currently, I am binding like this:

<Window x:Class="toxySharp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:classes="clr-namespace:MyApplication.Classes"
        Title="MainWindow" Height="325" Width="400"
        DataContext="{Binding Source={x:Static local:SingletonClass.Instance}}">

        <Grid x:Name="LayoutRoot">
            <ListBox x:Name="lstMyList" ItemsSource="{Binding Path=Objects, Mode=TwoWay}" DisplayMemberPath="Name" />
        </Grid>
</Window>

My singleton is a basic implementation like this:

public class SomeObject : INotifyPropertyChanged
{
    private Int32 m_vId;
    private String m_vName;

    public SomeObject() { }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String propName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

    public Int32 Id
    {
        get { return this.m_vId; }
        set { this.m_vId = value; NotifyPropertyChanged("Id"); }
    }

    public String Name
    {
        get { return this.m_vName; }
        set { this.m_vName = value; NotifyPropertyChanged("Name"); }
    }
}

public class SingletonClass : INotifyPropertyChanged
{
    private static SingletonClass m_vInstance;
    private ObservableCollection<SomeObject> m_vObjects;

    private SingletonClass()
    {
        this.m_vObjects = new ObservableCollection<SomeObject>();
        for (int x = 0; x < 255; x++)
            this.m_vObjects.Add(new SomeObject() { Id = x, Name = String.Format("{0} - new object", x) });
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String propName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }

    public static SingletonClass Instance
    {
        get
        {
            if (m_vInstance == null)
                m_vInstance = new SingletonClass();
            return m_vInstance;
        }
    }

    public ObservableCollection<SomeObject> Objects
    {
        get { return this.m_vObjects; }
        set { this.m_vObjects = value; NotifyPropertyChanged("Objects"); }
    }
}

Currently, the binding works on startup. The application will bind and properly show the names of each object. For example this is a test app doing the same implementation: enter image description here

In my main actual application I have async methods being called (Socket stuff BeginConnect, BeginSend, etc.) that use callbacks that can update the collection. (It's a list of players so when certain packets are received the list is updated with their data.)

My problem is when the collection is updated inside one of the async callbacks it doesn't update on the list. The collection data is updated properly, setting a break in the main code anywhere shows the collection being updated but the listbox never updates to reflect the changes. So it just stays saying the same thing no matter what.

Did I overlook something?

I've tried using a CollectionViewSource as well to allow filtering and that has the same problem.

== EDIT ==

I've found the problem which lies in the singleton with how the collection is initialized. Instead of using the internal copy member when initializing the collection, I needed to use the exposed property to allow it to update the UI.

So using the following fixed it:

private SingletonClass()
{
    this.Objects = new ObservableCollection<SomeObject>();
    for (int x = 0; x < 255; x++)
        this.Objects.Add(new SomeObject() { Id = x, Name = String.Format("{0} - new object", x) });
}

However now that the list binding works I want to be able to filter this based on another property inside the object class. (In the example SomeObject). I have a boolean stating if the object is active. Trying to bind to a CollectionViewSource leads me back to the not updating problems. So is there a way to filter this manually and keep the ui updated?

H.B.
  • 166,899
  • 29
  • 327
  • 400
atom0s
  • 485
  • 6
  • 22

2 Answers2

2

My problem is when the collection is updated inside one of the async callbacks it doesn't update on the list.

Well thats the problem isnt it! Observable collections are not thread safe. You need to make them that way.

No TwoWay binding mode or UpdateSourceTrigger=PropertyChanged will help in this case as the problem lies with multi threading in your code...

Use this custom implementation of thread safe and faster observable collection for your ease...

Fast performing and thread safe observable collection

As far as INotifyPropertyChanged interface is concerned, its 'PropertyChangedevent is automatically dispatched to the UI thread. So any multithreaded context updating the properties of a class that implementsINotifyPropertyChanged` will update the GUI.

Let me know if this helps,

Community
  • 1
  • 1
WPF-it
  • 19,625
  • 8
  • 55
  • 71
  • There are no thread problems, I make sure that anything in the callbacks are invoked onto the main thread to make sure that threading problems do not happen. – atom0s Feb 08 '12 at 05:46
  • Edited the main question, I found the cause of the update problem. But now the question is how to sort it since binding to a CollectionViewSource causes the not updating problems again. – atom0s Feb 08 '12 at 05:53
  • Can you bind the collection view (`CollectionViewSource.DefaultView`) to the listbox instead of the collection itself? – WPF-it Feb 08 '12 at 06:03
  • 1
    I don't see a way to do this within the Xaml since I don't want to rely on the code-behind for this. Do you have an example of this? – atom0s Feb 08 '12 at 06:28
  • Probably not the best solution but I gave up trying to use the CollectionViewSource. Instead I'm just defining a custom template and setting the visibility of the item based on the Active property I wanted to use with the filtering. This is working as desired, except I wish I could have gotten to this to work with the proper things exposed to me. – atom0s Feb 08 '12 at 07:30
  • @atom0s, you can use the XAML binding via a converter to do so. The converter would return the CollectionViewSource.DefaultView for the incoming collection. – WPF-it Feb 08 '12 at 08:42
0

UpdateSourceTrigger=PropertyChanged is missing

<ListBox x:Name="lstMyList" ItemsSource="{Binding Path=Objects,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" DisplayMemberPath="Name" />
Anton
  • 7,709
  • 5
  • 31
  • 33
  • 1
    This does nothing at all, ItemsSource is never changed on the target, it will not propagate back to the source in any case. – H.B. Feb 08 '12 at 08:21
  • if im understanding the OP correctly, then its the source that is being updated, but the view isnt seeing it. which needs to have an updatedsourcetrigger=propertychanged to fix it. If it does nothing, then let me know what your definition of "source" is. in my head it means the observablecollection on the viewmodel – Anton Feb 09 '12 at 02:05
  • 3
    No, `UpdateSourceTrigger` goes **the other way**, e.g. in a `TextBox` when someone enters text it determines if the source to which `Text` is bound to should be updated on every key-stroke. `ItemsControls` do not change the `ItemsSource` property and it cannot be changed by user-input either, `UpdateSourceTrigger` will never do anything. – H.B. Feb 09 '12 at 02:08