使用ItemsSource填充WPF列表框-好主意

本文关键字:好主意 列表 WPF ItemsSource 填充 使用 | 更新日期: 2023-09-27 18:16:03

我是一个(相对)有经验的Cocoa/Objective-C编码器,并且正在自学c#和WPF框架。

在Cocoa中,当填充NSTableView时,将委托和数据源分配给视图相对简单。然后使用这些委托/数据源方法填充表,并确定其行为。

我正在整理一个简单的应用程序,它有一个对象列表,我们称它们为Dog对象,每个对象都有一个public string name。这是Dog.ToString()的返回值

对象将显示在ListBox中,我想使用与Cocoa的NSTableViewDataSource类似的模式来填充这个视图。目前似乎可以使用:

public partial class MainWindow : Window, IEnumerable<Dog>
    {
        public Pound pound = new Pound();
        public MainWindow()
        {
            InitializeComponent();
            Dog fido = new Dog();
            fido.name = "Fido";
            pound.AddDog(fido);
            listBox1.ItemsSource = this;
            Dog spot = new Dog();
            spot.name = "Spot";
            pound.AddDog(spot);
        }
        public IEnumerator<Dog> GetEnumerator()
        {
            return currentContext.subjects.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

但是我想知道如何正确这是。我安装Visual Studio还不到一个小时,所以可以肯定地说,我根本不知道自己在做什么。

    这是正确的模式吗?
  1. 将第二项添加到列表(spot)似乎可以正确地更新ListBox,但我想知道是什么触发了更新?
  2. 如果我在后台线程上更新Pound会发生什么?
  3. 我如何手动要求ListBox更新自己?(我甚至需要吗?)

我知道我需要做的一个更改是将IEnumerable<Dog>实现重构为自己的类,如DogListItemsSource,但我想确保在完善它之前我有一个可靠的方法。

请随时在评论中指出我应该解决或记住的任何其他问题,无论大小。我想第一次用正确的方法学习这个

使用ItemsSource填充WPF列表框-好主意

我的建议是创建一个类除了你的窗口,这将负责提供数据给你的ListBox。一种常见的方法是被称为MVVM的WPF,它像任何模式一样有许多实现。

基础是每个模型(例如PoundDog)都有一个视图模型负责以一种易于与UI交互的方式呈现模型。

为了让您开始,WPF提供了一个优秀的类ObservableCollection<T>,它是一个集合,每当添加,移动或删除任何人时,都会触发"Hey I Changed"事件。

下面是一个不打算教你MVVM的例子,也没有使用任何MVVM的框架。然而,如果你设置一些断点并使用它,你将学习绑定、命令、INotifyPropertyChanged和ObservableCollection;所有这些都在WPF应用程序开发中扮演着重要的角色。

MainWindow开始,您可以将DataContext设置为视图模型:

public class MainWindow : Window
{
     // ...
     public MainWindow()
     {
         // Assigning to the DataContext is important
         // as all of the UIElement bindings inside the UI
         // will be a part of this hierarchy
         this.DataContext = new PoundViewModel();
         this.InitializeComponent();
     }
}

其中PoundViewModel管理DogViewModel对象的集合:

public class PoundViewModel
{
    // No WPF application is complete without at least 1 ObservableCollection
    public ObservableCollection<DogViewModel> Dogs
    {
        get;
        private set;
    }
    // Commands play a large role in WPF as a means of 
    // transmitting "actions" from UI elements
    public ICommand AddDogCommand
    {
        get;
        private set;
    }
    public PoundViewModel()
    {
        this.Dogs = new ObservableCollection<DogViewModel>();
        // The Command takes a string parameter which will be provided
        // by the UI. The first method is what happens when the command
        // is executed. The second method is what is queried to find out
        // if the command should be executed
        this.AddDogCommand = new DelegateCommand<string>(
            name => this.Dogs.Add(new DogViewModel { Name = name }),
            name => !String.IsNullOrWhitespace(name)
        );
    }
}

在你的XAML中(确保映射xmlns:local以允许XAML使用你的视图模型):

<!-- <Window ...
             xmlns:local="clr-namespace:YourNameSpace" -->
<!-- Binding the ItemsSource to Dogs, will use the Dogs property
  -- On your DataContext, which is currently a PoundViewModel
  -->
<ListBox x:Name="listBox1"
         ItemsSource="{Binding Dogs}">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type local:DogViewModel}">
            <Border BorderBrush="Black" BorderThickness="1" CornerRadius="5">
                <TextBox Text="{Binding Name}" />
            </Border>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>
<GroupBox Header="New Dog">
    <StackPanel>
        <Label>Name:</Label>
        <TextBox x:Name="NewDog" />
        <!-- Commands are another big part of WPF -->
        <Button Content="Add"
                Command="{Binding AddDogCommand}"
                CommandParameter="{Binding Text, ElementName=NewDog}" />
    </StackPanel>
</GroupBox>

当然,您需要DogViewModel:

public class DogViewModel : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return this.name; }
        set
        {
            this.name = value;
            // Needed to alert WPF to a change in the data
            // which will then update the UI
            this.RaisePropertyChanged("Name");
        }
    }
    public event PropertyChangedHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

最后,您需要实现DelegateCommand<T>:

public class DelegateCommand<T> : ICommand
{
    private readonly Action<T> execute;
    private readonly Func<T, bool> canExecute;
    public event EventHandler CanExecuteChanged;
    public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
    {
        if (execute == null) throw new ArgumentNullException("execute");
        this.execute = execute;
        this.canExecute = canExecute;
    }
    public bool CanExecute(T parameter)
    {
        return this.canExecute != null && this.canExecute(parameter); 
    }
    bool ICommand.CanExecute(object parameter)
    {
        return this.CanExecute((T)parameter);
    }
    public void Execute(T parameter)
    {
        this.execute(parameter);
    }
    bool ICommand.Execute(object parameter)
    {
        return this.Execute((T)parameter);
    }
}

这个答案绝对不会让你制作出身临其境的、完全绑定的WPF UI,但希望它能让你感受到UI是如何与你的代码交互的!

  1. 在WPF中,通常只有一些集合作为ItemsSource和数据模板来显示项目。

  2. 通常这些控件只有在ItemsSource实例实现INotifyCollectionChanged时才更新,也许你在ListBox检索它之前添加了项目。

  3. 什么是Pound?除非Pound有一些线程亲和性,如ObservableCollection,这是没有问题的,如果它需要使用调度。

  4. ListBox.Items.Refresh()可以这样做,但通常你只使用带有通知的集合。

WPF大量使用数据绑定,因此如果您想了解该框架的相关概述(以及所有其他概述)可能会对您感兴趣。