Aggiunta di modello alla raccolta di un altro modello di vista

2

Il programma di installazione

Quindi sto lavorando a un progetto in cui esiste una classe MainViewModel . Questo MainViewModel contiene un elenco di Soldiers attraverso una raccolta osservabile. Ho un pulsante in MainView che mostra un nuovo AddSoldierView . AddSoldierView è vincolante per gli elementi di AddSoldierViewModel . Il AddSoldierView è fondamentalmente una forma in cui l'utente inserisce tutti i dati per quel soldato.

Il problema

Ora che ho le informazioni di Soldier su AddSoldierViewModel , voglio poterle aggiungere nuovamente a ObservableCollection di MainViewModel . Ho associato un comando al pulsante (Aggiungi soldato) su AddSoldierView , ma non sono sicuro di come reindirizzare tali informazioni in MainViewModel .

Quello che ho provato

Ho già impostato un gestore di eventi su AddSoldierViewModel in cui SoldierModel è passato come EventArg. Ma non riesco a far scattare l'evento stesso.

Qualche suggerimento? Ho cercato di rimanere fedele allo spirito MVVM, ma ci sono ancora alcuni nodi che sto cercando di risolvere. Fammi sapere se vuoi vedere alcuni frammenti di codice, diagrammi UML o altro.

AddSoldierViewModel.cs

public class AddSoldierViewModel : ViewModelBase
{
    public event EventHandler<AddSoldierEventArgs> AddSoldierClicked;

    public ICommand AddSoldierCommand;

    private Soldier _soldier;

    public Soldier Soldier
    {
        get => _soldier;
        set
        {
            _soldier = value;
            RaisePropertyChanged(nameof(Soldier));
        }
    }

    public AddSoldierViewModel() 
    {
        AddSoldierCommand = new RelayCommand(AddSoldier);
    }

    private void AddSoldier()
    {
        OnAddSoldierClicked(new AddSoldierEventArgs()
        {
           Soldier = Soldier
        });
    }

    protected virtual void OnAddSoldierClicked(AddSoldierEventArgs e)
    {
        var handler = AddSoldierClicked;
        handler?.Invoke(this, e);
    }
}

MainViewModel.cs

public class MainViewModel : ViewModelBase
{
    #region - Private Properties -
    private Team _selectedTeam;
    private Soldier _selectedSoldier;
    #endregion // - Private Properties -

    #region // - Public Properties -
    public ObservableCollection<Soldier> Soldiers { get; set; }
    public ObservableCollection<Team> Teams { get; }
    public Team SelectedTeam
    {
        get => _selectedTeam;
        set
        {
            _selectedTeam = value;
            RaisePropertyChanged(nameof(SelectedTeam));
        }
    }
    public Soldier SelectedSoldier
    {
        get => _selectedSoldier;
        set
        {
            _selectedSoldier = value;
            RaisePropertyChanged(nameof(SelectedSoldier));
        }
    }
    #endregion // - Public Properties -

    #region // - Commands -
    public ICommand DeleteTeamCommand { get; private set; }
    public ICommand AddSoldierDialogCommand { get; private set; }
    #endregion // - Commands -

    #region  - Services -
    public IDialogService AddSoldierDialogService { get; private set; }
    #endregion // - Services -

    #region - Constructors -
    public MainViewModel()
    {
        Soldiers = new ObservableCollection<Soldier>();
        Teams = new ObservableCollection<Team>();

        Soldiers.CollectionChanged += Soldiers_CollectionChanged;
        Teams.CollectionChanged += Teams_CollectionChanged;

        DeleteTeamCommand = new RelayCommand(DeleteTeam);
        AddSoldierDialogCommand = new RelayCommand(AddSoldierDialog);

        AddSoldierDialogService = new AddSoldierDialogService();
    }

    #endregion // - Constructors -

