web-dev-qa-db-fra.com

Comment lier des propriétés booléennes inverses dans WPF?

Ce que j'ai est un objet qui a une propriété IsReadOnly. Si cette propriété a la valeur true, j'aimerais définir la propriété IsEnabled d'un bouton (par exemple) sur false.

J'aimerais croire que je peux le faire aussi facilement que IsEnabled="{Binding Path=!IsReadOnly}", mais cela ne va pas avec WPF.

Suis-je obligé de passer par tous les paramètres de style? Cela semble trop verbeux pour quelque chose d'aussi simple que de définir un booléen à l'inverse d'un autre bool.

<Button.Style>
    <Style TargetType="{x:Type Button}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="True">
                <Setter Property="IsEnabled" Value="False" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=IsReadOnly}" Value="False">
                <Setter Property="IsEnabled" Value="True" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Button.Style>
342
Russ

Vous pouvez utiliser un ValueConverter qui inverse une propriété bool pour vous.

XAML:

IsEnabled="{Binding Path=IsReadOnly, Converter={StaticResource InverseBooleanConverter}}"

Convertisseur:

[ValueConversion(typeof(bool), typeof(bool))]
    public class InverseBooleanConverter: IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter,
            System.Globalization.CultureInfo culture)
        {
            if (targetType != typeof(bool))
                throw new InvalidOperationException("The target must be a boolean");

            return !(bool)value;
        }

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

        #endregion
    }
444
Chris Nicol

Avez-vous considéré une propriété IsNotReadOnly? Si l'objet lié est un ViewModel dans un domaine MVVM, la propriété supplémentaire est parfaitement logique. S'il s'agit d'un modèle d'entité direct, vous pouvez envisager de composer et de présenter un modèle ViewModel spécialisé de votre entité au formulaire.

95
Paul Alexander

Avec la reliure standart, vous devez utiliser des convertisseurs qui ont l’air peu venteux. Donc, je vous recommande de regarder mon projet CalcBinding , qui a été développé spécialement pour résoudre ce problème et quelques autres. Avec la liaison avancée, vous pouvez écrire des expressions avec de nombreuses propriétés source directement dans xaml. Dis, tu peux écrire quelque chose comme:

<Button IsEnabled="{c:Binding Path=!IsReadOnly}" />

ou

<Button Content="{c:Binding ElementName=grid, Path=ActualWidth+Height}"/>

ou

<Label Content="{c:Binding A+B+C }" />

ou

<Button Visibility="{c:Binding IsChecked, FalseToVisibility=Hidden}" />

où A, B, C, IsChecked - les propriétés de viewModel et cela fonctionnera correctement

Bonne chance!

63
Alex141

Je recommanderais d'utiliser https://quickconverter.codeplex.com/

Inverser un booléen est alors aussi simple que: <Button IsEnabled="{qc:Binding '!$P', P={Binding IsReadOnly}}" />

Cela accélère le temps normalement nécessaire pour écrire des convertisseurs.

19
Noxxys

Je voulais que mon XAML reste aussi élégant que possible. J'ai donc créé une classe pour envelopper le booléen qui réside dans l'une de mes bibliothèques partagées. Les opérateurs implicites permettent à la classe d'être utilisée comme un booléen dans le code-behind de manière transparente.

public class InvertableBool
{
    private bool value = false;

    public bool Value { get { return value; } }
    public bool Invert { get { return !value; } }

    public InvertableBool(bool b)
    {
        value = b;
    }

    public static implicit operator InvertableBool(bool b)
    {
        return new InvertableBool(b);
    }

    public static implicit operator bool(InvertableBool b)
    {
        return b.value;
    }

}

Les seuls changements nécessaires à votre projet sont de faire en sorte que la propriété que vous souhaitez inverser retourne ceci au lieu de bool

    public InvertableBool IsActive 
    { 
        get 
        { 
            return true; 
        } 
    }

Et dans le post-fix XAML, la liaison avec Value ou Invert

IsEnabled="{Binding IsActive.Value}"

IsEnabled="{Binding IsActive.Invert}"
13
jevansio

Celui-ci fonctionne également pour les objets nullables.

 [ValueConversion(typeof(bool?), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (targetType != typeof(bool?))
        {
            throw new InvalidOperationException("The target must be a nullable boolean");
        }
        bool? b = (bool?)value;
        return b.HasValue && !b.Value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(value as bool?);
    }

    #endregion
}
8
Andreas

