Proprietà reciprocamente dipendenti in WPF

3

Ho una vista WPF che contiene 2 proprietà reciprocamente dipendenti; un cambiamento in uno potrebbe innescare un cambiamento nell'altro. La mia domanda: come posso scrivere codice che aggiorna in modo pulito entrambi?

Più in particolare: ho una vista WPF che consente di esportare i dati. Quella vista contiene un elenco di "canali" (è così che chiamo i miei dati), e per ciascun canale l'utente può selezionare diversi formati di file per l'esportazione. Quindi è possibile esportare il canale1 in .dat e .csv , mentre il canale 2 viene esportato in .xls e .csv , qualunque sia l'utente seleziona.
La vista contiene anche una casella di controllo che indica che il canale deve essere esportato.

Il mio comportamento desiderato è:

  • Se tutti i formati di file non sono selezionati, la casella di controllo principale deve essere disinserita.
  • Se è selezionato un formato file, la casella di controllo principale è impostata.
  • Se la casella di controllo principale non è impostata, l'elenco dei formati di file è invariato.
  • Se la casella di controllo principale è impostata e c'è un elenco di formati selezionati, è invariato.
  • se la casella di controllo principale è impostata e l'elenco dei formati selezionati è vuoto, vengono impostati alcuni formati di file predefiniti.

Il mio problema principale è farlo bene, perché non voglio buttare "RaisPropertyChanged" ovunque e vorrei avere un posto un po 'centralizzato per gestire l'aggiornamento.

Soluzione ingenua:

public bool? IsSelected {
    get { return this.isSelected; }
    set {
        if (this.isSelected != value) {
            this.isSelected = value;
            if (value == true && this.SelectedFileFormats.Count == 0) {
                ApplyDefaultFileFormats();
            }
            this.RaisePropertyChanged("IsSelected");
        }
    }
}


public void ApplyDefaultFileFormats() {
    this.SelectedFileFormats = // Some logic.
}

public IList<VariableCommandHandler> SelectedFileFormats {
    get { return this.selectedFileFormats; }
    set {
        this.selectedFileFormats = value;
        this.UpdatePropertiesRelatedToSelectedHandlers();
        this.RaisePropertyChanged("SelectedFileFormats");
        this.RaisePropertyChanged("FileName");
    }
}

private void UpdatePropertiesRelatedToSelectedHandlers() {
    this.IsSelected = (this.selectedFileFormats.Count > 0);
    // Do a bunch of other updates, which are derived only from the SelectedHandlers.
}

Funziona, ma non mi piace il fatto che stia andando due volte al IsSelected setter e manchi semplicemente di entrare in un ciclo infinito. Fondamentalmente, se IsSelected è impostato su true (mentre non ci sono formati di file selezionati), verranno invocati tutti i metodi precedenti. Una volta in UpdatePropertiesRelatedToSelectedHandlers l'esecuzione tornerà a IsSelected con true mentre è già true , quindi l'istruzione if all'inizio sarà true != true e l'esecuzione inizierà a ritornare.

Vorrei un modo più semplice, senza chiamare un setter mentre eseguivo un'altra esecuzione dello stesso setter.

Ho pensato di impostare i campi di supporto nei due setter, quindi chiamare il metodo di aggiornamento e chiamare entrambi "RaisPropertyChanged" per entrambe le proprietà. Ma questo non è il codice più pulito in quanto il setter dovrebbe essere l'unico posto in cui vengono apportate modifiche alle proprietà.

Qualche suggerimento su un modo più pulito per raggiungere questo obiettivo?

    
posta Ramzi Kahil 22.02.2017 - 08:17
fonte

1 risposta

0

Le dipendenze circolari come quella sono una cattiva idea in qualsiasi tipo di software. La soluzione consiste nell'aggiornare l'interfaccia utente in modo esplicito quando i valori cambiano, utilizzando un approccio funzionale: prendi le specifiche che hai ed esegui un mapping dallo stato dell'oggetto allo stato visivo, ogni volta che una proprietà dipendente cambia, o ad intervalli appropriati.

Per prevenire le dipendenze circolari, mantenere un campo booleano (_updatingView) nella vista di livello superiore o nel componente del controller (il "modulo" o controller, qualunque cosa si usi), che indica che si sta aggiornando la vista. Imposta questo campo in un blocco try, esegui il mapping della vista, quindi cancella il campo nel blocco finally. Se il campo è impostato quando un componente dell'interfaccia utente modifica una proprietà pertinente, ignorare la modifica.

    
risposta data 23.02.2017 - 17:08
fonte

Leggi altre domande sui tag