如何在WPF中绑定反向布尔属性?


377

我所拥有的是具有IsReadOnly属性的对象。如果此属性为true,我想将IsEnabledButton(例如)上的属性设置为false。

我想相信我可以像IsEnabled="{Binding Path=!IsReadOnly}"WPF 一样轻松地做到这一点。

我是否不得不经历所有样式设置?对于将一个布尔值设置为另一个布尔值的倒数这样的简单操作来说,似乎太罗word了。

<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>


嗯,ms做的很好,但没有完成
user1005462

Answers:


488

您可以使用ValueConverter为您反转bool属性。

XAML:

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

转换器:

[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
    }

8
在这里我需要考虑几件事,这可能会让我选择@Paul的答案。我自己编码(现在),所以我需要一个“我”会记住的解决方案,我将反复使用它。我还觉得少说些什么越好,创建逆属性非常明确,这使我以及未来的开发人员(我希望,我希望)能够轻松记住我,这样做,也使他们更容易把我扔在俗气的公共汽车下。
拉斯(Russ)

17
从您自己的观点来看,恕我直言,转换器解决方案从长远来看是更好的:您只需编写一次转换器,然后您就可以反复使用它。如果您要使用新属性,则必须在每个需要该属性的类中对其进行重写……
Thomas Levesque 2009年

51
我正在使用相同的方法...但是它使panda saaad ... =(
Max Galkin 2010年

27
与相比!,这是一些冗长的代码...人们花了很大的精力才能将自己认为是“代码”的人与那些糟糕的设计师分开。当我既是编码员又是设计师时,会特别痛苦。
罗曼·斯塔科夫

10
包括我在内的许多人都认为这是过度工程的一个典型例子。我建议像下面的Paul Alexander文章中那样使用反向属性。
Christian Westman 2014年

99

你考虑过IsNotReadOnly财产吗?如果要绑定的对象是MVVM域中的ViewModel,则附加属性很有意义。如果它是直接实体模型,则可以考虑组成并将实体的专用ViewModel呈现给表单。


5
我只是使用这种方法解决了相同的问题,并且我同意,与使用Converter相比,它不仅更优雅,而且更易于维护。
alimbada

28
我不同意这种方法比值转换器更好。如果需要几个NotProperty实例,它还会产生更多代码。
提鲁

25
MVVM不是要不编写代码,而是要声明式解决问题。为此,转换器正确的解决方案。
杰夫

14
该解决方案的问题在于,如果您有100个对象,则必须将IsNotReadOnly属性添加到所有100个对象中。该属性必须是DependencyProperty。这会将大约10行代码添加到所有100个对象或1000行代码中。转换器是20行代码。1000线或20线。您会选择哪一个?
Rhyous 2014年

8
俗话说:做一次,做两次,然后自动化。毫无疑问,我会在项目中第一次使用此答案时使用,然后如果情况发展,我将使用公认的答案。但是,预先制作转换器片段可能会降低使用难度。
heltonbiker 2015年

71

使用标准绑定,您需要使用看起来没有大风的转换器。因此,我建议您查看我的项目CalcBinding,该项目是专门为解决此问题而开发的。使用高级绑定,您可以直接在xaml中编写具有许多源属性的表达式。说,您可以编写如下内容:

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

要么

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

要么

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

要么

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

其中A,B,C,IsChecked-viewModel的属性,它将正常工作


6
尽管QuickConverter功能更强大,但我发现CalcBinding模式可读-可用。
xmedeko 2015年

3
这是一个很棒的工具。我希望它存在5年前!
jugg1es

出色的工具,但风格不尽人意。 <Setter.Value><cb:Binding Path="!IsReadOnly" /></Setter.Value>获取“绑定”对Setter.Value无效,编译时错误
mcalex19年


16

我希望XAML尽可能保持优雅,因此我创建了一个类来包装驻留在我的共享库中的bool,隐式运算符允许该类在代码背后无缝地用作bool

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;
    }

}

项目的唯一更改是使要反转的属性返回此值,而不是bool

    public InvertableBool IsActive 
    { 
        get 
        { 
            return true; 
        } 
    }

在XAML后缀中,使用Value或Invert进行绑定

IsEnabled="{Binding IsActive.Value}"

IsEnabled="{Binding IsActive.Invert}"