J'ai eu un problème d'inversion, mais une solution soignée.

La motivation était que le concepteur XAML affiche un contrôle vide, par exemple. quand il n'y avait aucun datacontext/no MyValues (itemssource).

Code initial: masquer contrôle lorsque MyValues est vide. Code amélioré: show contrôle lorsque MyValues n'est PAS null ou vide.

Bien entendu, le problème est de savoir comment exprimer "1 ou plusieurs éléments", ce qui est le contraire de 0.

<ListBox ItemsSource={Binding MyValues}">
  <ListBox.Style x:Uid="F404D7B2-B7D3-11E7-A5A7-97680265A416">
    <Style TargetType="{x:Type ListBox}">
      <Style.Triggers>
        <DataTrigger Binding="{Binding MyValues.Count}">
          <Setter Property="Visibility" Value="Collapsed"/>
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </ListBox.Style>
</ListBox>

Je l'ai résolu en ajoutant:

<DataTrigger Binding="{Binding MyValues.Count, FallbackValue=0, TargetNullValue=0}">

Ergo définir la valeur par défaut pour la liaison. Bien sûr, cela ne fonctionne pas pour toutes sortes de problèmes inverses, mais m'a aidé avec un code propre.

2
EricG

Je ne sais pas si cela concerne XAML, mais dans ma simple application Windows, j'ai créé la liaison manuellement et ajouté un gestionnaire d'événements Format.

public FormMain() {
  InitializeComponent();

  Binding argBinding = new Binding("Enabled", uxCheckBoxArgsNull, "Checked", false, DataSourceUpdateMode.OnPropertyChanged);
  argBinding.Format += new ConvertEventHandler(Binding_Format_BooleanInverse);
  uxTextBoxArgs.DataBindings.Add(argBinding);
}

void Binding_Format_BooleanInverse(object sender, ConvertEventArgs e) {
  bool boolValue = (bool)e.Value;
  e.Value = !boolValue;
}
2
Simon Dobson

Ajoutez une propriété supplémentaire dans votre modèle de vue, ce qui renverra une valeur inverse. Et lier ça au bouton. Comme;

en vue modèle:

public bool IsNotReadOnly{get{return !IsReadOnly;}}

en xaml:

IsEnabled="{Binding IsNotReadOnly"}
1
MMM

Suite à la réponse de @ Paul, j'ai écrit ce qui suit dans le ViewModel:

public bool ShowAtView { get; set; }
public bool InvShowAtView { get { return !ShowAtView; } }

J'espère qu'avoir un extrait ici aidera quelqu'un, probablement un novice comme je le suis.
Et s'il y a une erreur, s'il vous plaît faites le moi savoir!

BTW, je suis également d'accord avec @heltonbiker commenter - c'est certainement la bonne approche seulement si vous ne devez pas l'utiliser plus de 3 fois ...

1
Ofaim

J'ai fait quelque chose de très similaire. J'ai créé ma propriété en coulisse qui permettait la sélection d'une liste déroulante SEULEMENT si la recherche de données était terminée. Lorsque ma fenêtre apparaît pour la première fois, elle lance une commande chargée asynchrone, mais je ne souhaite pas que l'utilisateur clique sur la liste déroulante alors qu'elle charge toujours des données (serait vide, puis serait rempli). Donc, par défaut, la propriété est false, donc je retourne l'inverse dans le getter. Ensuite, lorsque je cherche, je règle la propriété sur true et sur False une fois l'opération terminée.

private bool _isSearching;
public bool IsSearching
{
    get { return !_isSearching; }
    set
    {
        if(_isSearching != value)
        {
            _isSearching = value;
            OnPropertyChanged("IsSearching");
        }
    }
}

public CityViewModel()
{
    LoadedCommand = new DelegateCommandAsync(LoadCity, LoadCanExecute);
}

private async Task LoadCity(object pArg)
{
    IsSearching = true;

    //**Do your searching task here**

    IsSearching = false;
}

private bool LoadCanExecute(object pArg)
{
    return IsSearching;
}

Ensuite, pour la liste déroulante, je peux la lier directement à IsSearching:

<ComboBox ItemsSource="{Binding Cities}" IsEnabled="{Binding IsSearching}" DisplayMemberPath="City" />
1
GregN