使用 ItemsSource 填充 WPF ListBox - 好主意? [英] Using ItemsSource to populate WPF ListBox - Good Idea?

查看:18
本文介绍了使用 ItemsSource 填充 WPF ListBox - 好主意?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名(相对)经验丰富的 Cocoa/Objective-C 编码员,并且正在自学 C# 和 WPF 框架.

I'm a (relatively) experienced Cocoa/Objective-C coder, and am teaching myself C# and the WPF framework.

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

In Cocoa, when populating an NSTableView, it's relatively simply to assign a delegate and datasource to the view. Those delegate/datasource methods are then used to populate the table, and to determine its behavior.

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

I'm putting together a simple application that has a list of objects, lets call them Dog objects, that each have a public string name. This is the return value of Dog.ToString().

对象将显示在 ListBox 中,我想使用与 Cocoa 的 NSTableViewDataSource 类似的模式填充此视图.它目前似乎正在使用:

The objects will be displayed in a ListBox, and I would like to populate this view using a similar pattern to Cocoa's NSTableViewDataSource. It currently seems to be working using:

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 不到一个小时,所以可以肯定地说我不知道​​自己在做什么.

But I'm wondering how correct this is. I've literally had Visual Studio installed for less than an hour, so it's safe to say I have no idea what I'm doing.

  1. 这是正确的模式吗?
  2. 将第二项添加到列表 (spot) 似乎可以正确更新 ListBox,但我想知道是什么触发了更新?
  3. 如果我在后台线程上更新 Pound 会发生什么?
  4. 如何手动要求 ListBox 自行更新?(我什至需要吗?)
  1. Is this the proper pattern?
  2. Adding the second item to the list (spot) seems to update the ListBox properly, but I'm wondering what triggers the updates?
  3. What happens if I update the Pound on a background thread?
  4. How can I manually ask the ListBox to update itself? (Do I even need to?)

我知道我需要做的一个改变是将 IEnumerable 实现重构到它自己的类中,例如 DogListItemsSource,但是我想在完善之前确保我有一个可靠的方法.

One change that I know I need to make is refactoring the IEnumerable<Dog> implementation into its own class, like DogListItemsSource, but I want to make sure I have a solid approach before polishing it.

随意在评论中指出我应该解决或记住的任何其他要点,无论大小.我想第一次以正确的方式学习这个.

Feel free to point out, in comments, any other points I should address or keep in mind, big or small. I'd like to learn this the right way, the first time.

推荐答案

我的建议是在您的 Window 之外创建一个类,该类负责将数据提供给您的 ListBox.一种常见的方法是将 WPF 称为 MVVM,它与任何模式一样有许多实现.

My suggestion would be to create a class besides your Window which would be responsible for providing the data to your ListBox. A common approach is WPF is called MVVM, which like any pattern has many implementations.

基础是每个模型(例如 PoundDog)都有一个 View Model 负责以易于从 UI 交互的方式呈现模型.

The basics are each Model (e.g. Pound and Dog) would have a View Model responsible for presenting the model in a manner which is easy to interact with from the UI.

为了帮助您入门,WPF 提供了一个出色的类,ObservableCollection<T>,这是一个在添加、移动或删除任何人时触发Hey I Changed"事件的集合.

To get you started, WPF provides an excellent class, ObservableCollection<T>, which is a collection that fires off a "Hey I Changed" event whenever anybody is added, moved, or removed.

下面是一个不打算教你 MVVM 的例子,也没有使用任何 MVVM 框架.但是,如果您设置一些断点并使用它,您将了解绑定、命令、INotifyPropertyChanged 和 ObservableCollection;所有这些都在 WPF 应用程序开发中发挥着重要作用.

Below is an example that doesn't intend to teach you MVVM, nor does it use any framework for MVVM. However, if you set some breakpoints and play with it, you'll learn about bindings, commands, INotifyPropertyChanged, and ObservableCollection; all of which play a large role in WPF application development.

MainWindow 开始,您可以将 DataContext 设置为 View Model:

Starting in the MainWindow, you can set your DataContext to a View Model:

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 对象的集合:

Where the PoundViewModel manages a collection of DogViewModel objects:

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 使用您的视图模型):

And in your XAML (be sure to map xmlns:local to allow XAML to use your View Models):

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

Finally you'll need an implementation of 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 如何与您的代码交互!

This answer by no means will have you whipping up immersive, fully bound WPF UI's, but hopefully it'll give you a feel for how the UI can interact with your code!

这篇关于使用 ItemsSource 填充 WPF ListBox - 好主意?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