2008-11-28 9 views
13

जो एक अच्छा WPF बाइंडिंग चुनौती की तरह उन लोगों के लिए:आप कैसे एक तरफ झुकाव के एक अलग बिट के लिए एक चेकबॉक्स बांध सकते हैं?

मैं दो तरह से एक झंडे गणना (धन्यवाद इयान ओक्स, original MSDN post) के एक व्यक्ति बिट के लिए एक चेकबॉक्स बंधन के एक लगभग कार्यात्मक उदाहरण है। समस्या यह है कि बाध्यकारी व्यवहार करता है जैसे कि यह एक तरीका है (डेटा कॉन्टेक्स्ट के लिए यूआई, इसके विपरीत नहीं)। तो प्रभावी रूप से चेक बॉक्स प्रारंभ नहीं होता है, लेकिन अगर यह डेटा स्रोत को टॉगल किया गया है तो सही ढंग से अपडेट किया गया है। अटैचमेंट बिट-आधारित बाइंडिंग को सक्षम करने के लिए कुछ संलग्न निर्भरता गुणों को परिभाषित करने वाला वर्ग है। मैंने जो देखा है वह है कि ValueChanged कभी नहीं कहा जाता है, भले ही मैं DataContext को बदलने के लिए मजबूर करता हूं।

मैं क्या कोशिश की है: संपत्ति परिभाषा के आदेश को बदलने से स्पष्ट रूप से बाध्यकारी मोड = TwoWay की स्थापना की पुष्टि करने के DataContext अद्यतन, किसी भी प्रशंसनीय FrameworkMetadataPropertyOptions (AffectsRender, BindsTwoWayByDefault), बाहर बुदबुदाती है एक लेबल और पाठ बॉक्स का उपयोग करना,, बीटिंग दीवार पर सिर, संघर्ष के मामले में EnumValueProperty करने के लिए ValueProperty बदलना।

किसी भी सुझाव या विचारों की अत्यधिक सराहना की जाएगी, जो भी आप पेशकश कर सकते हैं उसके लिए धन्यवाद!

गणन:


    [Flags] 
    public enum Department : byte 
    { 
     None = 0x00, 
     A = 0x01, 
     B = 0x02, 
     C = 0x04, 
     D = 0x08 
    } // end enum Department 

XAML उपयोग:


    CheckBox Name="studentIsInDeptACheckBox" 
      ctrl:CheckBoxFlagsBehaviour.Mask="{x:Static c:Department.A}" 
      ctrl:CheckBoxFlagsBehaviour.IsChecked="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}" 
      ctrl:CheckBoxFlagsBehaviour.Value="{Binding Department}" 

वर्ग:


    /// 
    /// A helper class for providing bit-wise binding. 
    /// 
    public class CheckBoxFlagsBehaviour 
    { 
     private static bool isValueChanging; 

     public static Enum GetMask(DependencyObject obj) 
     { 
      return (Enum)obj.GetValue(MaskProperty); 
     } // end GetMask 

     public static void SetMask(DependencyObject obj, Enum value) 
     { 
      obj.SetValue(MaskProperty, value); 
     } // end SetMask 

     public static readonly DependencyProperty MaskProperty = 
      DependencyProperty.RegisterAttached("Mask", typeof(Enum), 
      typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(null)); 

     public static Enum GetValue(DependencyObject obj) 
     { 
      return (Enum)obj.GetValue(ValueProperty); 
     } // end GetValue 

     public static void SetValue(DependencyObject obj, Enum value) 
     { 
      obj.SetValue(ValueProperty, value); 
     } // end SetValue 

     public static readonly DependencyProperty ValueProperty = 
      DependencyProperty.RegisterAttached("Value", typeof(Enum), 
      typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(null, ValueChanged)); 

     private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      isValueChanging = true; 
      byte mask = Convert.ToByte(GetMask(d)); 
      byte value = Convert.ToByte(e.NewValue); 

      BindingExpression exp = BindingOperations.GetBindingExpression(d, IsCheckedProperty); 
      object dataItem = GetUnderlyingDataItem(exp.DataItem); 
      PropertyInfo pi = dataItem.GetType().GetProperty(exp.ParentBinding.Path.Path); 
      pi.SetValue(dataItem, (value & mask) != 0, null); 

      ((CheckBox)d).IsChecked = (value & mask) != 0; 
      isValueChanging = false; 
     } // end ValueChanged 

     public static bool? GetIsChecked(DependencyObject obj) 
     { 
      return (bool?)obj.GetValue(IsCheckedProperty); 
     } // end GetIsChecked 

     public static void SetIsChecked(DependencyObject obj, bool? value) 
     { 
      obj.SetValue(IsCheckedProperty, value); 
     } // end SetIsChecked 

     public static readonly DependencyProperty IsCheckedProperty = 
      DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), 
      typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(false, IsCheckedChanged)); 

     private static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      if (isValueChanging) return; 

      bool? isChecked = (bool?)e.NewValue; 
      if (isChecked != null) 
      { 
       BindingExpression exp = BindingOperations.GetBindingExpression(d, ValueProperty); 
       object dataItem = GetUnderlyingDataItem(exp.DataItem); 
       PropertyInfo pi = dataItem.GetType().GetProperty(exp.ParentBinding.Path.Path); 

       byte mask = Convert.ToByte(GetMask(d)); 
       byte value = Convert.ToByte(pi.GetValue(dataItem, null)); 

       if (isChecked.Value) 
       { 
        if ((value & mask) == 0) 
        { 
         value = (byte)(value + mask); 
        } 
       } 
       else 
       { 
        if ((value & mask) != 0) 
        { 
         value = (byte)(value - mask); 
        } 
       } 

       pi.SetValue(dataItem, value, null); 
      } 
     } // end IsCheckedChanged 

     /// 
     /// Gets the underlying data item from an object. 
     /// 
     /// The object to examine. 
     /// The underlying data item if appropriate, or the object passed in. 
     private static object GetUnderlyingDataItem(object o) 
     { 
      return o is DataRowView ? ((DataRowView)o).Row : o; 
     } // end GetUnderlyingDataItem 
    } // end class CheckBoxFlagsBehaviour 

