0

I would like to bind ObservableCollection with Listbox in WPF application. So when elements in the ObservableCollection will be modified the ListBox will update itself.

There is a public static ObservableCollection<Camera> extension = new ObservableCollection<Camera>(); in class Camera

And the ListBox is in the class MainWindow.xaml

I tried that, but it does not work:

enter image description here

Camera class:

       using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace HomeSecurity {

    public class Camera : INotifyPropertyChanged {
        public static readonly Regex AxisMacPattern = new Regex("00408[Cc][a-zA-Z0-9]{6}");
        public string _IP;
        public string IP {
            get {
                return _IP;
            }
            set {

                if (_IP != value) {
                    _IP = value;
                    OnPropertyChanged("IP");
                }

            }
        }
        public string _HTTPPort;
        public string HTTPPort {
            get {
                return _HTTPPort;
            }
            set {

                if (_HTTPPort != value) {
                    _HTTPPort = value;
                    OnPropertyChanged("HTTP");
                }

            }
        }
        public string _MAC;
        public string MAC {
            get {
                return _MAC;
            }
            set {

                if (_MAC != value) {
                    _MAC = value;
                    OnPropertyChanged("MAC");
                }

            }
        }
        public string _ServiceName;
        public string ServiceName {
            get {
                return _ServiceName;
            }
            set {

                if (_ServiceName != value) {
                    _ServiceName = value;
                    OnPropertyChanged("ServiceName");
                }

            }
        }
        public string _FullName;
        public string FullName {
            get {
                return _FullName;
            }
            set {

                if (_FullName != value) {
                    _FullName = value;
                    OnPropertyChanged("FullName");
                }

            }
        }
        public string _HostName;
        public string HostName {
            get {
                return _HostName;
            }
            set {

                if (_HostName != value) {
                    _HostName = value;
                    OnPropertyChanged("HostName");
                }

            }
        } 

        public Camera() { }
        public Camera(string MAC) : this(null, null, MAC, null, null, null) { }
        public Camera(string MAC, string ServiceName) : this(null, null, MAC, ServiceName, null, null) { }
        public Camera(string IP, string HTTPPort, string MAC, string ServiceName, string FullName, string HostName) {
            this.IP = IP;
            this.HTTPPort = HTTPPort;
            this.MAC = MAC;
            this.ServiceName = ServiceName;
            this.FullName = FullName;
            this.HostName = HostName;
            AddToExtension(this);
        }
        public static ObservableCollection<Camera> _extension = new ObservableCollection<Camera>();
        //

        public ObservableCollection<Camera> extension {
            get { return _extension; }
            set {
                if (_extension != value) {
                    _extension = value;
                    OnPropertyChanged("extension");
                }

            }
        }
        private void OnPropertyChanged(string propertyName) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion


        // 

        public static void AddToExtension(Camera camera) {
            _extension.Add(camera);
        }

        public static void RemoveFromExtension(Camera camera) {
            _extension.Remove(camera);
        }

        public static Camera GetFromExtension(String MAC) {
            foreach (Camera camera in _extension)
                if (camera.MAC.Equals(MAC))
                    return camera;
            return null;
        }

        public static void PrintExtension() {
            foreach (Camera camera in _extension)
                Console.WriteLine(camera);
        }

        public override string ToString() {
            return "IP: " + IP + " HTTP Port: " + HTTPPort + " MAC: " + MAC + " Service Name: " + ServiceName + " FullName: " + FullName + " HostName: " + HostName;
        }
    }
}

XAML:

  <Window x:Class="HomeSecurity.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:HomeSecurity" 
        Title="MainWindow" WindowState="Maximized" Loaded="Window_Loaded"

        >

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*" />
            <RowDefinition Height="8*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="1366" />
        </Grid.ColumnDefinitions>
        <Border BorderBrush="Red" BorderThickness="4" Grid.Column="1" Grid.Row="0">
            <ListBox x:Name="CameraListBox"
         ItemsSource="{Binding Path=Camera.extension}">
                <ListBox.Resources>
                    <DataTemplate DataType="{x:Type local:Camera}">
                        <Border BorderBrush="Black" BorderThickness="1" CornerRadius="5">
                           <TextBox Text="Hello World" /> 
                        </Border>
                    </DataTemplate>
                </ListBox.Resources>
            </ListBox>
        </Border>

        <Border BorderBrush="Green" BorderThickness="2" Grid.Column="1" Grid.Row="1">
            <ScrollViewer >
                <WrapPanel x:Name="VideoPanel" >
                </WrapPanel>
            </ScrollViewer>
        </Border>


    </Grid>

