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>
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
}
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.
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!
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.
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}"
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
}
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.
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;
}
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"}
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 ...
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" />