WPF में उल्टे बूलियन गुण कैसे बाँधें?


377

मेरे पास जो वस्तु है वह IsReadOnlyसंपत्ति है। यदि यह गुण सत्य है, तो मैं IsEnabledसंपत्ति को एक बटन पर सेट करना चाहूंगा , (उदाहरण के लिए), असत्य को।

मुझे विश्वास है कि मैं इसे आसानी से कर सकता हूं 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>


एह एमएस अच्छी बात है, लेकिन यह अधूरा है
user1005462

जवाबों:


488

आप एक 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
यहाँ पर कुछ बातों पर विचार करना होगा, जो संभवतः मुझे इस एक पर पॉल के उत्तर को चुनने में मदद करेंगे। कोडिंग (अभी के लिए) जब मैं खुद से हूं, तो मुझे एक समाधान के साथ जाने की जरूरत है कि "मुझे" याद होगा, जिसे मैं बार-बार उपयोग करूंगा। मुझे यह भी लगता है कि कम चिंताजनक चीज़ कुछ बेहतर है, और उलटा संपत्ति बनाना बहुत स्पष्ट है, जिससे मुझे याद रखना आसान हो जाता है, साथ ही साथ भविष्य के देवता (आई होप, आई होप), जो मैं जल्दी से देख पाऊंगा। कर रहा था, साथ ही उनके लिए मुझे लौकिक बस के नीचे फेंकना आसान बना रहा था।
रस

17
अपने स्वयं के तर्कों द्वारा, IMHO कनवर्टर समाधान दीर्घकालिक में बेहतर है: आपको केवल एक बार कनवर्टर लिखना होगा, और उसके बाद आप इसे बार-बार उपयोग कर सकते हैं। यदि आप नई संपत्ति के लिए जाते हैं, तो आपको इसे हर उस वर्ग में फिर से लिखना होगा, जिसकी ज़रूरत है ...
थॉमस लेवेस्क

