2017-10-23 27 views
6

मैं अपने viewmodel में चर का एक संग्रह है:WPF मल्टीबाइंडिंग अपेक्षित होने पर स्रोत अपडेट नहीं कर रहा है; सभी को चुनें 'के साथ चेक बॉक्स

public ObservableCollection<ObservableVariable> Variables { get; }= new ObservableCollection<ObservableVariable>(); 

ObservableVariable वर्ग दो गुण है: स्ट्रिंग नाम और चयनित bool; कक्षा INOTifyPropertyChanged लागू करता है,

मेरा लक्ष्य यह संग्रह एक WPF व्यू में एक चेकलिस्ट से जुड़ा हुआ है, और मल्टीबाइंडिंग का उपयोग करके लागू की गई सूची में 'सभी का चयन करें' चेकबॉक्स रखना है। निम्नलिखित छवि वांछित दृश्य को दर्शाती है।

WPF checklist with 'Select All'

नीचे XAML का निरीक्षण करें:

<CheckBox Content="Select All" Name="SelectAllCheckbox"></CheckBox> 
... 
<ListBox ItemsSource="{Binding Variables}"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <CheckBox Content="{Binding Name}"> 
       <CheckBox.IsChecked> 
        <MultiBinding Converter="{StaticResource LogicalOrConverter}" Mode="TwoWay"> 
         <Binding Path="Selected"></Binding> 
         <Binding ElementName="SelectAllCheckbox" Path="IsChecked"></Binding> 
        </MultiBinding> 
       </CheckBox.IsChecked> 
      </CheckBox> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

LogicalOrConverter bools के किसी भी संख्या लेता है; यदि कोई सच है, तो सच हो जाओ।

जैसा कि आप ऊपर देख सकते हैं प्रत्येक चेकबॉक्स व्यूमोडेल में एक चर के लिए बाध्य है और 'सभी का चयन करें' चेकबॉक्स की स्थिति है। वर्तमान में, सब कुछ वांछित के रूप में काम करता है निम्न के बाद: यदि मैं 'सभी का चयन करें' पर क्लिक करता हूं, तो चेकबॉक्स अपडेट में अपडेट होते हैं, लेकिन परिवर्तन दृश्यमान में वापस प्रसारित नहीं होता है।

ध्यान दें, सही ढंग से मेरी कार्यान्वयन काम में सबसे अधिक बातें। उदाहरण के लिए, यदि मैं एक व्यक्तिगत चेकबॉक्स पर क्लिक करता हूं, तो व्यूमोडेल सही तरीके से अपडेट किया जाता है।

अधिक विस्तार से समस्या

:

जब मैं एक व्यक्ति चेकबॉक्स OnPropertyChanged घटना चर जिसका बॉक्स अभी बदली गई थी में निकाल दिया जाता है पर क्लिक करें; कनवर्टर में कनवर्टबैक फ़ंक्शन निकाल दिया गया है; व्यूमोडेल अपडेट किया गया है और सब ठीक है।

हालांकि, जब मैं "सभी का चयन करें" चेकबॉक्स पर क्लिक करता हूं, तो व्यक्तिगत चेकबॉक्स दृश्य में अपडेट होते हैं, लेकिन OnPropertyChanged को किसी भी चर में नहीं कहा जाता है, और कनवर्टर में कनवर्टबैक फ़ंक्शन को नहीं कहा जाता है।

अगर मैं "सभी का चयन करें" अनचेक करता हूं तो भी रिलीज करें, व्यक्तिगत चेक वापस जो कुछ भी पहले थे, उस पर वापस जाएं।

व्यूमोडेल को अपडेट करने का एकमात्र तरीका व्यक्तिगत चेकबॉक्स में क्लिक करना है। हालांकि, दृश्य के प्रयोजनों के लिए बहुआयामी कार्य करता है।

मेरा प्रश्न है:

क्यों viewmodel में स्रोत संग्रह करने के लिए प्रचारित किया चेकबॉक्स में परिवर्तन नहीं कर रहे हैं

कनवर्टर:

public class LogicalOrConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 

     foreach (object arg in values) 
     { 
      if ((arg is bool) && (bool)arg == true) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     object[] values = new object[2] {false, false}; 

     if (value is bool && (bool) value == true) 
      values[0] = true; 

     return values; 
    } 
} 

ObservableVariable परिभाषा:

