Использование ItemsSource для заполнения списка WPF - Хорошая идея?

StackOverflow https://stackoverflow.com/questions/8318601

  •  25-10-2019
  •  | 
  •  

Вопрос

Я (относительно) опытный программист Cocoa / Objective-C и сам изучаю C # и фреймворк WPF.

В Какао, при заполнении 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, который, как и любой шаблон, имеет множество реализаций.

Основой является каждая Модель (например, Pound и Dog) будет иметь модель представления, ответственную за представление модели таким образом, чтобы с ней было легко взаимодействовать из пользовательского интерфейса.

Чтобы вы могли начать, 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, но, надеюсь, он даст вам представление о том, как пользовательский интерфейс может взаимодействовать с вашим кодом!

Другие советы

  1. В WPF у вас обычно просто есть некоторая коллекция в качестве ItemsSource и шаблоны данных чтобы отобразить элемент.

  2. Обычно эти элементы управления обновляются только в том случае, если экземпляр ItemsSource реализует INotifyCollectionChanged, возможно, вы добавили элемент перед ListBox извлек его.

  3. Что такое фунт?Если только Pound не имеет некоторого сродства к потоку, как, например, ObservableCollection делает, это не проблема, если вам нужно использовать отправка.

  4. ListBox.Items.Refresh() можно было бы это сделать, но обычно вы просто используете коллекцию с уведомлениями.

WPF в значительной степени использует привязку данных, поэтому, если вы хотите изучить фреймворк соответствующий обзор (вместе с все остальные) может представлять интерес.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top