2012-04-26 14 views
18

के बजाय बाध्यकारी एक्सप्रेशन के साथ बुलाया जाता है, मैं अपने डब्ल्यूपीएफ एप्लिकेशन में सत्यापन प्रमाणीकरण का उपयोग करने के साथ शुरुआत कर रहा हूं, लेकिन काफी उलझन में हूं। इस प्रकारValidationStep = "UpdateValue" के साथ प्रमाणीकरण नियम को अद्यतन मूल्य

class RequiredRule : ValidationRule 
{ 
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
    { 
     if (String.IsNullOrWhiteSpace(value as string)) 
     { 
      return new ValidationResult(false, "Must not be empty"); 
     } 
     else 
     { 
      return new ValidationResult(true, null); 
     } 

    } 
} 

XAML में प्रयुक्त:

<TextBox> 
    <TextBox.Text> 
     <Binding Path="Identity.Name"> 
      <Binding.ValidationRules> 
       <validation:RequiredRule/> 
      </Binding.ValidationRules> 
     </Binding> 
    </TextBox.Text> 
</TextBox> 

यह ज्यादातर काम करता है के रूप में मैं उम्मीद करेंगे

मैं निम्नलिखित सरल नियम है। मुझे आश्चर्य हुआ कि मेरी स्रोत संपत्ति (Identity.Name) सेट नहीं की जा रही थी; मेरे पास एक पूर्ववत कार्य है जो परिवर्तन को कभी नहीं देखता है, और इसे फिर से टाइप करने के अलावा मूल्य को वापस करने का कोई तरीका नहीं है (अच्छा नहीं)।

माइक्रोसॉफ्ट के Data Binding Overview नीचे के सत्यापन की प्रक्रिया का वर्णन करता है, जो इस व्यवहार को बहुत अच्छी तरह से बताता है। इस पर आधारित, मैं अपना ValidationStepUpdatedValue पर सेट करना चाहता हूं।

<validation:RequiredRule ValidationStep="UpdatedValue"/> 

यह वह जगह है जहां चीजें मेरे लिए अजीब होती हैं। प्रमाणीकरण() को ऑब्जेक्ट वैल्यू के साथ बुलाया जाने वाला गुण मान सेट किया गया था (यानी, एक स्ट्रिंग), मुझे System.Windows.Data.BindingExpression मिलता है! मुझे माइक्रोसॉफ्ट के दस्तावेज में कुछ भी नहीं दिख रहा है जो इस व्यवहार का वर्णन करता है।

डीबगर में, मैं स्रोत ऑब्जेक्ट (TextBox का) देख सकता हूं, संपत्ति के पथ पर नेविगेट कर सकता हूं, और देख सकता हूं कि मान सेट किया गया है। हालांकि, मुझे सत्यापन नियम के भीतर सही संपत्ति पर जाने का कोई अच्छा तरीका नहीं दिख रहा है।

नोट: ValidationStepConvertedProposedValue के रूप में के साथ, मैं में प्रवेश किया स्ट्रिंग (मैं उपयोग में एक कनवर्टर की जरूरत नहीं है) मिलता है, लेकिन यह भी मान्यता है, विफल रहता ब्लॉक स्रोत संपत्ति अद्यतन अपेक्षा के अनुरूप। CommittedValue के साथ, मुझे स्ट्रिंग के बजाय BindingExpression मिलता है।

यहाँ में कई प्रश्न हैं:

  1. मैं() के लिए मान्य पारित किया जा रहा एक असंगत तर्क प्रकार ValidationStep सेटिंग के आधार पर क्यों मिलता है?

  2. मैं बाइंडिंगएक्सप्रेस से वास्तविक मूल्य कैसे प्राप्त कर सकता हूं?

  3. वैकल्पिक रूप से, क्या उपयोगकर्ता को टेक्स्टबॉक्स को पिछले (वैध) स्थिति में वापस करने की अनुमति देने का एक अच्छा तरीका है? अपने 2 सवाल का जवाब देने के लिए (मैं उल्लेख किया है, मेरे अपने पूर्ववत समारोह कभी नहीं परिवर्तन देखता है।)

+0

मुझे आश्चर्य है कि कोई प्रतिक्रिया नहीं दिख रही है, खासकर जब से इसे हटा दिया गया है। मेरे लिए, यह ValidationRule दृष्टिकोण का उपयोग करने में एक शोस्टॉपर प्रतीत होता है, जो अन्यथा उत्कृष्ट और सहज दिखता है। क्या IDataErrorInfo का उपयोग करना बेहतर है, भले ही यह तुलना में गुंजाइश लगता है? – mbmcavoy

उत्तर

13

मैंने मामूली सीमा के साथ BindingExpression से मूल्य निकालने की समस्या हल कर दी है।

सबसे पहले, कुछ और अधिक पूर्ण XAML:

<Window x:Class="ValidationRuleTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:ValidationRuleTest" 
     Title="MainWindow" Height="100" Width="525"> 
    <Window.DataContext> 
     <local:MainWindowViewModel/> 
    </Window.DataContext> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="50"/> 
      <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <TextBlock Text="String 1"/> 
     <TextBox Grid.Column="1"> 
      <TextBox.Text> 
       <Binding Path="String1" UpdateSourceTrigger="PropertyChanged"> 
        <Binding.ValidationRules> 
         <local:RequiredRule ValidationStep="RawProposedValue"/> 
        </Binding.ValidationRules> 
       </Binding> 
      </TextBox.Text> 
     </TextBox> 
     <TextBlock Text="String 2" Grid.Row="1"/> 
     <TextBox Grid.Column="1" Grid.Row="1"> 
      <TextBox.Text> 
       <Binding Path="String2" UpdateSourceTrigger="PropertyChanged"> 
        <Binding.ValidationRules> 
         <local:RequiredRule ValidationStep="UpdatedValue"/> 
        </Binding.ValidationRules> 
       </Binding> 
      </TextBox.Text> 
     </TextBox> 
    </Grid> 
</Window> 

ध्यान दें कि पहले पाठ बॉक्स ValidationStep="RawProposedValue" (डिफ़ॉल्ट) का उपयोग करता है, दूसरा एक ValidationStep="UpdatedValue" का उपयोग करता है, लेकिन दोनों एक ही सत्यापन नियम का उपयोग करते हुए।

एक साधारण ViewModel (उपेक्षा INPC और अन्य उपयोगी सामग्री):

class MainWindowViewModel 
{ 
    public string String1 
    { get; set; } 

    public string String2 
    { get; set; } 
} 

और अंत में, नई RequiredRule:

class RequiredRule : ValidationRule 
{ 
    public override ValidationResult Validate(object value, 
     System.Globalization.CultureInfo cultureInfo) 
    { 
     // Get and convert the value 
     string stringValue = GetBoundValue(value) as string; 

     // Specific ValidationRule implementation... 
     if (String.IsNullOrWhiteSpace(stringValue)) 
     { 
      return new ValidationResult(false, "Must not be empty"); 
     } 
     else 
     { 
      return new ValidationResult(true, null); 
     } 
    } 

    private object GetBoundValue(object value) 
    { 
     if (value is BindingExpression) 
     { 
      // ValidationStep was UpdatedValue or CommittedValue (Validate after setting) 
      // Need to pull the value out of the BindingExpression. 
      BindingExpression binding = (BindingExpression)value; 

      // Get the bound object and name of the property 
      object dataItem = binding.DataItem; 
      string propertyName = binding.ParentBinding.Path.Path; 

      // Extract the value of the property. 
      object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null); 

      // This is what we want. 
      return propertyValue; 
     } 
     else 
     { 
      // ValidationStep was RawProposedValue or ConvertedProposedValue 
      // The argument is already what we want! 
      return value; 
     } 
    } 
} 

GetBoundValue() विधि मूल्य मैं अगर यह के बारे में परवाह की खुदाई होगा एक बाइंडिंग एक्स्प्रेशन प्राप्त करता है, या अगर यह नहीं है तो बस तर्क को वापस लातें। वास्तविक कुंजी "पथ" ढूंढ रही थी, और उसके बाद संपत्ति और उसके मूल्य को प्राप्त करने के लिए इसका उपयोग कर रहा था।

सीमा: मेरे मूल प्रश्न में, मेरे बाध्यकारी में Path="Identity.Name" था, क्योंकि मैं अपने व्यूमोडेल की उप-वस्तुओं में खोद रहा था। यह काम नहीं करेगा, क्योंकि उपर्युक्त कोड बाध्य वस्तु पर सीधे संपत्ति के लिए पथ की अपेक्षा करता है। सौभाग्य से, मैंने पहले से ही मेरे व्यूमोडेल को चपटा कर दिया है, इसलिए यह अब मामला नहीं है, लेकिन नियंत्रण के डेटाकॉन्टेक्स्ट को उप-ऑब्जेक्ट के रूप में सेट करने के लिए एक कामकाज हो सकता है।

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

+1

ऐसी स्थितियां हैं जहां IDataErrorInfo पर्याप्त नहीं है, जैसे कि आप उदाहरण के लिए डेटाग्रिड में अद्वितीय फ़ील्ड को सत्यापित करना चाहते हैं, और आपको पंक्तियों की तुलना करना होगा। मुझे खुशी है कि आप जो खोज रहे थे उसे ढूंढें। चीयर्स! –

+0

सीमा को हटाने के लिए मेरा जवाब देखें। –

1

आदेश में:

स्ट्रिंग strVal = (स्ट्रिंग) ((BindingExpression) मूल्य)। DataItem

+0

