WPFで逆ブールプロパティをバインドする方法


378

私が持っているのは、IsReadOnlyプロパティを持つオブジェクトです。このプロパティがtrueの場合IsEnabled、ボタン(たとえば)のプロパティをfalseに設定します。

私はそれが同じくらい簡単にできると信じたいのですIsEnabled="{Binding Path=!IsReadOnly}"が、それはWPFではうまくいきません。

私はすべてのスタイル設定を通過する必要があることに追いやられていますか?あるブール値を別のブール値の逆に設定するような単純なことには、言い過ぎです。

<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

回答:


488

boolプロパティを反転するValueConverterを使用できます。

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の答えを選ぶようになるでしょう。私は(今のところ)コーディングするときに一人でいるので、「私」が覚えておくソリューションを使用する必要があります。私はまた、言葉遣いが少ない方が良いと感じています。また、inverseプロパティの作成は非常に明示的であり、将来の開発者(I Hope、I Hope)だけでなく、私が何をしているかをすぐに思い出すことができます彼らがことわざのバスの下で私を投げるのを容易にするだけでなく、そうしていました。
Russ

17
あなた自身の主張によれば、長期的に見ればコンバーターソリューションの方が優れています。コンバーターを作成する必要があるのは1回だけで、その後は何度でも再利用できます。新しいプロパティに行く場合、それを必要とするすべてのクラスでそれを書き直す必要があります...
Thomas Levesque '26 / 06/26

51
私は同じアプローチを使用しています...しかし、それはパンダをsaaadします... =(
Max Galkin

27
と比較すると!、これは長い間苦労したコードです...人々は、「コード」であると感じるものをそれらの貧弱なデザイナーから分離するために途方もない量の努力を費やします。私がコーダーでありデザイナーでもあるときは、さらに大変です。
Roman Starkov

10
私を含む多くの人々は、これをオーバーエンジニアリングの典型的な例と考えています。以下のPaul Alexanderの投稿のように、反転したプロパティを使用することをお勧めします。
クリスチャンウェストマン2014年

99

IsNotReadOnlyプロパティを検討しましたか?バインドされるオブジェクトがMVVMドメインのViewModelである場合、追加のプロパティは完全に理にかなっています。それが直接のエンティティモデルである場合は、エンティティの特殊なViewModelを構成してフォームに表示することを検討します。


5
私はこのアプローチを使用して同じ問題を解決しましたが、Converterを使用するよりもエレガントであるだけでなく、はるかに保守しやすいことに同意します。
alimbada

28
このアプローチが値コンバーターよりも優れているとは思いません。複数のNotPropertyインスタンスが必要な場合は、より多くのコードも生成します。
Thiru

25
MVVMは、コードを記述しないことではなく、宣言的に問題を解決することです。そのためには、コンバーター正しい解決策です。
ジェフ

14
このソリューションの問題は、100個のオブジェクトがある場合、100個すべてのオブジェクトにIsNotReadOnlyプロパティを追加する必要があることです。そのプロパティはDependencyPropertyである必要があります。これにより、100個すべてのオブジェクトまたは1000行のコードに約10行のコードが追加されます。Converterは20行のコードです。1000行または20行。どちらを選びますか?
Rhyous、2014年

8
これには一般的な言い回しがあります。1回実行してから2回実行してから自動化します。疑わしいのですが、プロジェクトで初めて必要になったときにこの回答を使用し、状況が改善した場合は、受け入れられた回答を使用します。ただし、コンバータースニペットを事前に作成しておくと、使用が難しくなります。
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

3
これは素晴らしいツールです。5年前にあったらいいのに!
jugg1es

見事なツールですが、スタイルが崩れます。 <Setter.Value><cb:Binding Path="!IsReadOnly" /></Setter.Value>「Binding」を取得すると、Setter.Valueのコンパイル時エラーは無効になります
mcalex

21

https://quickconverter.codeplex.com/を使用することをお勧めします

ブール値の反転は次のように簡単です: <Button IsEnabled="{qc:Binding '!$P', P={Binding IsReadOnly}}" />

これにより、コンバーターの作成に通常必要な時間が短縮されます。


19
誰かに-1を与えるときは、その理由を説明するとよいでしょう。
Noxxys、2014年

16

XAMLをできるだけエレガントなままにしたかったので、共有ライブラリの1つにあるブールをラップするクラスを作成しました。暗黙の演算子を使用すると、コードビハインドでクラスをブールとしてシームレスに使用できます

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

}