1
缺点是您必须更改所有与/进行比较的代码,bool甚至将其分配给其他Type Expressions / Variables,甚至不引用该反值。我会改为向中添加“非”扩展方法Boolean Struct
汤姆(Tom)

1
h!没关系。忘记了是PropertyMethodBinding。我的“缺点”声明仍然适用。顺便说一句,“ Boolean”“ Not”扩展方法对于避免“!”仍然很有用。操作这是很容易错过,当它(这是常有的情况)下一个被嵌入到字符,看起来像它(即一个/多个“(”“和‘1’的和“我”的)。
汤姆·

10

这也适用于可空布尔。

 [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
}

4

在视图模型中再添加一个属性,该属性将返回反向值。并将其绑定到按钮。喜欢;

在视图模型中:

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

在xaml中:

IsEnabled="{Binding IsNotReadOnly"}

1
好答案。要添加的一件事是,使用此方法最好在属性IsReadOnly的设置器中引发IsNotReadOnly的PropertyChanged事件。这样,您将确保UI正确更新。
Muhannad

这是最简单的答案,应该被接受。
gabnaim

2

不知道这是否与XAML有关,但是在我的简单Windows应用程序中,我手动创建了绑定并添加了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;
}

1
似乎与转换器方法几乎相同。Format并且Parse在的WinForms绑定事件大致相当于WPF的转换器。
亚历杭德罗(Alejandro)

2

我有一个反转问题,但是一个很好的解决方案。

动机是XAML设计器将显示一个空控件,例如,当没有datacontext / no MyValues(项目源)。

初始代码:为空时隐藏控件MyValues。改进的代码:何时显示控件MyValues不为null或为空。

当然,问题在于如何表达“ 1个或多个项目”,这与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>

我通过添加解决了它:

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

设置绑定的默认设置。当然,这不适用于各种逆问题,但可以用干净的代码帮助我。


2

Net .Net核心解决方案 💡

处理空情况并且不引发异常,但是true如果不提供任何值,则返回;否则,将输入的布尔值取反。

public class BooleanToReverseConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     => !(bool?) value ?? true;

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
     => !(value as bool?);
}

Xaml

IsEnabled="{Binding IsSuccess Converter={StaticResource BooleanToReverseConverter}}"

App.Xaml我喜欢将所有转换器静态变量都放在app.xaml文件中,因此我不必在项目的Windows /页面/控件中重新声明它们。

<Application.Resources>
    <converters:BooleanToReverseConverter x:Key="BooleanToReverseConverter"/>
    <local:FauxVM x:Key="VM" />
</Application.Resources>

需要明确的 converters:是实际类实现(xmlns:converters="clr-namespace:ProvingGround.Converters")的名称空间。


1

遵循@Paul的回答,我在ViewModel中编写了以下内容:

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

我希望这里有一个摘要可以帮助某人,也许像我一样是新手。
如果有错误,请告诉我!

顺便说一句,我也同意@heltonbiker的评论- 只有当您不必使用超过3次时,这绝对是正确的方法...


2
不是完整的属性,并且缺少“ OnPropertyChanged”,这将无法正常工作。根据情况,我使用的是第一个或第二个答案。除非您使用Prism之类的框架,否则frameowkr知道何时更新“引用”属性。然后在使用类似您所建议的内容(但具有完整属性)之间进行折腾,然后回答1
Oyiwai

1

我做了非常相似的事情。我在幕后创建了我的属性,该属性仅在组合框完成搜索数据后才允许选择组合框。当我的窗口首次出现时,它会启动一个异步加载命令,但我不希望用户在仍在加载数据的同时单击组合框(将其为空,然后将其填充)。因此,默认情况下,该属性为false,因此我在getter中返回反函数。然后,当我搜索时,将属性设置为true,并在完成时将其设置为false。

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;
}

然后对于组合框,我可以将其直接绑定到IsSearching:

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

0

我使用类似@Ofaim的方法

private bool jobSaved = true;
private bool JobSaved    
{ 
    get => jobSaved; 
    set
    {
        if (value == jobSaved) return;
        jobSaved = value;

        OnPropertyChanged();
        OnPropertyChanged("EnableSaveButton");
    }
}

public bool EnableSaveButton => !jobSaved;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.