उत्तर

37

आप एक मूल्य कनवर्टर का उपयोग कर सकते हैं। यहाँ लक्ष्य Enum के लिए एक बहुत विशिष्ट कार्यान्वयन है, लेकिन मुश्किल नहीं होगा कैसे कनवर्टर अधिक सामान्य बनाने के लिए देखने के लिए:

[Flags] 
public enum Department 
{ 
    None = 0, 
    A = 1, 
    B = 2, 
    C = 4, 
    D = 8 
} 

public partial class Window1 : Window 
{ 
    public Window1() 
    { 
     InitializeComponent(); 

     this.DepartmentsPanel.DataContext = new DataObject 
     { 
      Department = Department.A | Department.C 
     }; 
    } 
} 

public class DataObject 
{ 
    public DataObject() 
    { 
    } 

    public Department Department { get; set; } 
} 

public class DepartmentValueConverter : IValueConverter 
{ 
    private Department target; 

    public DepartmentValueConverter() 
    { 
    } 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     Department mask = (Department)parameter; 
     this.target = (Department)value; 
     return ((mask & this.target) != 0); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     this.target ^= (Department)parameter; 
     return this.target; 
    } 
} 

और फिर XAML में कनवर्टर का उपयोग:

<Window.Resources> 
    <l:DepartmentValueConverter x:Key="DeptConverter" /> 
</Window.Resources> 

<StackPanel x:Name="DepartmentsPanel"> 
    <CheckBox Content="A" 
       IsChecked="{Binding 
          Path=Department, 
          Converter={StaticResource DeptConverter}, 
          ConverterParameter={x:Static l:Department.A}}"/> 
    <!-- more --> 
</StackPanel> 

संपादित करें : मेरे पास नीचे टिप्पणी करने के लिए पर्याप्त "प्रतिनिधि" (अभी तक!) नहीं है इसलिए मुझे अपनी पोस्ट अपडेट करनी है :(

अंतिम टिप्पणी में demwiz.myopenid.com कहता है "लेकिन जब यह दो- कन्वर्टबैक बाध्यकारी तरीका अलग हो जाता है ", ठीक है मैंने अद्यतन किया है ConvertBack परिदृश्य को संभालने के लिए उपरोक्त मेरा नमूना कोड; मैंने एक नमूना कार्यरत अनुप्रयोग भी पोस्ट किया है here (संपादित करें: ध्यान दें कि नमूना कोड डाउनलोड में कनवर्टर का एक सामान्य संस्करण भी शामिल है)।

व्यक्तिगत रूप से मुझे लगता है कि यह बहुत आसान है, मुझे उम्मीद है कि इससे मदद मिलती है।

+0

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

+0

दरअसल, नमूना थोड़ा सा सरल है; हालांकि, मुझे लगता है कि यह समाधान अभी भी लागू होता है क्योंकि आप आने वाले बूल को देख सकते हैं? मूल्य और फिर^= कनवर्टर पैरामीटर में आपूर्ति किए गए मास्क पर आधारित मान; सही बात? यदि lemme नहीं पता है और छुट्टियों पर मुझे कुछ समय मिलता है तो मैं कुछ कोड पोस्ट करूंगा। – PaulJ

+0

कनवर्टबैक परिदृश्य को शामिल करने के लिए पोस्ट को अपडेट किया गया, यह भी ध्यान दें कि मैंने एप्लिकेशन की एक कार्यशील प्रतिलिपि के लिए एक लिंक पोस्ट किया है। – PaulJ

1

अपने DataObject जो चेक बॉक्स को बांधता है की जाँच करें शामिल विभाग संपत्ति एक INotifyPropertyChnaged है। PropertyChanged अपने सेटर पर बुलाया?

+0

मैं दृढ़ता से टाइप किए गए डेटारो के लिए बाध्यकारी हूं जो सफलतापूर्वक PropertyChanged ईवेंट प्रकाशित करता है। मैंने इसे अन्य यूआई नियंत्रण (लेबल, टेक्स्टबॉक्स) पर बाध्य करके पुष्टि की जो सही ढंग से अपडेट होगा। हालांकि सुझाव के लिए धन्यवाद। :) –

