Pregunta

Soy un codificador de cacao/objetivo-C experimentado (relativamente), y me estoy enseñando C# y el marco WPF.

En cacao, al poblar un NSTableView, es relativamente simplemente asignar un delegado y un plato de datos a la vista. Esos métodos de delegado/fuente de datos se utilizan para llenar la tabla y para determinar su comportamiento.

Estoy armando una aplicación simple que tiene una lista de objetos, llamémoslos Dog objetos, que cada uno tiene un public string name. Este es el valor de retorno de Dog.ToString().

Los objetos se mostrarán en un ListBox, y me gustaría poblar esta visión usando un patrón similar al cacao NSTableViewDataSource. Actualmente parece estar funcionando usando:

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

Pero me pregunto como correcto esto es. Literalmente he instalado Visual Studio durante menos de una hora, por lo que es seguro decir que no tengo idea de lo que estoy haciendo.

  1. ¿Es este el patrón adecuado?
  2. Agregar el segundo elemento a la lista (spot) parece actualizar el ListBox correctamente, pero me pregunto qué desencadena las actualizaciones.
  3. ¿Qué pasa si actualizo el Pound ¿Sobre un hilo de fondo?
  4. ¿Cómo puedo preguntar manualmente el ListBox para actualizarse? (¿Necesito?)

Un cambio que sé que necesito hacer es refactorizar el IEnumerable<Dog> implementación en su propia clase, como DogListItemsSource, pero quiero asegurarme de tener un enfoque sólido antes de pulirlo.

Sentirse libre Para señalar, en los comentarios, cualquier otro punto que deba abordar o tener en cuenta, grande o pequeño. Me gustaría aprender esto de la manera correcta, la primera vez.

¿Fue útil?

Solución

Mi sugerencia sería crear una clase además de su ventana que sea responsable de proporcionar los datos a su ListBox. Se llama un enfoque común WPF MVVM, que, como cualquier patrón, tiene muchas implementaciones.

Los conceptos básicos son cada modelo (por ejemplo Pound y Dog) tendría un modelo de vista responsable de presentar el modelo de una manera que sea fácil de interactuar desde la interfaz de usuario.

Para comenzar, WPF proporciona una excelente clase, ObservableCollection<T>, que es una colección que dispara un evento "hey, cambié" cada vez que se agrega, se mueve o elimina a alguien.

A continuación se muestra un ejemplo que no tiene la intención de enseñarle MVVM, ni utiliza ningún marco para MVVM. Sin embargo, si establece algunos puntos de interrupción y juega con él, aprenderá sobre enlaces, comandos, inotifypropertychanged y observableCollection; Todo lo cual juega un papel importante en el desarrollo de aplicaciones WPF.

Comenzando en el MainWindow, puedes configurar tu DataContext a un modelo de vista:

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

Donde el PoundViewModel administra una colección de DogViewModel objetos:

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

Y en tu xaml (asegúrese de mapear xmlns:local Para permitir que XAML use sus modelos de vista):

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

Por supuesto, necesitarías 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));
    }
}

Finalmente necesitará una implementación 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);
    }
}

Esta respuesta de ninguna manera te hará preparar la UI WPF inmersiva y totalmente vinculada, ¡pero espero que te dé una idea de cómo la interfaz de usuario puede interactuar con tu código!

Otros consejos

  1. En WPF, generalmente solo tiene una colección como SECHESOURCE y plantillas de datos Para mostrar el elemento.

  2. Normalmente, esos controles solo se actualizan si la instancia de elementos del elemento implementa INotifyCollectionChanged, tal vez agregó el artículo antes del ListBox lo recuperó.

  3. ¿Qué es la libra? A menos que la libra tenga alguna afinidad de hilo como por ejemplo ObservableCollection lo hace, eso no es un problema, si es así, necesitas usar envío.

  4. ListBox.Items.Refresh() Podría hacer eso, pero generalmente solo usa una colección con notificaciones.

WPF usa en gran medida el enlace de datos, por lo que si desea aprender el marco la descripción respectiva (junto con todos los otros) podría ser de interés.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top