WPF - modo elegante di disattivazione e attivazione diversi controlli sulla base di diversi stati del modello utilizzando MVVM

StackOverflow https://stackoverflow.com/questions/3764484

Domanda

Sto cercando una soluzione elegante per il seguente problema.

Supponiamo che abbiamo un (View) Modello con le seguenti proprietà booleane:

  • Alpha
  • Beta
  • Gamma
  • Delta

Avanti Ho 5 controlli sulla superficie, che devono essere visibili solo quando sono soddisfatte una condizione basata su tali proprietà. Naturalmente, non appena una di queste proprietà viene aggiornato il cambiamento dovrebbe essere propagate immediatelly:

  • ControlA -> Alpha && (Beta || Gamma)
  • ControlB -> Delta
  • controlC -> Delta || Beta
  • controld -> Gamma && && Alpha Delta
  • CONTROLE -> Alpha || Gamma

L'unica soluzione mi è venuta finora sta usando MultiValueConverters.

Esempio per ControlA:

<ControlA>
   <ControlA.Visibility>
      <MultiBinding Converter={StaticResource ControlAVisibilityConverter}>
          <Binding Path="Alpha"/>
          <Binding Path="Beta"/>
          <Binding Path="Gamma"/>
      </MultiBinding>
   </ControlA.Visibility>
</ControlA>

Questa ControlAVisibilityConverter verifica la presenza di condizioni di "Alpha && (Beta || gamma)" e restituisce il valore appropriato.

Si fa il lavoro .. beh .. ma forse si può trovare una soluzione più elegante?

Grazie, TwinHabit

È stato utile?

Soluzione

Scrivi convertitore per ogni regola mette la logica di business due posti in questo caso (nel convertitore e il modello di vista). Suggerisco di creare una proprietà / bandierina per ogni controllo nel vostro ViewModel con eventi INotifyPropertyChanged per decidere se il controllo è visibile (o altri comportamenti).

Si noti che quando si guarda a mio ViewModel (sotto) si vedrà che io espongo le proprietà di tipo bool e visibilità.

Se è necessario utilizzare la proprietà come un generale uso regola bool e DataTrigger.

public bool ControlD

Se avete solo bisogno di una visibilità di controllo si può associare Visibilità direttamente:

public Visibility ControlA

Aggiorna : A causa del commento di @Wallstreet programmatore, ho aggiunto un'altra opzione per utilizzare un BooleanVisibilityConverter. Ho aggiornato il quinto controllo qui sotto per riflettere come utilizzare un convertitore. Ho aggiunto il codice per il convertitore in basso.

Ecco una finestra di prova in XAML:

<Window x:Class="ControlVisibleTrigger.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Main Window" Height="400" Width="800">
  <Window.Resources>
    <Style x:Key="DropDownStyle" TargetType="TextBox">
        <Setter Property="Visibility" Value="Hidden"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding ControlC}" Value="True">
                <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
  </Window.Resources>
  <DockPanel>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0">
            <CheckBox IsChecked="{Binding Path=Alpha,Mode=TwoWay}" Content="Alpha"/>
            <CheckBox IsChecked="{Binding Path=Beta,Mode=TwoWay}" Content="Beta"/>
            <CheckBox IsChecked="{Binding Path=Gamma,Mode=TwoWay}" Content="Gamma"/>
            <CheckBox IsChecked="{Binding Path=Delta,Mode=TwoWay}" Content="Delta"/>
        </StackPanel>
        <TextBox Grid.Row="1" Visibility="{Binding Path=ControlA}" Text="Binding to visibility"/>
        <Button Grid.Row="2" Visibility="{Binding Path=ControlB}" Content="Binding to visibility"/>
        <TextBox Grid.Row="3" Style="{StaticResource DropDownStyle}" Text="Using WindowResource DataTrigger"/>
        <TextBox Grid.Row="4" Text="Using Local DataTrigger">
            <TextBox.Style>
              <Style TargetType="TextBox">
                <Setter Property="Visibility" Value="Hidden"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ControlD}" Value="True">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
              </Style>
            </TextBox.Style>
        </TextBox>
        <Button Grid.Row="5" 
                Content="Press me" 
                Visibility="{Binding Path=ControlE, Converter={StaticResource booleanVisibilityConverter}, ConverterParameter=True, Mode=OneWay}">
    </Grid>
  </DockPanel>
</Window>

Ecco l'ViewModel:

public class MainViewModel : ViewModelBase
{
  public MainViewModel()
  {
  }

  private bool _alpha = true;
  public bool Alpha
  {
     get
     {
        return _alpha;
     }
     set
     {
        _alpha = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  private bool _beta = true;
  public bool Beta
  {
     get
     {
        return _beta;
     }
     set
     {
        _beta = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  private bool _gamma = true;
  public bool Gamma
  {
     get
     {
        return _gamma;
     }
     set
     {
        _gamma = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  private bool _delta = true;
  public bool Delta
  {
     get
     {
        return _delta;
     }
     set
     {
        _delta = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  public Visibility ControlA
  {
     get
     {
        Visibility result = Visibility.Hidden;
        if ( Alpha && (Beta || Gamma))
        {
           result = Visibility.Visible;
        }
        return result;
     }
  }

  public Visibility ControlB
  {
     get
     {
        Visibility result = Visibility.Hidden;
        if ( Delta )
        {
           result = Visibility.Visible;
        }
        return result;
     }
  }

  private bool _controlC = false;
  public bool ControlC
  {
     get
     {
        return Delta || Beta;
     }
  }

  private bool _controlD = false;
  public bool ControlD
  {
     get
     {
        return Gamma && Alpha && Delta;
     }
  }

  private bool _controlE = false;
  public bool ControlE
  {
     get
     {
        return Alpha || Gamma;
     }
  }
}

Ecco il convertitore:

public class BooleanVisibilityConverter : IValueConverter
{
  public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
  {
    if( ( value == null ) || ( !( value is bool ) ) )
      return Binding.DoNothing;

    Visibility elementVisibility;
    bool shouldCollapse = ( ( bool )value );

    if( parameter != null )
    {
      try
      {
        bool inverse = System.Convert.ToBoolean( parameter );

        if( inverse )
          shouldCollapse = !shouldCollapse;
      }
      catch
      {
      }
    }

    elementVisibility = shouldCollapse ? Visibility.Collapsed : Visibility.Visible;
    return elementVisibility;
  }

  public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
  {
    throw new NotImplementedException();
  }
}

Altri suggerimenti

Partendo dal presupposto che c'è una ragione logica di business per se i controlli devono essere visualizzati avrei sicuramente hanno la logica memorizzato come un bool nel ViewModel (anche se mi piacerebbe nominarlo secondo la logica di business ad esempio: CriteriaA non ControlAVisible ). Questo permette un facile unità test per garantire che i criteri siano impostate correttamente come alfa, beta, gamma e delta cambiamento. Diverso da quello che ero d'accordo con Wallstreet programmatori risposta (anche se non ho il rappresentante di commentare o votare la sua risposta).

Se i controlli supportano i comandi (ad esempio se sono pulsanti), utilizzare il comando modello. Con RelayCommand (guardare in su), è possibile specificare la condizione in cui il controllo è attiva con un'espressione lambda (che è esattamente quello che ti serve). Ha bisogno di un po 'di code-behind però.

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