我是一名(相对)经验丰富的 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, ,但我想确保在完善它之前我有一个可靠的方法。

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

有帮助吗?

解决方案

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

基础知识是每个模型(例如 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 有一定的线程亲和力,例如 ObservableCollection 是的,那没问题,如果是的话你需要使用 调度.

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

WPF大量使用数据绑定,所以如果你想学习这个框架 各自的概述 (随着 所有其他人)可能会感兴趣。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top