【问题标题】:How to bind ObservableCollection with Listbox in WPF如何在 WPF 中将 ObservableCollection 与 Listbox 绑定
【发布时间】:2014-01-15 03:35:47
【问题描述】:

我想在 WPF 应用程序中将ObservableCollectionListbox 绑定。因此,当 ObservableCollection 中的元素被修改时,ListBox 将自行更新。

相机类

中有一个public static ObservableCollection<Camera> extension = new ObservableCollection<Camera>();

ListBox 属于 MainWindow.xaml

我试过了,还是不行:

相机类:

       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);

        }
    }
}

如果我将MAinWindow.xaml.cs 中的构造函数更改为:

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

我明白了:

但这不是更新的数据...它是相机对象插入扩展时的数据。

【问题讨论】:

  • 您可以尝试添加一个虚拟对象并立即将其删除。但我不确定你到底想要完成什么?您是否考虑过将您的 UI 绑定到集合中各个对象的 INotifyPropertyChange?
  • 只需将ListBox的ItemSource设置为你的ObservableCollection,它会在添加/删除项目时监听。
  • &lt;ListBox x:Name="CameraListBox" ItemsSource="{Binding Path=Camera.extension}"&gt; 呢?假设您的 DataContext 是正确的。我认为这是正确的语法,但不是在我的工作电脑上。
  • Camera 是否实现 INotifyPropertyChanged(和扩展属性)?如果不是,您将绑定到一个空值,并且在创建可观察集合并将其分配给扩展时,您的 UI 永远不会更新。
  • 您需要实现它(或在InitalizeComponent() 之前设置它)- 请参阅我在这篇文章中的回答-stackoverflow.com/questions/20664707/…

标签: c# events observablecollection


【解决方案1】:

你不想使用静态属性,除非你真的想在你的类的所有实例之间共享。

当您的 UI 初始化时,扩展名将为空。因此绑定将设置为 null 并且不会发生任何事情。您需要做的是让您的 UI 知道扩展何时更新,以便它可以在添加新对象时进行监听。这有意义吗?

您在上面的示例中,该属性在创建扩展时没有调用 PropertyChangedEventArgs,因此它实际上并没有监听您的集合。

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

}

还将它添加到您的构造函数DataContext = this,并在您的窗口类中实现 INotifyPropertyChanged。像这样的

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
}

我认为你也需要有一个数据模板才能显示出来

<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>

【讨论】:

  • 您可以根据需要将其设为静态,您只需要将 OnPropertyChanged 也设为静态即可。
  • 您还需要在 MainWindow 上实现 INotifyPropertyChange,以便它知道何时设置了 Camera。
  • 您的 UI 应该监听每个项目并响应它们(通过相机上的 INotifyPropetyChange)。我不明白为什么 ListBox 需要直接更新。也许我只是不明白这个问题?
  • @Yoda 那是因为您没有在 IPHTTPPortMAC 和 Camera 类中的其他属性的设置器中调用 OnPropertyChanged 方法
  • 查看我更新的答案,我添加了 MainWindow 和 INotify - 你实现它就像我们为相机所做的那样。 @har07 是对的,它需要针对您在 UI 中使用的所有属性进行更新(即使它是对象链的一部分)。否则,如果缺少任何数据绑定,DataBinding 将无法正常工作。基本上在任何属性更改时添加OnPropertyChanged("&lt;PROPERTY NAME");
【解决方案2】:

尝试创建一个返回 extension 静态字段的属性,然后将您的 ListBox 绑定到该属性。据我所知,您必须绑定到属性而不是字段。

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

更新:

正如我从更新中看到的,您将 DataContext 设置为后面的代码。这意味着,您必须在 MainWindow.xaml.cs 中创建一个名为 Camera 的属性。然后,您必须在那里实现 INotifyPropertyChanged 或在设置 DataContex 之前初始化 Camera 属性:

public MainWindow() {
            InitializeComponent();
            this.Camera = new Camera();
            this.DataContext = this;
        }
...
public Camera Camera { get; set; }

【讨论】:

  • 我复制了您的代码,然后在 XAML 中添加了:&lt;ListBox x:Name="CameraListBox" ItemsSource="{Binding Path=Camera.bindableExtension}" /&gt;。但是没有效果。我必须对DataContext 或这个INotifyPropertyChanged 做点什么吗?
  • 当然,您必须在某处设置 DataContext(在 XAML 或 c# 中),我看不到您在相关的 XAML/代码中执行此操作。
  • 在操作中是当前代码。我添加了您发布的内容,但问题是它的工作方式与我通过以下代码执行时一样糟糕:Camera._extension.CollectionChanged += (s, e) =&gt; { CameraListBox.Items.Add(Camera.extension.Last()); }; 当 Camera.extension 中的对象更新时 -> 字段已更改,没有任何反应。
  • 我做了你和ansible所说的一切。请你看一下op。目前没有任何内容进入 listBox。
猜你喜欢
  • 2013-12-31
  • 2015-08-04
  • 1970-01-01
  • 2016-09-01
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多