WPF'de ters boole özellikleri nasıl bağlanır?


378

Sahip olduğum şey, IsReadOnly özelliği . Bu özellik true olursa IsEnabled, bir Düğme (örneğin), özelliği false olarak ayarlamak istiyorum.

Bunu olabildiğince kolay yapabileceğime inanmak istiyorum IsEnabled="{Binding Path=!IsReadOnly}" ancak bu WPF ile uçmuyor.

Tüm stil ayarlarından geçmek zorunda kaldım mı? Bir boolü başka bir boolun tersine ayarlamak kadar basit bir şey için çok garip görünüyor.

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


Eh ms iyi bir şey yapmak ama eksik
user1005462

Yanıtlar:


488

Sizin için bir bool özelliğini tersine çeviren bir ValueConverter kullanabilirsiniz.

XAML:

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

Dönüştürücü:

[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
Burada dikkate almam gereken birkaç şey var, bu da muhtemelen Paul'ün bu konudaki cevabını seçmemi sağlayacak. Kodlama yaparken kendimim (şimdilik), bu yüzden hatırlayacağım ve tekrar tekrar kullanacağım bir çözümle gitmem gerekiyor. Ayrıca daha az garip bir şeyin daha iyi olduğunu ve ters bir özellik yaratmanın çok açık olduğunu hissediyorum, hatırlamamı ve gelecekteki geliştiricilerin (Umarım, Umarım), ne olduğumu hızlı bir şekilde görebilmeyi kolaylaştırır beni meşhur otobüse atmalarını kolaylaştırıyordu.
Russ

17
Kendi argümanlarınızla, IMHO dönüştürücü çözümü uzun vadede daha iyidir: dönüştürücüyü yalnızca bir kez yazmanız gerekir ve bundan sonra tekrar tekrar kullanabilirsiniz. Yeni mülk için giderseniz, ihtiyacı olan her sınıfta yeniden yazmanız gerekecek ...
Thomas Levesque

51
Aynı yaklaşımı kullanıyorum ... ama panda saaad yapıyor ... = (
Max Galkin

27
Buna kıyasla !, bu uzun soluklu bir kod ... İnsanlar "kod" olduğunu düşündüklerini bu kötü tasarımcılardan ayırmak için çılgınca çaba harcıyorlar. Hem kodlayıcı hem de tasarımcı olduğum zaman ekstra ekstra acı verici.
Roman Starkov

10
ben dahil birçok insan bunu aşırı mühendislik için en iyi örnek olarak görüyor. Aşağıdaki Paul Alexander yazısında olduğu gibi ters çevrilmiş bir özellik kullanmanızı öneririz.
Christian Westman

99

Bir IsNotReadOnlymülk mü düşündünüz ? Bağlanan nesne bir MVVM etki alanında bir ViewModel ise, ek özellik mükemmeldir. Doğrudan bir Varlık modeliyse, kompozisyonunuzu ve varlığınızın özel bir ViewModel'ini forma sunmayı düşünebilirsiniz.


5
Aynı problemi bu yaklaşımı kullanarak çözdüm ve sadece daha zarif değil, aynı zamanda bir Dönüştürücü kullanmaktan daha sürdürülebilir olduğunu kabul ediyorum.
alimbada

28
Bu yaklaşımın değer dönüştürücüsünden daha iyi olduğuna katılmıyorum. Ayrıca birkaç NotProperty örneğine ihtiyacınız varsa daha fazla kod üretir.
Thiru

25
MVVM kod yazmamak değil, sorunları deklaratif olarak çözmekle ilgilidir. Bu amaçla, dönüştürücü olan doğru çözüm.
Jeff

14
Bu çözümle ilgili sorun, 100 nesneniz varsa, 100 nesnenin tümüne bir IsNotReadOnly özelliği eklemeniz gerektiğidir. Bu mülkün bir Bağımlılık Mülkiyeti olması gerekir. Bu, 100 nesneye veya 1000 kod satırına yaklaşık 10 kod satırı ekler. Dönüştürücü 20 kod satırıdır. 1000 satır veya 20 satır. Hangi seçerdiniz?
Rhyous

8
Bunun ortak bir sözü vardır: bir kez yapın, iki kez yapın ve sonra otomatikleştirin. Kuşkusuz, bu cevabı bir projede ilk ihtiyaç duyulduğunda kullanırım ve sonra işler büyürse kabul edilen cevabı kullanırdım. Ancak, dönüştürücü snippet'in önceden yapılmış olması kullanımı daha az zorlaştırabilir.
heltonbiker

71

Standart ciltleme ile biraz rüzgarlı görünen dönüştürücüler kullanmanız gerekir. Bu nedenle, bu sorunu ve diğerlerini çözmek için özel olarak geliştirilen CalcBinding projeme bakmanızı tavsiye ederim . Gelişmiş ciltleme ile birçok kaynak özellikli ifadeleri doğrudan xaml içine yazabilirsiniz. Söyle, şöyle bir şey yazabilirsin:

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

veya

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

veya

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

veya

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

Burada A, B, C, IsChecked - viewModel özellikleri ve düzgün çalışır


6
QuickConverter daha güçlü olmasına rağmen, CalcBinding modunu okunabilir - kullanılabilir buluyorum.
xmedeko

3
Bu harika bir araç. Keşke 5 yıl önce var olsaydı!
jugg1es

Parlak bir araç, ama stilleri düşüyor. <Setter.Value><cb:Binding Path="!IsReadOnly" /></Setter.Value>Bir 'Bağlama' Setter.Value' derleme zamanı hatası için geçerli değil alır
mcalex

21

Https://quickconverter.codeplex.com/ adresini kullanmanızı öneririm

Bir boole tersine çevirmek şu kadar basittir: <Button IsEnabled="{qc:Binding '!$P', P={Binding IsReadOnly}}" />

Bu, dönüştürücü yazmak için normalde gereken süreyi hızlandırır.


19
Birine -1 verirken, nedenini açıklamak güzel olurdu.
Noxxys

16

XAML'ımın olabildiğince zarif kalmasını istedim, bu yüzden paylaşılan kütüphanelerimden birinde bulunan bool'u sarmak için bir sınıf oluşturdum, örtük operatörler sınıfın sorunsuz bir şekilde arka planda kod olarak bir bool olarak kullanılmasına izin veriyor

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

}

Projeniz için gereken tek değişiklik, ters çevirmek istediğiniz mülkün bool yerine bunu döndürmesini sağlamaktır

    public InvertableBool IsActive 
    { 
        get 
        { 
            return true; 
        } 
    }

Ve XAML postfix'te bağlamayı Değer veya Ters Çevir ile

IsEnabled="{Binding IsActive.Value}"

IsEnabled="{Binding IsActive.Invert}"

1
Dezavantajı, boolters değerle referansta bulunmasa bile, onu diğer Tür İfadeleri / Değişkenleri ile karşılaştıran / atanan tüm kodları değiştirmeniz gerekmesidir. Bunun yerine "Not" Uzantısı Yöntemi eklersiniz Boolean Struct.
Tom

1
Doh! Boşver. PropertyUnutmak Methodiçin vs olması gerekiyordu Binding. "Dezavantaj" ifadem hala geçerlidir. Btw, 'Boolean` "Not" Genişletme Yöntemi hala "!" (Sık sık olduğu gibi) benzeyen karakterlerin yanına gömüldüğünde kolayca özlenen operatör (yani bir / daha fazla "(" ve "l" ve "I")
Tom

10

Bu aynı zamanda nullable bools için de çalışır.

 [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

Görünüm modelinize, ters değer döndürecek bir özellik daha ekleyin. Ve bunu düğmeye bağla. Sevmek;

görünüm modelinde:

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

xaml içinde:

IsEnabled="{Binding IsNotReadOnly"}

1
Mükemmel cevap. Eklenecek bir şey, bunu kullanarak IsRotOnly özelliğinin ayarlayıcısında IsNotReadOnly için PropertyChanged olayını daha iyi yükseltirsiniz. Bununla kullanıcı arayüzünün doğru bir şekilde güncellendiğinden emin olursunuz.
Muhannad

Bu, en basit olduğu için kabul edilen cevap olmalıdır.
gabnaim

2

Bunun XAML ile alakalı olup olmadığını bilmiyorum, ancak basit Windows uygulamamda bağlayıcıyı manuel olarak oluşturdum ve bir Format olay işleyicisi ekledim.

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
Dönüştürücü yaklaşımından hemen hemen aynı görünüyor. Formatve ParseWinForms bağlarındaki olaylar kabaca WPF dönüştürücüsüyle eşdeğerdir.
Alejandro

2

Bir ters çevirme problemim vardı, ama temiz bir çözüm.

Motivasyon, XAML tasarımcısının boş bir kontrol göstermesiydi, örneğin veri içeriği olmadığında / MyValues veri içeriği (itemssource) olmadığında.

Başlangıç ​​kodu: boş olduğunda kontrolü gizleMyValues . Geliştirilmiş kod: ne zaman kontrolü gösterMyValues boş veya boş DEĞİL .

Tabii sorun, 0 öğenin tersi olan '1 veya daha fazla öğenin' nasıl ifade edileceğidir.

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

Ekleyerek çözdüm:

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

Ergo ciltleme için varsayılanı ayarlar. Tabii ki bu her türlü ters problem için işe yaramaz, ama temiz kodla bana yardımcı oldu.


2

💡 .Net Çekirdek Çözüm 💡

Boş durumu işler ve bir istisna atmaz, ancak truehiçbir değer sunulmazsa geri döner ; aksi takdirde girilen Boole'yi alır ve tersine çevirir.

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 Tüm dönüştürücü statiklerimi app.xaml dosyasına koymayı seviyorum, böylece projenin pencereleri / sayfaları / kontrolleri boyunca yeniden tanımlamak zorunda kalmam.

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

Açık converters:olmak gerekirse, gerçek sınıf uygulamasının ( xmlns:converters="clr-namespace:ProvingGround.Converters") ad alanıdır .


1

@ Paul'un cevabını takiben ViewModel'de aşağıdakileri yazdım:

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

Umarım burada bir pasaja sahip olmak birine yardım eder, muhtemelen benim gibi acemi.
Ve bir hata varsa, lütfen bana bildirin!

BTW, @heltonbiker yorumuna da katılıyorum - sadece 3 kereden fazla kullanmanız gerekmiyorsa kesinlikle doğru yaklaşım ...


2
Tam özellik olmamak ve "OnPropertyChanged" eksik bu işe yaramaz. 1. veya 2. cevaplar duruma göre kullanıyorum. Frameowkr'un "yönlendirilen" özellikleri ne zaman güncelleyeceğini bildiği Prism gibi bir çerçeve kullanmadığınız sürece. O zaman önerdiğiniz gibi bir şey kullanmak (ama tam mülkiyet ile) arasında bir atmak ve cevap 1
Oyiwai

1

Çok benzer bir şey yaptım. Mülkümü SADECE veri aramayı bitirmişse bir combobox seçimini etkinleştiren sahnelerin arkasında oluşturdum. Pencerem ilk göründüğünde, zaman uyumsuz yüklü bir komut başlatır, ancak kullanıcının hala veri yüklerken birleşik giriş kutusuna tıklamasını istemiyorum (boş olur, daha sonra doldurulur). Böylece varsayılan getter tersi özelliği yanlıştır. Sonra arama yaparken özelliği true olarak ayarlayın ve tamamlandığında false olarak geri ayarlayın.

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

Sonra açılan kutu için doğrudan IsSearching bağlayabilirsiniz:

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

0

@Ofaim gibi benzer bir yaklaşım kullanıyorum

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

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

public bool EnableSaveButton => !jobSaved;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.