2010-07-24 10 views
9

मैं कस्टम नियंत्रण संलेखन सीखने के लिए एक अभ्यास के रूप में वास्तविक NumericUpDown/Spinner नियंत्रण लिख रहा हूं। मुझे उचित व्यवहार सहित अधिकांश व्यवहार मिल गया है। हालांकि, मेरे परीक्षणों में से एक ने एक दोष प्रकट किया है।मेरे डेटा बाइंडिंग को मजबूर मूल्य के बजाय वास्तविक मूल्य क्यों दिखता है?

मेरे नियंत्रण में 3 निर्भरता गुण हैं: Value, MaximumValue, और MinimumValue। मैं यह सुनिश्चित करने के लिए जबरदस्ती का उपयोग करता हूं कि Value न्यूनतम और अधिकतम, समावेशी के बीच बनी हुई है। उदा।

// In NumericUpDown.cs 

public static readonly DependencyProperty ValueProperty = 
    DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), 
    new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, HandleValueChanged, HandleCoerceValue)); 

[Localizability(LocalizationCategory.Text)] 
public int Value 
{ 
    get { return (int)this.GetValue(ValueProperty); } 
    set { this.SetCurrentValue(ValueProperty, value); } 
} 

private static object HandleCoerceValue(DependencyObject d, object baseValue) 
{ 
    NumericUpDown o = (NumericUpDown)d; 
    var v = (int)baseValue; 

    if (v < o.MinimumValue) v = o.MinimumValue; 
    if (v > o.MaximumValue) v = o.MaximumValue; 

    return v; 
} 

मेरा परीक्षण यह सुनिश्चित करने के लिए है कि डेटा बाध्यकारी काम करता है कि मैं कैसे उम्मीद करता हूं। मैं खिड़कियों आवेदन WPF एक डिफ़ॉल्ट बनाया है और निम्नलिखित XAML में फेंक दिया: बहुत ही सरल codebehind साथ

<Window x:Class="WpfApplication.MainWindow" x:Name="This" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:nud="clr-namespace:WpfCustomControlLibrary;assembly=WpfCustomControlLibrary" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 
     <nud:NumericUpDown Value="{Binding ElementName=This, Path=NumberValue}"/> 
     <TextBox Grid.Row="1" Text="{Binding ElementName=This, Path=NumberValue, Mode=OneWay}" /> 
    </Grid> 
</Window> 

:

public partial class MainWindow : Window 
{ 
    public int NumberValue 
    { 
     get { return (int)GetValue(NumberValueProperty); } 
     set { SetCurrentValue(NumberValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for NumberValue. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty NumberValueProperty = 
     DependencyProperty.Register("NumberValue", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));  

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 
} 

(मैं नियंत्रण की प्रस्तुति के लिए XAML को छोड़ते हुए हूँ)

अब अगर मैं इसे चलाता हूं तो मुझे NumericUpDown से मूल्य को टेक्स्टबॉक्स में उचित रूप से प्रतिबिंबित किया गया है, लेकिन यदि मैं श्रेणी मान से बाहर एक मान टाइप करता हूं तो श्रेणी टेक्स्टबॉक्स में प्रदर्शित होता है जबकि NumericUpDown सही मान दिखाता है।

क्या यह अनुमानित मूल्यों को कार्य करना चाहिए? यह अच्छा है कि यह यूई में घुमाया गया है, लेकिन मुझे उम्मीद है कि coerced मूल्य डेटाबेस के माध्यम से भी चलाने के लिए।

+0

लगता है बाध्यकारी कंसियरिंग वैल का समर्थन नहीं करता है। क्या आपने टेक्स्टबॉक्स में diff मोड की कोशिश की है? –

उत्तर

9

वाह, यह आश्चर्यजनक है। जब आप एक निर्भरता संपत्ति पर मूल्य निर्धारित करते हैं, तो बाध्यकारी अभिव्यक्तियों को मूल्य मजबूर करने से पहले अपडेट किया जाता है!

यदि आप प्रतिबिंबक में DependencyObject.SetValueCommon देखते हैं, तो आप अभिव्यक्ति के लिए कॉल देख सकते हैं। विधि के माध्यम से आधा रास्ते। UpdateEffectiveValue पर कॉल जो आपके CoerceValueCallback का आह्वान करेगा, बाध्यकारी पहले ही अपडेट हो जाने के बाद बहुत अंत में है।

आप इसे फ्रेमवर्क कक्षाओं पर भी देख सकते हैं।

<StackPanel> 
    <Slider Name="Slider" Minimum="10" Maximum="20" Value="{Binding Value, 
     RelativeSource={RelativeSource AncestorType=Window}}"/> 
    <Button Click="SetInvalid_Click">Set Invalid</Button> 
</StackPanel> 

और निम्नलिखित कोड:

private void SetInvalid_Click(object sender, RoutedEventArgs e) 
{ 
    var before = this.Value; 
    var sliderBefore = Slider.Value; 
    Slider.Value = -1; 
    var after = this.Value; 
    var sliderAfter = Slider.Value; 
    MessageBox.Show(string.Format("Value changed from {0} to {1}; " + 
     "Slider changed from {2} to {3}", 
     before, after, sliderBefore, sliderAfter)); 
} 

public int Value { get; set; } 

आप स्लाइडर खींचें और फिर बटन पर क्लिक करें, तो आप "मान की तरह एक संदेश मिलेगा एक नया WPF आवेदन से, निम्नलिखित XAML जोड़ने 11 से -1 तक बदल गया; स्लाइडर 11 से 10 तक बदल गया "।

+1

दिलचस्प है, इसलिए यह व्यवहार की उम्मीद है। मुझे लगता है कि मेरा अगला कदम यह पता लगाने के लिए है कि एक बाइंडेबल 'वास्तविक वैल्यू' संपत्ति की आपूर्ति कैसे करें जो दर्शाता है कि वास्तव में न्यूमेरिकअपडाउन में क्या प्रदर्शित होता है। क्या आप फिर से मूल्यांकन के बजाय एक बेहतर तरीके से अवगत हैं, एक संपत्ति में मजबूती से हैंडलर बदल गया है और वहां 'वास्तविक वैल्यू' स्थापित किया गया है? –

+0

मुझे लगता है कि आप उत्तर के लिए एक से अधिक अपवर्तनीय हैं। –

-1

आप v को जोड़ रहे हैं जो एक int और इस प्रकार का मान प्रकार है। इसलिए यह ढेर पर संग्रहीत है। यह किसी भी तरह से baseValue से जुड़ा हुआ नहीं है। तो बदलना v आधार वैल्यू नहीं बदलेगा।

वही तर्क बेसवेल पर लागू होता है। यह मूल्य (संदर्भ द्वारा नहीं) द्वारा पारित किया जाता है, इसलिए यह बदलकर वास्तविक पैरामीटर नहीं बदलेगा।

v वापस कर दिया गया है और यूआई को अपडेट करने के लिए स्पष्ट रूप से उपयोग किया जाता है।

आप गुण डेटा प्रकार को किसी संदर्भ प्रकार में बदलने की जांच कर सकते हैं। फिर यह संदर्भ द्वारा पारित किया जाएगा और किए गए किसी भी बदलाव स्रोत पर वापस प्रतिबिंबित होगा। डाटाबेसिंग प्रक्रिया मानना ​​एक प्रतिलिपि नहीं बनाता है।

+0

अगर यह जबरदस्ती विधि से ऑब्जेक्ट के रूप में लौटाया जाता है तो क्या यह बॉक्सिंग नहीं होता है? –

1

एक पुराने प्रश्न के लिए एक नया उत्तर: :-)

ValueProperty एक FrameworkPropertyMetadata उदाहरण के पंजीकरण में प्रयोग किया जाता है। UpdateSourceTrigger इस उदाहरण की संपत्ति Explicit पर सेट करें। यह एक कन्स्ट्रक्टर अधिभार में किया जा सकता है।

public static readonly DependencyProperty ValueProperty = 
    DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), 
    new FrameworkPropertyMetadata(
     0, 
     FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, 
     HandleValueChanged, 
     HandleCoerceValue, 
     false 
     UpdateSourceTrigger.Explicit)); 

अब ValueProperty के बंधन स्रोत PropertyChanged पर स्वचालित रूप से अपडेट नहीं किया जाएगा। अपने HandleValueChanged विधि में मैन्युअल रूप से अपडेट करें (उपरोक्त कोड देखें)। इस पद्धति को केवल संपत्ति के 'असली' परिवर्तनों पर ही कहा जाता है जब वाणिज्य विधि को बुलाया जाता है।

आप इसे इस तरह से कर सकते हैं:

static void HandleValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
{ 
    NumericUpDown nud = obj as NumericUpDown; 
    if (nud == null) 
     return; 

    BindingExpression be = nud.GetBindingExpression(NumericUpDown.ValueProperty); 
    if(be != null) 
     be.UpdateSource(); 
} 

इस तरह से आप अपने DependencyProperty के गैर मजबूर मूल्यों के साथ अपने बाइंडिंग अद्यतन करने के लिए से बचने कर सकते हैं।

+0

अच्छा कामकाज। :) –

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