Używam BooleanToVisibilityConverter
w WPF do powiązania Visibility
właściwości formantu z plikiem Boolean
. Działa to dobrze, ale chciałbym, aby jedna z kontrolek ukryła się, jeśli wartość logiczna jest true
, i pokazała, czy tak jest false
.
Używam BooleanToVisibilityConverter
w WPF do powiązania Visibility
właściwości formantu z plikiem Boolean
. Działa to dobrze, ale chciałbym, aby jedna z kontrolek ukryła się, jeśli wartość logiczna jest true
, i pokazała, czy tak jest false
.
Odpowiedzi:
Zaimplementuj własną implementację IValueConverter. Przykładowa implementacja znajduje się pod adresem
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx
W swojej metodzie Convert, niech zwraca wartości, które chcesz, zamiast wartości domyślnych.
Zamiast odwracania, możesz osiągnąć ten sam cel, używając ogólnej IValueConverter
implementacji, która może konwertować wartość logiczną na konfigurowalne wartości docelowe dla prawdy i fałszu. Poniżej jedna taka implementacja:
public class BooleanConverter<T> : IValueConverter
{
public BooleanConverter(T trueValue, T falseValue)
{
True = trueValue;
False = falseValue;
}
public T True { get; set; }
public T False { get; set; }
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool && ((bool) value) ? True : False;
}
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is T && EqualityComparer<T>.Default.Equals((T) value, True);
}
}
Następnie podklasuj go, gdzie T
jest Visibility
:
public sealed class BooleanToVisibilityConverter : BooleanConverter<Visibility>
{
public BooleanToVisibilityConverter() :
base(Visibility.Visible, Visibility.Collapsed) {}
}
Na koniec w ten sposób można użyć BooleanToVisibilityConverter
powyższego w XAML i skonfigurować go, na przykład, do używania Collapsed
dla true i Visible
for false:
<Application.Resources>
<app:BooleanToVisibilityConverter
x:Key="BooleanToVisibilityConverter"
True="Collapsed"
False="Visible" />
</Application.Resources>
Ta inwersja jest przydatna, gdy chcesz powiązać z właściwością logiczną o nazwie IsHidden
przeciwieństwo IsVisible
.
Visibility
właściwości w modelu widoku.
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public sealed class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var flag = false;
if (value is bool)
{
flag = (bool)value;
}
else if (value is bool?)
{
var nullable = (bool?)value;
flag = nullable.GetValueOrDefault();
}
if (parameter != null)
{
if (bool.Parse((string)parameter))
{
flag = !flag;
}
}
if (flag)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var back = ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
if (parameter != null)
{
if ((bool)parameter)
{
back = !back;
}
}
return back;
}
}
a następnie przekaż wartość true lub false jako parametr ConverterParameter
<Grid.Visibility>
<Binding Path="IsYesNoButtonSetVisible" Converter="{StaticResource booleanToVisibilityConverter}" ConverterParameter="true"/>
</Grid.Visibility>
else if (value is bool?)
strony ReSharper mówi mi: „Wyrażenie jest zawsze fałszywe”. Ponadto, if (flag)
część może być zapisane jako bardziej zwięźle return flag ? Visibility.Visible : Visibility.Collapsed;
.
var nullable = (bool?)value; flag = nullable.GetValueOrDefault();
można uczynić znacznie krótszym i prostszym:flag = (bool?)value ?? false;
Napisz własne to na razie najlepsze rozwiązanie. Oto przykład konwertera, który może działać zarówno w trybie normalnym, jak i odwróconym. Jeśli masz z tym jakieś problemy, po prostu zapytaj.
[ValueConversion(typeof(bool), typeof(Visibility))]
public class InvertableBooleanToVisibilityConverter : IValueConverter
{
enum Parameters
{
Normal, Inverted
}
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var boolValue = (bool)value;
var direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter);
if(direction == Parameters.Inverted)
return !boolValue? Visibility.Visible : Visibility.Collapsed;
return boolValue? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
}
<UserControl.Resources>
<Converters:InvertableBooleanToVisibilityConverter x:Key="_Converter"/>
</UserControl.Resources>
<Button Visibility="{Binding IsRunning, Converter={StaticResource _Converter}, ConverterParameter=Inverted}">Start</Button>
BooleanToVisibilityConverter
, sprawdzając parametr na null:Parameter direction = Parameter.Normal; if (parameter != null) direction = (Parameter)Enum.Parse(typeof(Parameter), (string)parameter);
Istnieje również projekt konwerterów WPF w Codeplex. W swojej dokumentacji mówią, że możesz użyć ich MapConverter do konwersji z wyliczenia Visibility do bool
<Label>
<Label.Visible>
<Binding Path="IsVisible">
<Binding.Converter>
<con:MapConverter>
<con:Mapping From="True" To="{x:Static Visibility.Visible}"/>
<con:Mapping From="False" To="{x:Static Visibility.Hidden}"/>
</con:MapConverter>
</Binding.Converter>
</Binding>
</Label.Visible>
</Label>
Jeszcze jeden sposób powiązania wartości logicznej ViewModel (IsButtonVisible) z właściwością widoczności kontrolki XAML. Bez kodowania, bez konwersji, tylko stylizacja.
<Style TargetType={x:Type Button} x:Key="HideShow">
<Style.Triggers>
<DataTrigger Binding="{Binding IsButtonVisible}" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Button Style="{StaticResource HideShow}">Hello</Button>
Albo prawdziwie leniwy mężczyzna, po prostu skorzystaj z tego, co już jest i odwróć to:
public class InverseBooleanToVisibilityConverter : IValueConverter
{
private BooleanToVisibilityConverter _converter = new BooleanToVisibilityConverter();
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var result = _converter.Convert(value, targetType, parameter, culture) as Visibility?;
return result == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var result = _converter.ConvertBack(value, targetType, parameter, culture) as bool?;
return result == true ? false : true;
}
}
Jeśli nie lubisz pisać niestandardowego konwertera, możesz użyć wyzwalaczy danych, aby rozwiązać ten problem:
<Style.Triggers>
<DataTrigger Binding="{Binding YourBinaryOption}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding YourBinaryOption}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
Właśnie napisałem o tym post. Wykorzystałem podobny pomysł, co Michael Hohlios. Tylko użyłem Właściwości zamiast „parametru obiektu”.
Widoczność powiązania z wartością bool w WPF
Using Properties sprawia, że moim zdaniem jest bardziej czytelny.
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True" />
Oto jeden, który napisałem i często używam. Używa boolowskiego parametru konwertera, który wskazuje, czy należy odwrócić wartość, a następnie używa XOR do wykonania negacji:
[ValueConversion(typeof(bool), typeof(System.Windows.Visibility))]
public class BooleanVisibilityConverter : IValueConverter
{
System.Windows.Visibility _visibilityWhenFalse = System.Windows.Visibility.Collapsed;
/// <summary>
/// Gets or sets the <see cref="System.Windows.Visibility"/> value to use when the value is false. Defaults to collapsed.
/// </summary>
public System.Windows.Visibility VisibilityWhenFalse
{
get { return _visibilityWhenFalse; }
set { _visibilityWhenFalse = value; }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool negateValue;
Boolean.TryParse(parameter as string, out negateValue);
bool val = negateValue ^ System.Convert.ToBoolean(value); //Negate the value when negateValue is true using XOR
return val ? System.Windows.Visibility.Visible : _visibilityWhenFalse;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool negateValue;
Boolean.TryParse(parameter as string, out negateValue);
if ((System.Windows.Visibility)value == System.Windows.Visibility.Visible)
return true ^ negateValue;
else
return false ^ negateValue;
}
}
Oto tabela prawdy XOR w celach informacyjnych:
XOR
x y XOR
---------
0 0 0
0 1 1
1 0 1
1 1 0
Szukałem bardziej ogólnej odpowiedzi, ale nie mogłem jej znaleźć. Napisałem konwerter, który może pomóc innym.
Opiera się na fakcie, że musimy wyróżnić sześć różnych przypadków:
Oto moja implementacja dla pierwszych 4 przypadków:
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BooleanToVisibilityConverter : IValueConverter
{
enum Types
{
/// <summary>
/// True to Visible, False to Collapsed
/// </summary>
t2v_f2c,
/// <summary>
/// True to Visible, False to Hidden
/// </summary>
t2v_f2h,
/// <summary>
/// True to Collapsed, False to Visible
/// </summary>
t2c_f2v,
/// <summary>
/// True to Hidden, False to Visible
/// </summary>
t2h_f2v,
}
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var b = (bool)value;
string p = (string)parameter;
var type = (Types)Enum.Parse(typeof(Types), (string)parameter);
switch (type)
{
case Types.t2v_f2c:
return b ? Visibility.Visible : Visibility.Collapsed;
case Types.t2v_f2h:
return b ? Visibility.Visible : Visibility.Hidden;
case Types.t2c_f2v:
return b ? Visibility.Collapsed : Visibility.Visible;
case Types.t2h_f2v:
return b ? Visibility.Hidden : Visibility.Visible;
}
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
var v = (Visibility)value;
string p = (string)parameter;
var type = (Types)Enum.Parse(typeof(Types), (string)parameter);
switch (type)
{
case Types.t2v_f2c:
if (v == Visibility.Visible)
return true;
else if (v == Visibility.Collapsed)
return false;
break;
case Types.t2v_f2h:
if (v == Visibility.Visible)
return true;
else if (v == Visibility.Hidden)
return false;
break;
case Types.t2c_f2v:
if (v == Visibility.Visible)
return false;
else if (v == Visibility.Collapsed)
return true;
break;
case Types.t2h_f2v:
if (v == Visibility.Visible)
return false;
else if (v == Visibility.Hidden)
return true;
break;
}
throw new InvalidOperationException();
}
}
przykład:
Visibility="{Binding HasItems, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter='t2v_f2c'}"
Myślę, że parametry są łatwe do zapamiętania.
Mam nadzieję, że to komuś pomoże.
Możesz użyć programu QuickConverter .
Dzięki QuickConverter możesz napisać logikę konwertera w linii z BindingExpression
Oto odwrócony konwerter BooleanToVisibility:
Visibility="{qc:Binding '!$P ? Visibility.Visible : Visibility.Collapsed', P={Binding Example}}"
Możesz dodać QuickConverter za pośrednictwem NuGet. Zapoznaj się z dokumentacją dotyczącą instalacji. Link: https://quickconverter.codeplex.com/
Prosta wersja jednokierunkowa, której można użyć w następujący sposób:
Visibility="{Binding IsHidden, Converter={x:Static Ui:Converters.BooleanToVisibility}, ConverterParameter=true}
można zaimplementować w następujący sposób:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var invert = false;
if (parameter != null)
{
invert = Boolean.Parse(parameter.ToString());
}
var booleanValue = (bool) value;
return ((booleanValue && !invert) || (!booleanValue && invert))
? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Konwertuj wszystko na wszystko (bool, string, enum itp.):
public class EverythingConverterValue
{
public object ConditionValue { get; set; }
public object ResultValue { get; set; }
}
public class EverythingConverterList : List<EverythingConverterValue>
{
}
public class EverythingConverter : IValueConverter
{
public EverythingConverterList Conditions { get; set; } = new EverythingConverterList();
public object NullResultValue { get; set; }
public object NullBackValue { get; set; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Conditions.Where(x => x.ConditionValue.Equals(value)).Select(x => x.ResultValue).FirstOrDefault() ?? NullResultValue;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Conditions.Where(x => x.ResultValue.Equals(value)).Select(x => x.ConditionValue).FirstOrDefault() ?? NullBackValue;
}
}
Przykłady XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:MvvmGo.Converters;assembly=MvvmGo.WindowsWPF"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<conv:EverythingConverter x:Key="BooleanToVisibilityConverter">
<conv:EverythingConverter.Conditions>
<conv:EverythingConverterValue ResultValue="{x:Static Visibility.Visible}">
<conv:EverythingConverterValue.ConditionValue>
<sys:Boolean>True</sys:Boolean>
</conv:EverythingConverterValue.ConditionValue>
</conv:EverythingConverterValue>
<conv:EverythingConverterValue ResultValue="{x:Static Visibility.Collapsed}">
<conv:EverythingConverterValue.ConditionValue>
<sys:Boolean>False</sys:Boolean>
</conv:EverythingConverterValue.ConditionValue>
</conv:EverythingConverterValue>
</conv:EverythingConverter.Conditions>
</conv:EverythingConverter>
<conv:EverythingConverter x:Key="InvertBooleanToVisibilityConverter">
<conv:EverythingConverter.Conditions>
<conv:EverythingConverterValue ResultValue="{x:Static Visibility.Visible}">
<conv:EverythingConverterValue.ConditionValue>
<sys:Boolean>False</sys:Boolean>
</conv:EverythingConverterValue.ConditionValue>
</conv:EverythingConverterValue>
<conv:EverythingConverterValue ResultValue="{x:Static Visibility.Collapsed}">
<conv:EverythingConverterValue.ConditionValue>
<sys:Boolean>True</sys:Boolean>
</conv:EverythingConverterValue.ConditionValue>
</conv:EverythingConverterValue>
</conv:EverythingConverter.Conditions>
</conv:EverythingConverter>
<conv:EverythingConverter x:Key="MarriedConverter" NullResultValue="Single">
<conv:EverythingConverter.Conditions>
<conv:EverythingConverterValue ResultValue="Married">
<conv:EverythingConverterValue.ConditionValue>
<sys:Boolean>True</sys:Boolean>
</conv:EverythingConverterValue.ConditionValue>
</conv:EverythingConverterValue>
<conv:EverythingConverterValue ResultValue="Single">
<conv:EverythingConverterValue.ConditionValue>
<sys:Boolean>False</sys:Boolean>
</conv:EverythingConverterValue.ConditionValue>
</conv:EverythingConverterValue>
</conv:EverythingConverter.Conditions>
<conv:EverythingConverter.NullBackValue>
<sys:Boolean>False</sys:Boolean>
</conv:EverythingConverter.NullBackValue>
</conv:EverythingConverter>
Zamiast pisać własny kod / wymyślać na nowo, rozważ użycie CalcBinding :
Automatic two way convertion of bool expression to Visibility and back if target property has such type: description
<Button Visibility="{c:Binding !IsChecked}" />
<Button Visibility="{c:Binding IsChecked, FalseToVisibility=Hidden}" />
CalcBinding jest również bardzo przydatne w wielu innych scenariuszach.
Wiem, że to jest przestarzałe, ale nie musisz niczego ponownie wdrażać.
To, co zrobiłem, to zanegowanie wartości nieruchomości w następujący sposób:
<!-- XAML code -->
<StackPanel Name="x" Visibility="{Binding Path=Specials, ElementName=MyWindow, Converter={StaticResource BooleanToVisibilityConverter}}"></StackPanel>
<StackPanel Name="y" Visibility="{Binding Path=NotSpecials, ElementName=MyWindow, Converter={StaticResource BooleanToVisibilityConverter}}"></StackPanel>
....
//Code behind
public bool Specials
{
get { return (bool) GetValue(SpecialsProperty); }
set
{
NotSpecials= !value;
SetValue(SpecialsProperty, value);
}
}
public bool NotSpecials
{
get { return (bool) GetValue(NotSpecialsProperty); }
set { SetValue(NotSpecialsProperty, value); }
}
I działa dobrze!
Czy coś mi brakuje?