2

हर किसी की मदद के लिए धन्यवाद, मैंने अंततः इसे समझ लिया।

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

यहां अंतिम संशोधन में चेकबॉक्स फ्लेग्सबीयर क्लास दोहराया गया है। मूल कार्यान्वयन के लिए इयान ओक्स के लिए फिर से धन्यवाद!

public class CheckBoxFlagsBehaviour 
{ 
    private static bool isValueChanging; 

    public static Enum GetMask(DependencyObject obj) 
    { 
     return (Enum)obj.GetValue(MaskProperty); 
    } // end GetMask 

    public static void SetMask(DependencyObject obj, Enum value) 
    { 
     obj.SetValue(MaskProperty, value); 
    } // end SetMask 

    public static readonly DependencyProperty MaskProperty = 
     DependencyProperty.RegisterAttached("Mask", typeof(Enum), 
     typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(null)); 

    public static byte GetValue(DependencyObject obj) 
    { 
     return (byte)obj.GetValue(ValueProperty); 
    } // end GetValue 

    public static void SetValue(DependencyObject obj, byte value) 
    { 
     obj.SetValue(ValueProperty, value); 
    } // end SetValue 

    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.RegisterAttached("Value", typeof(byte), 
     typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(default(byte), ValueChanged)); 

    private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     isValueChanging = true; 
     byte mask = Convert.ToByte(GetMask(d)); 
     byte value = Convert.ToByte(e.NewValue); 

     BindingExpression exp = BindingOperations.GetBindingExpression(d, IsCheckedProperty); 
     object dataItem = GetUnderlyingDataItem(exp.DataItem); 
     PropertyInfo pi = dataItem.GetType().GetProperty(exp.ParentBinding.Path.Path); 
     pi.SetValue(dataItem, (value & mask) != 0, null); 

     ((CheckBox)d).IsChecked = (value & mask) != 0; 
     isValueChanging = false; 
    } // end ValueChanged 

    public static bool? GetIsChecked(DependencyObject obj) 
    { 
     return (bool?)obj.GetValue(IsCheckedProperty); 
    } // end GetIsChecked 

    public static void SetIsChecked(DependencyObject obj, bool? value) 
    { 
     obj.SetValue(IsCheckedProperty, value); 
    } // end SetIsChecked 

    public static readonly DependencyProperty IsCheckedProperty = 
     DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), 
     typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(false, IsCheckedChanged)); 

    private static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     if (isValueChanging) return; 

     bool? isChecked = (bool?)e.NewValue; 
     if (isChecked != null) 
     { 
      BindingExpression exp = BindingOperations.GetBindingExpression(d, ValueProperty); 
      object dataItem = GetUnderlyingDataItem(exp.DataItem); 
      PropertyInfo pi = dataItem.GetType().GetProperty(exp.ParentBinding.Path.Path); 

      byte mask = Convert.ToByte(GetMask(d)); 
      byte value = Convert.ToByte(pi.GetValue(dataItem, null)); 

      if (isChecked.Value) 
      { 
       if ((value & mask) == 0) 
       { 
        value = (byte)(value + mask); 
       } 
      } 
      else 
      { 
       if ((value & mask) != 0) 
       { 
        value = (byte)(value - mask); 
       } 
      } 

      pi.SetValue(dataItem, value, null); 
     } 
    } // end IsCheckedChanged 

    private static object GetUnderlyingDataItem(object o) 
    { 
     return o is DataRowView ? ((DataRowView)o).Row : o; 
    } // end GetUnderlyingDataItem 
} // end class CheckBoxFlagsBehaviour 
+0

यह बहुत जटिल लगता है - क्यों एक साधारण मूल्य कनवर्टर नहीं होगा नौकरी करें? –

+1

एक वैल्यू कनवर्टर एक तरफा बाध्यकारी के लिए बहुत अच्छा है, लेकिन जब कन्वर्टबैक बाध्यकारी दो-तरफा बाध्य होता है तो आप अलग-अलग मूल्यों को वापस करने के लिए अन्य बिट्स सेट नहीं कर सकते हैं। –

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