Domanda

Sono un (relativamente) sperimentato Cocoa / Objective-C coder, e insegno io C # e il quadro WPF.

In Cocoa, quando popola una NSTableView, è relativamente semplice per assegnare un delegato e datasource alla vista. Quei delegati / metodi di origine dati vengono poi utilizzati per popolare la tabella, e per determinare il suo comportamento.

Sto mettendo insieme una semplice applicazione che ha una lista di oggetti, consente di chiamare loro Dog oggetti, che hanno ciascuno una public string name. Questo è il valore di ritorno di Dog.ToString().

Gli oggetti sarà visualizzato in un ListBox, e vorrei per popolare questo punto di vista utilizzando un modello simile a NSTableViewDataSource di cacao. Attualmente sembra funzionare utilizzando:

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();
        }
    }

Ma mi chiedo come corretta questo è. Ho letteralmente dovuto Visual Studio installato per meno di un'ora, quindi è sicuro di dire non ho idea di quello che sto facendo.

  1. E 'questo il modello corretto?
  2. aggiunta del secondo elemento alla lista (spot) sembra per aggiornare il ListBox correttamente, ma mi chiedo che cosa fa scattare gli aggiornamenti?
  3. Che cosa accade se aggiorno il Pound su un thread in background?
  4. Come posso chiedere manualmente il ListBox di aggiornarsi? (Do Ho anche bisogno di?)

Un cambiamento che so che ho bisogno di fare è refactoring l'attuazione IEnumerable<Dog> nella propria classe, come DogListItemsSource, ma voglio essere sicuro di avere un approccio solido prima lucidatura esso.

Sentitevi liberi a sottolineare, nei commenti, eventuali altri punti che devono affrontare o tenere a mente, grande o piccolo. Mi piacerebbe imparare questo il modo giusto, la prima volta.

È stato utile?

Soluzione

Il mio suggerimento sarebbe quello di creare una classe oltre al tuo Window, che sarebbe responsabile di fornire i dati al vostro ListBox. Un approccio comune è WPF è chiamato MVVM , che come qualsiasi modello ha molte implementazioni.

Le basi sono ogni modello (ad esempio Pound e Dog) avrebbe una vista del modello responsabile per la presentazione del modello in un modo che è facile interagire con dall'interfaccia utente.

Per iniziare, WPF fornisce una classe eccellente, ObservableCollection<T> , che è una collezione che spara un "Hey ho cambiato" evento ogni volta che viene aggiunto a nessuno, spostato o rimosso.

Di seguito è riportato un esempio che non intende insegnare MVVM, né utilizza alcun quadro di riferimento per MVVM. Tuttavia, se si imposta alcuni punti di interruzione e giocare con lui, imparerete a conoscere attacchi, comandi, INotifyPropertyChanged, e ObservableCollection; ognuno dei quali gioca un ruolo importante nello sviluppo di applicazioni WPF.

A partire dal MainWindow, è possibile impostare il DataContext ad un Model View:

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();
     }
}

Quando la PoundViewModel gestisce una collezione di oggetti 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)
        );
    }
}

E nel tuo XAML ( essere sicuri di mappare xmlns:local per consentire di utilizzare il XAML Visualizza i modelli ):

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

Naturalmente, avresti bisogno di un 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));
    }
}

Infine avrete bisogno di un'implementazione di 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);
    }
}

Questa risposta in nessun modo vi farà fustigazione coinvolgente, pienamente legata WPF UI, ma si spera che sarà darvi un'idea di come l'interfaccia utente può interagire con il vostro codice!

Altri suggerimenti

  1. In WPF di solito basta avere un po 'di raccolta come ItemsSource e modelli di dati per visualizzare l'elemento.

  2. Normalmente tali controlli aggiornano solo se gli attrezzi istanza ItemsSource INotifyCollectionChanged , forse si è aggiunto la voce prima che il ListBox recuperò.

  3. Che cos'è Pound? A meno che Pound abbia qualche filo affinità come ad esempio ObservableCollection fa, che non è un problema, se lo fa è necessario utilizzare invio .

  4. ListBox.Items.Refresh() poteva farlo, ma di solito è sufficiente utilizzare una collezione con le notifiche.

WPF utilizza pesantemente l'associazione di dati, quindi se volete imparare il quadro rispettivo panoramica (insieme a tutti gli altri ) potrebbe essere di interessi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top