Question

Je suis un (relativement) connu Cocoa / Objective-C codeur, et je suis moi-même enseigne C # et le cadre WPF.

En Cocoa, lors du remplissage d'un NSTableView, il est relativement simple d'attribuer un délégué et DataSource à la vue. Les délégués / méthodes de source de données sont ensuite utilisées pour remplir la table, et de déterminer son comportement.

Je prépare une application simple qui a une liste d'objets, permet de les appeler des objets Dog, qui ont chacun un public string name. Ceci est la valeur de retour de Dog.ToString().

Les objets seront affichés dans un ListBox, et je voudrais remplir ce point de vue en utilisant un modèle similaire à NSTableViewDataSource Cocoa. Il semble actuellement travailler en utilisant:

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

Mais je me demande comment correct c'est. J'ai littéralement eu Visual Studio installé pour moins d'une heure, il est sûr de dire que je ne sais pas ce que je fais.

  1. Est-ce le modèle approprié?
  2. Ajout du second élément à la liste (spot) semble mettre à jour le ListBox correctement, mais je me demande ce qui déclenche les mises à jour?
  3. Qu'est-ce qui se passe si je mets à jour le Pound sur un thread d'arrière-plan?
  4. Comment puis-je demander manuellement le ListBox de se mettre à jour? (Ai-je encore besoin?)

Un changement que je sais que je dois faire est refactoring la mise en œuvre de IEnumerable<Dog> dans sa propre classe, comme DogListItemsSource, mais je veux vous assurer que j'ai une approche solide avant le polir.

Sentez-vous libre pour indiquer, dans les commentaires, les autres points que j'adresse ou garder à l'esprit, grand ou petit. Je voudrais savoir ce la bonne voie, la première fois.

Était-ce utile?

La solution

Ma suggestion serait de créer une classe en plus de votre fenêtre qui serait chargé de fournir les données à votre ListBox. Une approche commune est WPF est appelée MVVM , qui, comme tout modèle a de nombreuses implémentations.

Les bases sont chaque modèle (par exemple Pound et Dog) aurait une vue de modèle chargé de présenter le modèle d'une manière qui est facile d'interagir avec de l'interface utilisateur.

Pour vous aider à démarrer, WPF offre une excellente classe, ObservableCollection<T> , qui est une collection qui Déclenché un « Hey I Changed » événement chaque fois que quelqu'un est ajouté, déplacé ou retiré.

Voici un exemple qui n'a pas l'intention de vous enseigner MVVM, ni utiliser aucun cadre pour MVVM. Toutefois, si vous définissez des points d'arrêt et jouer avec lui, vous apprendrez sur les liaisons, les commandes, INotifyPropertyChanged et ObservableCollection; qui jouent tous un rôle important dans le développement d'applications WPF.

A partir du MainWindow, vous pouvez définir votre DataContext à une vue Modèle:

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

Lorsque la PoundViewModel gère une collection d'objets 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)
        );
    }
}

Et dans votre XAML ( assurez-vous de la carte xmlns:local pour permettre XAML d'utiliser votre Modèles Voir ):

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

Bien sûr, vous auriez besoin d'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));
    }
}

Enfin, vous aurez besoin d'une mise en œuvre de 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);
    }
}

Cette réponse en aucun cas vous fera fouettant immersive, entièrement liée WPF UI de, mais nous espérons qu'il va vous donner une idée de la façon dont l'interface utilisateur peut interagir avec votre code!

Autres conseils

  1. Dans WPF vous habituellement ont quelques-unes collection comme ItemsSource et modèles de données pour afficher l'élément.

  2. Normalement, ces contrôles ne sont mis à jour si les outils d'instance ItemsSource INotifyCollectionChanged , peut-être que vous avez ajouté l'élément avant la ListBox récupéra.

  3. Qu'est-ce que Pound? Sauf Pound a un fil d'affinité comme par exemple ObservableCollection fait, qui est sans problème, si elle ne vous devez utiliser dispatching .

  4. ListBox.Items.Refresh() pourrait le faire, mais généralement vous suffit d'utiliser une collection avec des notifications.

WPF utilise fortement la liaison de données, donc si vous voulez apprendre le cadre respectif Vue d'ensemble (ainsi que tous les autres ) pourraient être intérêt.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top