使用ItemsSource来填充WPF ListBox - 是一个好主意吗?

5

我是一名(相对)经验丰富的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. 这是正确的模式吗?
  2. 将第二个项目(spot)添加到列表中似乎会正确更新ListBox,但我想知道是什么触发了更新?
  3. 如果我在后台线程上更新Pound会发生什么?
  4. 如何手动请求ListBox更新自身?(我需要吗?)

我知道我需要做出的一个更改是将IEnumerable<Dog>实现重构为自己的类,比如DogListItemsSource,但我想确保我有一个扎实的方法在进行优化之前。

请随意在评论中指出任何其他我应该注意或需要解决的问题,无论大小。我希望第一次就学会正确的方法。

2个回答

13

我的建议是在你的窗口之外创建一个类,该类负责为你的ListBox提供数据。WPF中常用的一种方法称为MVVM,就像任何模式一样,有许多实现方式。

基本原则是每个模型(例如PoundDog)都会有一个视图模型,负责以易于从UI交互的方式呈现模型。

为了让你开始工作,WPF提供了一个很好的类,ObservableCollection<T>,它是一个集合,每当有人添加、移动或删除时就会触发“嘿,我改变了”事件。

以下是一个示例,它不打算教你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如何与你的代码交互!

1
哎呀,这东西真是太复杂了!与KVO、MVC和IBOutlets的简单相去甚远,但也许我还需要适应一下。:) 非常感谢你的解释和精心准备的示例,接下来的几天我会经常参考它。 - Craig Otis
@craig:是的,这与Obj-C不同,毫无疑问。尽管我很难从WPF转到Obj-C/Cocoa ;) 有用的MVVM框架链接:WPF应用程序框架Prism 4.0Prism第5章:实现MVVM模式 都是开始的好地方。 - user7116

1
  1. 在 WPF 中,通常只需将一些集合作为 ItemsSource 并使用 data templates 来显示项。

  2. 通常,如果 ItemsSource 实例实现了 INotifyCollectionChanged,那么这些控件只有在更新时才会更新,也许您是在 ListBox 检索该项之前添加了该项。

  3. 什么是 Pound?除非 Pound 具有像 ObservableCollection 一样的线程亲和性,否则没有问题,如果它有,您需要使用 dispatching

  4. ListBox.Items.Refresh() 可以做到这一点,但通常您只需使用具有通知的集合即可。

WPF 严重依赖于数据绑定,因此如果你想学习该框架相应的概述(以及其他所有内容),可能会感兴趣。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接