यह * लगभग * काम करता है, लेकिन नहीं करता है।DataItem में संपत्ति का मूल्य नहीं है, बल्कि डेटाकॉन्टेक्स्ट (हाँ, संपत्ति वहां है)। दुर्भाग्य से, मुझे नहीं पता कि कौन सी संपत्ति * यह * सत्यापन नियम उदाहरण जांचना है। मुझे बाइंडिंगएक्सप्रेस में कुछ भी दिखाई नहीं देता है जो मुझे यह निर्धारित करने में सक्षम बनाता है कि कौन सी संपत्ति बाध्य है (यानी, पथ)। – mbmcavoy

+0

मैंने सोचा था कि आपका "आवश्यक नियम" उद्देश्य सामान्य प्रकार का था, इससे कोई फ़र्क नहीं पड़ता कि यह कितनी संपत्ति है जब तक यह स्ट्रिंग के रूप में मान्य हो ... –

+0

हां, आवश्यक नियम श्रेणी किसी स्ट्रिंग प्रॉपर्टी को सत्यापित करने में सक्षम होना चाहिए। उपरोक्त एक्सएएमएल के मामले में, टेक्स्टबॉक्स पथ = "पहचान नाम" से बंधे हैं। मेरे पास एक आवश्यक टेक्स्टबॉक्स है जो पथ = "पहचान। डिस्क्रिप्शन" के साथ बाध्य है, एक आवश्यक नियम लागू होता है। चूंकि पास किए गए बाइंडिंगएक्सप्रेस का डेटाइटम डेटा संदर्भ है, इसलिए मैं * पहचान * नाम और पहचान दोनों के साथ देख सकता हूं। (साथ ही साथ मेरे व्यूमोडेल में सब कुछ)। जबकि मैं डीबगर में जिस मूल्य की देखभाल करता हूं, उसे देख सकता हूं, मुझे यह पता लगाने के लिए वैधता नियम का कोई तरीका नहीं दिख रहा है कि उसे किस संपत्ति को मान्य करना चाहिए। – mbmcavoy

3

यह mbmcavoy के answer का विस्तार है।

मैंने बाध्यकारी पथों के लिए सीमा को हटाने के लिए GetBoundValue विधि को संशोधित किया है। BindingExpression में गुणों को हल किया गया है ResolvedSource और ResolvedSourcePropertyName, जो डीबगर में दिखाई दे रहे हैं लेकिन सामान्य कोड के माध्यम से सुलभ नहीं हैं। उन्हें प्रतिबिंब के माध्यम से प्राप्त करने के लिए कोई समस्या नहीं है और इस समाधान को किसी भी बाध्यकारी पथ के साथ काम करना चाहिए।

private object GetBoundValue(object value) 
{ 
    if (value is BindingExpression) 
    { 
     // ValidationStep was UpdatedValue or CommittedValue (validate after setting) 
     // Need to pull the value out of the BindingExpression. 
     BindingExpression binding = (BindingExpression)value; 

     // Get the bound object and name of the property 
     string resolvedPropertyName = binding.GetType().GetProperty("ResolvedSourcePropertyName", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null).ToString(); 
     object resolvedSource = binding.GetType().GetProperty("ResolvedSource", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null); 

     // Extract the value of the property 
     object propertyValue = resolvedSource.GetType().GetProperty(resolvedPropertyName).GetValue(resolvedSource, null); 

     return propertyValue; 
    } 
    else 
    { 
     return value; 
    } 
} 
+1

किसी भी व्यक्ति जो पहले .NET 4.5 को लक्षित कर रहा है, ResolvedSource और ResolvedSourcePropertyName .NET 4.5 में जोड़ा गया था, इसलिए यह काम नहीं करेगा। – user2339814

+0

बहुत आसान। मुझे एक ऐसा मामला मिला जहां वैध नियंत्रण हटा दिया गया था (यह ग्रिड कंट्रोल सेल में था), प्रमाणीकरण को पिछली बार कहा गया था, जिसमें ResolvedSourcePropertyName के लिए शून्य मान था। तो मुझे उस शून्य पर ToString को कॉल करने से बचने के लिए, GetValue() से वापसी मान पर एक शून्य जांच जोड़नी पड़ी। –

3

यह mbmcavoy और adabyron के उत्तर का एक वैकल्पिक विस्तार है।

रास्तों बंधन के लिए सीमा को निकालने के लिए, मैं इस तरह के विधि का उपयोग कर संपत्ति के मूल्य मिलता है:

public static object GetPropertyValue(object obj, string propertyName) 
{ 
    foreach (String part in propertyName.Split('.')) 
    { 
     if (obj == null) { return null; } 

     Type type = obj.GetType(); 
     PropertyInfo info = type.GetProperty(part); 
     if (info == null) { return null; } 

     obj = info.GetValue(obj, null); 
    } 

    return obj; 
} 

अब बस बदलने

object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null); 

को
object propertyValue = GetPropertyValue(dataItem, propertyName); 

संबंधित पोस्ट: Get property value from string using reflection in C#

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