</Window>

MainWindow.xaml.cs:

using Bonjour;

namespace HomeSecurity {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged {

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

        //   this.DataContext = this;
        }
       //
        private Camera _camera;
        public Camera Camera
        {
            get { return _camera; }
            set
            {
                if (_camera != value)
                {
                     _camera= value;
                     OnPropertyChanged("Camera");
                }

            }
        }

        /// <summary>
        /// Raises the PropertyChanged notification in a thread safe manner
        /// </summary>
        /// <param name="propertyName"></param>
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
        //
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            createGUI();
        }

        private void createGUI() {
            Console.WriteLine("dupa");
            Scanner.ScanService();
      //      startListening();
            //THIS CODE WON'T RUN BECAUSE   Scanner.ScanService(); have frozen it
            AddVideoStream("192.168.0.2");
            AddVideoStream("192.168.0.2");
            AddVideoStream("192.168.0.2");
        }

        private void startListening() {
            Camera._extension.CollectionChanged += (s, e) => {
             //  CameraListBox.Items.Add(Camera.extension.Last());
            };
        }


        //TEN
        private void AddVideoStream(String sourceIP) {
            int cols = 2;
            int formsHostWidth = (int)(VideoPanel.ActualWidth / cols) - 4;

            WindowsFormsHost formsHost = new WindowsFormsHost();
            VideoStream videoStream = new VideoStream(sourceIP);
            formsHost.Width = formsHostWidth;
            formsHost.Height = videoStream.GetPrefferedHeight(formsHostWidth);
            formsHost.Child = videoStream;
            Border lineBorder = new Border();
            lineBorder.BorderBrush = Brushes.Green;
            lineBorder.BorderThickness = new Thickness(2);
            lineBorder.Child = formsHost;
            VideoPanel.Children.Add(lineBorder);

        }
    }
}

enter image description here

If I change constructor in MAinWindow.xaml.cs to:

  InitializeComponent();
            Camera = new Camera();
            DataContext = this;

I get:

enter image description here

but this is not updated data... it is data frome the time where the Camera object was inserted in the extension.

Yoda
  • 17,363
  • 67
  • 204
  • 344
  • Do you mean sth like this? http://stackoverflow.com/questions/8490533/notify-observablecollection-when-item-changes – Lei Yang Jan 15 '14 at 03:50
  • 1
    You could try adding a dummy object and removing it right away. But I'm not sure exactly what you are trying to accomplish? Have you considered having your UI bounded to the INotifyPropertyChange of the individual objects in your collection instead? – ansible Jan 15 '14 at 03:51
  • @ansible Oh That binding is good. I have ObservableCollection list in class A, and listBox in class B... how to bind those two? – Yoda Jan 15 '14 at 03:55
  • 1
    Just set it to the ListBox's ItemSource to your ObservableCollection, it will listen when items are added/removed. – ansible Jan 15 '14 at 03:59
  • @ansible Please lok at the edit. The binding does not work. When I want to set ItemSource there is only `custom expression` or `create data binding option`. But I can't find `Camera.extension` anywhere int those menus – Yoda Jan 15 '14 at 04:05
  • 1
    What about ``? Assuming your DataContex is correct. I think that's the right syntax, not at my work PC though. – ansible Jan 15 '14 at 04:11
  • @ansible It does not work(the syntax is ok). At the beginning of the program the `Camera.extension` is empty then the items are added after a short time(cameras are detected in the local network and added to `Camera.extension`) and the listbox needs update. How to add to this xaml notify that collection changed or sth like that? – Yoda Jan 15 '14 at 04:19
  • 1
    Does Camera implement INotifyPropertyChanged (and the extension property)? If not you are binding to a null value, and your UI never is updated when the observable collection is created and assigned to extension. – ansible Jan 15 '14 at 04:21
  • @ansible At the beginning thank you for your help. I added to the OP code of Camera class I added `: INotifyPropertyChanged` after your last comment but I do not know how to implement it. – Yoda Jan 15 '14 at 04:26
  • @ansible The extension is in the half of the class. – Yoda Jan 15 '14 at 04:27
  • 1
    You will need to implement that (or set it before `InitalizeComponent()`)- see my answer on this post - https://stackoverflow.com/questions/20664707/how-to-bind-textblock-with-property/20664893#20664893 – ansible Jan 15 '14 at 04:38
  • I did: `DataContext = this;` in MainWindow.xaml.cs but no effect. – Yoda Jan 15 '14 at 04:41
  • @ansible Maybe I am pursuing wrong thing. I want to get change in that lisbox when a property of single object in that extension will be changed – Yoda Jan 15 '14 at 04:43
  • 1
    See my answer - let me know if that makes any sense. – ansible Jan 15 '14 at 04:49
  • @ansible Ok, I did you EVERYTHING that you told me. The updated code is in the original post. There is no results in the listbox at all. Don't know what to do else. – Yoda Jan 15 '14 at 05:52
  • And if you can please look at the end of the post... there is reference to your post. – Yoda Jan 15 '14 at 06:03