public class ObservableVariable : INotifyPropertyChanged 
{ 
    private string _name; 
    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      OnPropertyChanged(nameof(Name)); 
     } 
    } 

    private bool _selected; 
    public bool Selected 
    { 
     get { return _selected; } 
     set 
     { 
      _selected = value; 
      OnPropertyChanged(nameof(Selected)); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 
+0

एक प्रश्न - यदि मूल्य "चेक सब" के माध्यम से चुने गए थे और फिर "सभी जांचें" के माध्यम से चयन किया गया था, तो एक प्रश्न - चेकबॉक्स होना चाहिए। आपके एमवीएल कनवर्टर के साथ वे कभी भी अचयनित नहीं होंगे, अगर मूल्य वीएम को प्रचारित किया जाएगा। – Rekshino

+0

@Rekshino, देर से प्रतिक्रिया के लिए खेद है। मैंने काम नहीं किया है कि कैसे अचयनित काम करेगा। इसके लिए तर्क जोड़ना आवश्यक हो सकता है, जो मैं करना चाहता हूं उसके आधार पर। मैंने जानबूझकर समस्या के उस हिस्से को छोड़ दिया क्योंकि मैं अचयनित रसद को एक अलग मुद्दा मानता हूं। –

+0

यदि आप व्यूमोडेल को अपडेट करने के लिए मल्टीबाइंडिंग का उपयोग करना चाहते हैं तो आप इसे वर्कअराउंड के माध्यम से प्राप्त कर सकते हैं, लेकिन मुझे संदेह है कि यह प्रैक्सिस में उपयोगी होगा। यदि आपको इसकी ज़रूरत है तो मैं बाद में अपना उत्तर पोस्ट करूंगा। – Rekshino

उत्तर

5

आपके मल्टीबाइंडिंग के साथ समस्या यह है कि यह दोनों डेटा परिवर्तनों पर "ट्रिगर" करेगा, लेकिन पहला बाध्यकारी (Path="Selected") वह है जो आपके वीएम में डेटा अपडेट करेगा जैसा कि डेटा के लिए बाध्य है। दूसरा बाध्यकारी केवल SelectAll चेकबॉक्स को ट्रिगर करेगा और IsChecked गुणों को बदलें। सिर्फ इसलिए कि आपके पास मल्टीबाइंडिंग है इसका मतलब यह नहीं है कि अन्य बाइंडिंग एक दूसरे को अपने परिवर्तनों का प्रचार करेंगे।

यही कारण है कि आप SelectAll पर क्लिक के व्यवहार को देखते हैं और चेकबॉक्स बदलते हैं लेकिन डेटा नहीं। डेटा को बदलने के लिए ViewModel को बताने के लिए आप SelectAll चेकबॉक्स के लिए स्पष्ट रूप से एक तंत्र स्थापित करते हैं।

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

न्यू XAML:

<CheckBox Content="Select All" x:Name="SelectAllCheckbox" 
      Command="{Binding SelectAllCommand}" 
      CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"/> 


    <ListBox ItemsSource="{Binding Variables}"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <CheckBox Content="{Binding Name}" 
          IsChecked="{Binding Selected}"> 
       </CheckBox> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 

मैं तो आप का चयन नियंत्रण और अचयनित कर सकते हैं एक पैरामीटर के रूप IsChecked शामिल थे।

मेरे ViewModel:

public class ViewModel 
{ 
    public ObservableCollection<ObservableVariable> Variables { get; set; } 
    public ViewModel() 
    { 
     Variables = new ObservableCollection<ObservableVariable>(); 
     SelectAllCommand = new RelayCommand(SelectAll,()=>true); 
    } 

    public RelayCommand SelectAllCommand { get; set; } 

    public void SelectAll(object param) 
    { 
     foreach (var observableVariable in Variables) 
     { 
      observableVariable.Selected = (bool)param; 
     } 
    } 
} 

जाहिर है आप पैरामीटर पर बेहतर मान्यता तर्क चाहते हैं। यह मुख्य रूप से एक संक्षिप्त उत्तर के लिए है।

और पूर्णता बीमार के लिए मैंने मानक रिलेकॉमैंड कोड का उपयोग किया।

public class RelayCommand : ICommand 
{ 
    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 
    private Action<object> methodToExecute; 
    private Func<bool> canExecuteEvaluator; 
    public RelayCommand(Action<object> methodToExecute, Func<bool> canExecuteEvaluator) 
    { 
     this.methodToExecute = methodToExecute; 
     this.canExecuteEvaluator = canExecuteEvaluator; 
    } 
    public RelayCommand(Action<object> methodToExecute) 
     : this(methodToExecute, null) 
    { 
    } 
    public bool CanExecute(object parameter) 
    { 
     if (this.canExecuteEvaluator == null) 
     { 
      return true; 
     } 
     else 
     { 
      bool result = this.canExecuteEvaluator.Invoke(); 
      return result; 
     } 
    } 
    public void Execute(object parameter) 
    { 
     this.methodToExecute.Invoke(parameter); 
    } 
} 
+0

दिलचस्प। मैं दृश्य मॉडल में 'सभी का चयन करें' तर्क डालने से बचने की उम्मीद कर रहा था। ऐसा लगता है कि एमवीवीएम प्रतिमान का उल्लंघन करता है, लेकिन हां। मुझे अक्सर लगता है कि डब्ल्यूपीएफ में एमवीवीएम को सही तरीके से लागू करने के लिए टूल्स शामिल नहीं हैं। –

+0

@ShaneSims यह वास्तव में एमवीवीएम का उल्लंघन नहीं करता है। आपका दृश्य मॉडल डेटा और दृश्य के बीच किसी भी अतिरिक्त तर्क को संभालने के लिए है। और यही वह है जो आप कर रहे हैं। आप डेटा (चयनित संपत्ति) को और अधिक जटिल तरीके से बदलना चाहते हैं। यह पूरी तरह से UI आधारित नहीं है (जिसे XAML या कोड के पीछे रखा जाएगा) क्योंकि आपके डेटा में वह स्थिति है। –

संबंधित मुद्दे