51
मैं एक ही दृष्टिकोण का उपयोग कर रहा हूँ ... लेकिन यह पांडा सा ... = (
मैक्स

27
की तुलना में !, यह कुछ लंबे-घुमावदार कोड है ... लोग जो कुछ भी महसूस करते हैं उसे अलग करने के लिए पागल मात्रा में जाते हैं जो उनके गरीब डिजाइनरों से "कोड" है। अतिरिक्त अतिरिक्त दर्दनाक जब मैं दोनों कोडर और डिजाइनर हूँ।
रोमन स्टार्कोव

10
खुद सहित कई लोग इसे ओवर-इंजीनियरिंग का एक प्रमुख उदाहरण मानते हैं। मैं नीचे पॉल अलेक्जेंडर पोस्ट के रूप में एक औंधा संपत्ति का उपयोग करने का सुझाव देता हूं।
क्रिश्चियन वेस्टमैन

99

क्या आपने कोई IsNotReadOnlyसंपत्ति मानी है? यदि ऑब्जेक्ट बाध्य किया जा रहा है, तो MVVM डोमेन में एक ViewModel है, तो अतिरिक्त संपत्ति सही अर्थ बनाती है। यदि यह एक प्रत्यक्ष इकाई मॉडल है, तो आप अपनी इकाई के एक विशेष ViewModel को रचना और प्रस्तुत करने पर विचार कर सकते हैं।


5
मैंने इस दृष्टिकोण का उपयोग करके बस उसी समस्या को हल किया है और मैं मानता हूं कि न केवल यह अधिक सुरुचिपूर्ण है, बल्कि एक कनवर्टर का उपयोग करने की तुलना में बहुत अधिक बनाए रखने योग्य है।
अलिम्बाडा

28
मैं असहमत हूं कि यह दृष्टिकोण मूल्य परिवर्तक से बेहतर है। यदि आपको कई NotProperty उदाहरणों की आवश्यकता है, तो यह अधिक कोड भी उत्पन्न करता है।
थिरू

25
MVVM कोड नहीं लिखने के बारे में नहीं है, यह समस्याओं को घोषित रूप से हल करने के बारे में है। इसके लिए कनवर्टर है सही समाधान।
जेफ

14
इस समाधान के साथ समस्या यह है कि यदि आपके पास 100 ऑब्जेक्ट हैं, तो आपको सभी 100 ऑब्जेक्ट में एक IsNotReadOnly गुण जोड़ना होगा। उस संपत्ति को एक डिपेंडेंसीप्रॉपर्टी होना होगा। यह सभी 100 वस्तुओं या कोड की 1000 लाइनों के लिए कोड की लगभग 10 लाइनें जोड़ता है। कनवर्टर कोड की 20 लाइनें है। 1000 लाइनें या 20 लाइनें। तुम किसे चुनोगे?
रिहायस

8
इसके लिए एक सामान्य कहावत है: इसे एक बार करें, दो बार करें और फिर स्वचालित करें। संदेह में, मैं इस उत्तर का उपयोग पहली बार किसी परियोजना में आवश्यक होगा, और फिर अगर चीजें बढ़ती हैं, तो मैं स्वीकृत उत्तर का उपयोग करूंगा। लेकिन कनवर्टर स्निपेट के पूर्व-निर्मित होने से इसका उपयोग करना कम कठिन हो सकता है।
हेलटोनबीकर 17

71

Standart बाइंडिंग के साथ आपको उन कन्वर्टर्स का उपयोग करने की आवश्यकता होती है जो थोड़ा घुमावदार दिखते हैं। इसलिए, मैं आपको अपनी परियोजना 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>एक 'बाइंडिंग' सेटर के लिए मान्य नहीं है। वेल 'संकलन समय त्रुटि
mcalex

21

मैं https://quickconverter.codeplex.com/ का उपयोग करने की सलाह दूंगा

एक बूलियन में घुसना तब के रूप में सरल है: <Button IsEnabled="{qc:Binding '!$P', P={Binding IsReadOnly}}" />

सामान्य रूप से कन्वर्टर्स लिखने के लिए आवश्यक समय को गति देता है।


18
किसी को -1 देते समय, यह स्पष्ट करना अच्छा होगा कि क्यों।
Noxxys

16

मैं चाहता था कि मेरा XAML जितना संभव हो उतना सुरुचिपूर्ण बना रहे, इसलिए मैंने बूल को लपेटने के लिए एक वर्ग बनाया जो मेरे साझा पुस्तकालयों में से एक में रहता है, निहित संचालक वर्ग को मूल रूप से कोड के पीछे एक बूल के रूप में उपयोग करने की अनुमति देते हैं

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 पोस्टफिक्स में वैल्यू या इनवर्ट के साथ बाइंडिंग को पोस्टफिक्स किया

IsEnabled="{Binding IsActive.Value}"

IsEnabled="{Binding IsActive.Invert}"

1
नकारात्मक पक्ष यह है कि आपको उन सभी कोड को बदलना होगा, जिन्होंने इसकी तुलना अन्य boolटाइप एक्सप्रेशन / वेरिएबल्स से की है, यहाँ तक कि उलटा मान भी नहीं दिया है। मैं इसके बजाय एक "नहीं" एक्सटेंशन विधि जोड़ना होगा Boolean Struct
टॉम

1
रवींद्र! कोई बात नहीं। होना ही था भूल गए Propertyबनाम Methodके लिए Binding। मेरा "डाउनसाइड" बयान अभी भी लागू होता है। Btw, 'बूलियन' "नहीं" एक्सटेंशन विधि अभी भी "से बचने के लिए उपयोगी है!" ऑपरेटर जो आसानी से याद किया जाता है जब यह (जैसा कि अक्सर होता है) वर्णों के बगल में एम्बेडेड होता है जो इसके जैसा दिखता है (यानी एक / अधिक "(" 's' और 'l' 's और "I" s)।
टॉम

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
बहुत बढ़िया जवाब। जोड़ने के लिए एक बात है, इसका उपयोग करके आप संपत्ति के लिए IsCotReadOnly के लिए सेटटर में IsRotOnly के लिए PropertyChanged घटना को बढ़ाते हैं। इससे आप सुनिश्चित करेंगे कि UI सही तरीके से अपडेट हो गया है।
मुहनाद

यह स्वीकृत उत्तर होना चाहिए क्योंकि यह सबसे सरल है।
गाबनीम

2

नहीं पता कि यह XAML के लिए प्रासंगिक है, लेकिन अपने सरल विंडोज ऐप में मैंने मैन्युअल रूप से बाइंडिंग बनाई और एक फॉर्मेट इवेंट हैंडलर जोड़ा।

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और ParseWinForms बाइंडिंग में घटनाओं WPF कनवर्टर के लगभग बराबर हैं।
अलेजांद्रो

2

मुझे उलटा समस्या थी, लेकिन एक साफ समाधान।

प्रेरणा यह थी कि XAML डिज़ाइनर एक खाली नियंत्रण दिखाएगा, जैसे कि कोई datacontext / no MyValues(आइटम्स) नहीं था।

प्रारंभिक कोड: खाली होने पर नियंत्रण छिपाएंMyValues । बेहतर कोड: जब शून्य या खाली न हो तो नियंत्रण दिखाएंMyValues

समस्या यह है कि '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

💡 नेट कोर समाधान 💡

अशक्त स्थिति को संभालता है और एक अपवाद नहीं फेंकता है, लेकिन 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

@ पॉल के उत्तर के बाद, मैंने ViewModel में निम्नलिखित लिखा:

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

मुझे उम्मीद है कि यहां एक स्निपेट होने से किसी को मदद मिलेगी, शायद नौसिखिया जैसा मैं हूं।
और अगर कोई गलती है, तो कृपया मुझे बताएं!

BTW, मैं @heltonbiker टिप्पणी से भी सहमत हूं - यह निश्चित रूप से केवल सही दृष्टिकोण है यदि आपको इसे 3 महीने से अधिक उपयोग नहीं करना है ...


2
एक पूर्ण संपत्ति नहीं होने और "OnPropertyChanged" का अभाव होने से यह काम नहीं करेगा। 1 या 2 जवाब, जो मैं उपयोग कर रहा हूं, केस पर निर्भर करता है। जब तक आप प्रिज्म की तरह एक फ्रेमवर्क का उपयोग नहीं कर रहे हैं, जहां फ्रेमवर्क को पता है कि "संदर्भित" गुणों को कब अपडेट करना है। फिर यह आपके द्वारा सुझाए गए (लेकिन पूरी संपत्ति के साथ) जैसे कुछ का उपयोग करने के बीच टॉस है, और जवाब 1
ओइवाई

1

मैंने कुछ ऐसा ही किया। मैंने अपनी संपत्ति को पर्दे के पीछे बनाया जो केवल एक कॉम्बोक्स के चयन को सक्षम करता था यदि यह डेटा खोज रहा था। जब मेरी विंडो पहली बार दिखाई देती है, तो यह एक async लोड कमांड को लॉन्च करता है लेकिन मैं नहीं चाहता कि उपयोगकर्ता कॉम्बोक्स पर क्लिक करे जबकि यह अभी भी डेटा लोड कर रहा है (खाली होगा, फिर आबाद होगा)। इसलिए डिफ़ॉल्ट रूप से संपत्ति झूठी है इसलिए मैं गेट्टर में उलटा लौटाता हूं। फिर जब मैं खोज रहा हूं तो मैंने संपत्ति को सही पर सेट किया है और पूर्ण होने पर वापस झूठे में।

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

फिर कॉम्बोक्स के लिए मैं इसे सीधे खोज में बाँध सकता हूँ:

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

0

मैं @ ओफिम की तरह एक समान दृष्टिकोण का उपयोग करता हूं

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

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

public bool EnableSaveButton => !jobSaved;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.