2 Answers2

2

Try to create a property that return extension static field, then bind your ListBox to that property. As far as I know you have to bind to property instead of field.

public static ObservableCollection<Camera> extension = new ObservableCollection<Camera>();
public ObservableCollection<Camera> bindableExtension 
{ 
    get { return extension; }
}

UPDATE :

As I can see from update you set DataContext to code behind. This means, you have to create a property named Camera in MainWindow.xaml.cs. Then you have to either implement INotifyPropertyChanged there or initialize Camera property before setting the DataContex :

public MainWindow() {
            InitializeComponent();
            this.Camera = new Camera();
            this.DataContext = this;
        }
...
public Camera Camera { get; set; }
har07
  • 88,338
  • 12
  • 84
  • 137
  • I copied your code and then added: `` in XAML. But no effect. Do I have to do something about `DataContext` or this `INotifyPropertyChanged` ? – Yoda Jan 15 '14 at 04:36
  • 1
    of course you have to set DataContext somewhere (in XAML or c#), and I can't see you did it in the XAML/codes posted in question. – har07 Jan 15 '14 at 04:56
  • In the op is current code. I added that what you posted but the problem is that it works as bad as when I did it by this code: `Camera._extension.CollectionChanged += (s, e) => { CameraListBox.Items.Add(Camera.extension.Last()); };` When the object in the Camera.extension is updated -> field has changed nothing happens. – Yoda Jan 15 '14 at 05:22
  • I did all you and ansible said. Could you please look at the op. Currently nothing goes into listBox. – Yoda Jan 15 '14 at 05:54
2

You don't want to use a static property unless you really want to be shared between all instances of your class.

When your UI is initialized extension will be null. So the binding will be setup to null and nothing will happen. What you need to do is let your UI know when extention is updated, so it can listen for when new objects are added. Does that make sense?

Your example above, the property is not calling PropertyChangedEventArgs when extension is created so it is not actually listening to your collection.

public class Camera : INotifyPropertyChanged
{
        private ObservableCollection<Camera> _extension;
        public ObservableCollection<Camera> extension;
        {
            get { return _extension; }
            set
            {
                if (_extension != value)
                {
                     _extension= value;
                     OnPropertyChanged("extension");
                }

            }
        }

        /// <summary>
        /// Raises the PropertyChanged notification in a thread safe manner
        /// </summary>
        /// <param name="propertyName"></param>
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

}

Also add this to your constructor DataContext = this, and implement INotifyPropertyChanged in your window class also. Something like this

public partial class MainWindow : Window, INotifyPropertyChanged
{
        public MainWindow() {
            DataContext = this;
            InitializeComponent();
        }

        // ... 

        private Camera _camera;
        public Camera Camera;
        {
            get { return _camera; }
            set
            {
                if (_camera != value)
                {
                     _camera= value;
                     OnPropertyChanged("Camera");
                }

            }
        }

        /// <summary>
        /// Raises the PropertyChanged notification in a thread safe manner
        /// </summary>
        /// <param name="propertyName"></param>
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
}

I think you need to have a data template for something to show up too

<ListBox x:Name="CameraListBox"
         ItemsSource="{Binding Path=Camera.extension}">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type local:Camera}">
            <Border BorderBrush="Black" BorderThickness="1" CornerRadius="5">
                <TextBox Text="Hello World" />
            </Border>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>
