Использование ItemsSource для заполнения WPF ListBox - хорошая идея? - PullRequest
5 голосов
/ 30 ноября 2011

Я (относительно) опытный кодер Cocoa / Objective-C и учу себя C # и структуре WPF.

В Какао при заполнении NSTableView относительно просто назначить делегат и источник данных для представления. Эти методы делегата / источника данных затем используются для заполнения таблицы и определения ее поведения.

Я собираю простое приложение со списком объектов, давайте назовем их Dog объектами, каждый из которых имеет public string name. Это возвращаемое значение Dog.ToString().

Объекты будут отображаться в ListBox, и я хотел бы заполнить это представление, используя шаблон, аналогичный 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 голосов
/ 30 ноября 2011

Я бы предложил создать класс помимо вашего Окна, который бы отвечал за предоставление данных вашему 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 голос
/ 30 ноября 2011
  1. В WPF у вас обычно есть просто коллекция элементов ItemsSource и шаблоны данных для отображения элемента.

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

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

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

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

...