プロジェクトに必要な唯一の変更は、反転するプロパティがブール値ではなくこれを返すようにすることです

    public InvertableBool IsActive 
    { 
        get 
        { 
            return true; 
        } 
    }

XAML postfixで、ValueまたはInvertのいずれかを使用したバインディング

IsEnabled="{Binding IsActive.Value}"

IsEnabled="{Binding IsActive.Invert}"

1
欠点は、それを比較したすべてのコードを変更する必要がありbool、逆の値を参照していなくても、他の型式/変数に割り当てなければならないことです。代わりに、「Not」拡張メソッドをに追加しBoolean Structます。
トム

1
どー!気にしないで。でなければならなかった忘れPropertyMethodについてBinding。私の「欠点」の記述はまだ当てはまります。ところで、 'Boolean`の "Not"拡張メソッドは、 "!"を回避するのに役立ちます。それは(多くの場合そうであるように))それ(つまり、1 /より多くの「(」さんと『L』さんと「I」のように見えることを次の文字になるように埋め込まれている場合、オペレータは見逃しやすいされている。
トム・

10

これはnull許容のブール値でも機能します。

 [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

ビューモデルにプロパティをもう1つ追加すると、逆の値が返されます。それをボタンにバインドします。お気に入り;

ビューモデル:

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

XAMLで:

IsEnabled="{Binding IsNotReadOnly"}

1
すばらしい答えです。追加すべきことの1つは、これを使用して、プロパティIsReadOnlyのセッターでIsNotReadOnlyのPropertyChangedイベントを発生させることです。これにより、UIが正しく更新されるようになります。
Muhannad

これが最も簡単なので、これは受け入れられる答えになるはずです。
ガブナイム

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リサイズバインディングのイベントは、WPFコンバータのほぼ同等です。
アレハンドロ

2

私は反転問題を抱えていましたが、きちんとした解決策でした。

動機は、XAMLデザイナが空のコントロールを表示することでした。たとえば、MyValuesデータコンテキスト/ no (itemssource)がない場合です。

初期コード:が空の場合はコントロールを非表示にしMyValuesます。改善されたコード:がnullまたは空でない場合にコントロールを表示しMyValuesます。

もちろん問題は、0アイテムの反対である「1アイテム以上」をどのように表現するかです。

<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 Coreソリューション 💡

nullの状況を処理し、例外をスローしませんが、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ファイルに入れたいので、プロジェクトのウィンドウ、ページ、コントロール全体でそれらを再宣言する必要はありません。

<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"がないため、これは機能しません。ケースに応じて、1つ目または2つ目の回答を使用しています。「紹介された」プロパティをいつ更新するかをframeowkrが知っているPrismのようなフレームワークを使用しているのでない限り。それからそれはあなたが提案したもののようなものを使用することの間のトスです(しかし、完全なプロパティで)、そして答え1
Oyiwai

1

私は非常に似たようなことをしました。データの検索が終了した場合にのみ、コンボボックスの選択を可能にするプロパティを舞台裏で作成しました。ウィンドウが最初に表示されると、非同期で読み込まれたコマンドが起動しますが、まだデータを読み込んでいるときにユーザーがコンボボックスをクリックしないようにします(空の場合は、データが入力されます)。したがって、デフォルトではプロパティはfalseなので、ゲッターで逆を返します。次に、検索時にプロパティを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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.