    #region - Methods -
    private void AddSoldierDialog()
    {
        AddSoldierViewModel addSoldierViewModel = new AddSoldierViewModel();
        addSoldierViewModel.AddSoldierClicked += AddSoldierViewModel_AddSoldierClicked;
        AddSoldierDialogService.ShowDialog(addSoldierViewModel);
    }

    private void AddSoldierViewModel_AddSoldierClicked(object sender, AddSoldierEventArgs e)
    {
        Soldiers.Add(new Soldier(e.Soldier));
    }

    private void Teams_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        foreach (var item in e.NewItems)
        {
        }
        foreach (var item in e.OldItems)
        {
        }
        RaisePropertyChanged(nameof(Teams));
    }

    private void Soldiers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        foreach (var item in e.NewItems)
        {
        }
        foreach (var item in e.OldItems)
        {
        }
        RaisePropertyChanged(nameof(Soldiers));
    }
    #endregion // - Methods -
}
    
posta Matthew S 25.01.2018 - 05:22
fonte

2 risposte

0

Quindi ho fatto un po 'di casino in più ieri sera e penso di aver trovato una buona soluzione per MVVM. Ho rivisitato l'idea dell'evento e aggiunto un EventHandler AddSoldierClick a AddSoldierVM . Ho anche creato una classe EventArgs personalizzata per contenere il Soldier selezionato.

Quando istanziato un AddSoldierVM sul mio MainVM , mi iscrivo all'evento AddSoldierClick e aggiungo l'oggetto Soldier di EventArg alla mia ObservableCollection in MainVM .

Dopodiché, ho solo RaisePropertyChanged(nameof(Soldiers)) nell'evento Soldiers_CollectionChanged() .

Vado a postare snippet di codice quando torno a casa. Ma ho deciso di condividere questa risposta con voi ragazzi. Ci sono altri modi per farlo? Penso che questo sia abbastanza pulito (bisogna solo fare attenzione alle potenziali perdite di memoria nell'iscriversi all'evento e dimenticarsi di annullare l'iscrizione quando viene eseguito il MainVM ).

    
risposta data 25.01.2018 - 16:57
fonte
0

Poiché AddSoldierViewModel è per una finestra di dialogo, penso che tu possa rendere le cose molto più semplici. L'unica cosa che deve essere cambiata è il metodo AddSoldierDialog() :

private void AddSoldierDialog()
{
    AddSoldierViewModel addSoldierViewModel = new AddSoldierViewModel();

    if(AddSoldierDialogService.ShowDialog(addSoldierViewModel) == true)
    {
        Soldiers.Add(addSoldierViewModel.Soldier);
    }
}

Il valore di ritorno di ShowDialog() dovrebbe essere bool? , dove true significa che l'utente ha chiuso la finestra di dialogo in buono stato. false chiuderà la finestra di dialogo in cattivo stato e null chiuderà la finestra di dialogo dalla finestra stessa. Una volta che l'utente intende aggiungere quel soldato, puoi aggiungerlo direttamente all'elenco.

Questo evita percorsi complicati degli eventi e semplifica il debug.

Naturalmente, se il caso d'uso della finestra di dialogo Aggiungi soldato era resta aperto e consente all'utente di aggiungere più nuovi soldati prima di chiudere, quindi il tuo evento sarebbe necessario. Assicurati di annullare la registrazione quando non ne hai più bisogno.

L'ultimo commento sarebbe se si sceglie di mantenere l'evento, si gestisce la copia dell'istanza Soldier prima di inviarlo. In questo modo non hai bug persistenti perché da qualche parte qualcuno ha pensato che il soldato dell'evento fosse unico.

In questo caso il tuo gestore di clic sarà simile a questo:

private void RaiseAddSoldierClicked()
{
    AddSoldierClicked?.Invoke(this, new AddSoldierEventArgs
    {
        Soldier = new Soldier(Soldier)
    });
}

Gestirlo sulla fonte degli eventi anziché su tutti gli ascoltatori ti aiuterà a ridurre i bug inattesi.

    
risposta data 26.03.2018 - 22:43
fonte

Leggi altre domande sui tag