ansible
  • 3,569
  • 2
  • 18
  • 29
  • I want these elements to be in static collection. In polish it's called "ekstensja klasy" and the closest word to this is "extension of class" this is a collection which has all the instances of particular class inside. – Yoda Jan 15 '14 at 04:51
  • 1
    You can make it static if you want, you just need to make OnPropertyChanged static too. – ansible Jan 15 '14 at 04:57
  • Ok I edited the OP... still no results, but maybe I ommitted something(there are objects in that collection cause when I print it in console I get): `IP: 192.168.0.12 HTTP Port: 9080 MAC: 00408CBEEAE5 Service Name: AXIS M1011-W - 00408CBEEAE5 FullName: AXIS\032M1011-W\032-\03200408CBEEAE5._axis-video._tcp.local. HostName: axis-00408cbeeae5.local.` – Yoda Jan 15 '14 at 04:59
  • Do I suppose to post full xaml and mainwindow? – Yoda Jan 15 '14 at 04:59
  • 1
    You'll need to implement INotifyPropertyChange on the MainWindow also, so it knows when Camera is set. – ansible Jan 15 '14 at 05:08
  • I know that you might get angry on me, maybe it's the time here: 06:09am. But how to do it? Those classes have nothing in common, there is no connection between them. Do I need add a in MainWindow.cs `public ObservableCollection extension; { get { return Camera.extension OR Camera._extension????; } set { if (_extension != value) { _extension= value; OnPropertyChanged("extension"); } } }` I am lost – Yoda Jan 15 '14 at 05:12
  • OK, OK I it worked now when I added `InitializeComponent(); this.Camera = new Camera(); this.DataContext = this;` in the main window constructor. But I am in the same place I started when I binded it by the event. – Yoda Jan 15 '14 at 05:18
  • ListBox is only updated when the item is added to extension. Not when it is modified. – Yoda Jan 15 '14 at 05:18
  • The problem is that now it works the same like when I just added that code: `Camera._extension.CollectionChanged += (s, e) => { CameraListBox.Items.Add(Camera.extension.Last()); };` – Yoda Jan 15 '14 at 05:23
  • 1
    Your UI should be listening to each item and responding to them (via INotifyPropetyChange on Camera). I don't understand why the ListBox needs to be updated directly. Maybe I'm just not understanding the problem? – ansible Jan 15 '14 at 05:23
  • 1
    @Yoda thats because you don't call OnPropertyChanged method in setter of `IP`, `HTTPPort`, `MAC`, and other properties in Camera class – har07 Jan 15 '14 at 05:24
  • No, no it does not have to updated directly. I only say that right now the result is the same as when I did that using this code`Camera._extension.CollectionChanged += (s, e) => { CameraListBox.Items.Add(Camera.extension.Last()); };`..., How to implement this INotify in main window : ( ? – Yoda Jan 15 '14 at 05:25
  • 1
    See my updated answer, I added MainWindow and INotify - you implement it just like we did for camera. And @har07 is right, it needs to be updated for ALL properties that you are using in the UI (even if it's part of an object chain). Otherwise DataBinding won't work properly if any are missing. Basically add `OnPropertyChanged(" – ansible Jan 15 '14 at 05:28
  • Please look at the at of the OP I get Stackoverflow... after adding onproperty change to IP. – Yoda Jan 15 '14 at 05:29
  • 1
    Your window (and Camera property on your window) do not implement INotify, see my updated answer. Also, most of the properties in Camera are not implementing them either. They will need to if shown in your UI. – ansible Jan 15 '14 at 05:30
  • @ansible he adds `DataContext = this;` after `InitializeComponent()` you add it before. Whis one is ok? – Yoda Jan 15 '14 at 05:42
  • 1
    I think you want it before `InitializeComponent()` executes your XAML, so you need your data context setup before then, otherwise your binding will not work as written. – ansible Jan 15 '14 at 05:45
  • 1
    let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/45243/discussion-between-ansible-and-yoda) – ansible Jan 15 '14 at 06:03