2010-06-23 7 views
9

मेरे पास एक ऐसा फॉर्म है जो कई डेटा टेम्पलेट तत्वों के आधार पर उत्पन्न होता है। DataTemplate तत्वों में से एक एक वर्ग है कि इस तरह दिखता है से बाहर किसी पाठ बॉक्स बनाता है:डब्ल्यूपीएफ बाध्यकारी और गतिशील रूप से स्ट्रिंगफॉर्मेट संपत्ति

public class MyTextBoxClass 
{ 
    public object Value { get;set;} 
    //other properties left out for brevity's sake 
    public string FormatString { get;set;} 
} 

मैं एक तरह से करने के लिए "बाँध" बंधन की "StringFormat" संपत्ति के लिए FormatString संपत्ति में मूल्य की जरूरत है। अब तक मेरे पास है:

<DataTemplate DataType="{x:Type vm:MyTextBoxClass}"> 
<TextBox Text="{Binding Path=Value, StringFormat={Binding Path=FormatString}" /> 
</DataTemplate> 

हालांकि, बाद से StringFormat निर्भरता संपत्ति नहीं है, मैं यह करने के लिए बाध्य नहीं कर सकते।

मेरा अगला विचार एक मूल्य कनवर्टर बनाना था और कनवर्टर पैरामीटर पर स्वरूपस्ट्रिंग संपत्ति के मूल्य को पास करना था, लेकिन मैं एक ही समस्या में भाग गया - कनवर्टर पैरामीटर निर्भरता प्रॉपर्टी नहीं है।

तो, अब मैं आपके पास आ गया हूं, SO। मैं बाध्यकारी के स्ट्रिंगफॉर्मेट को गतिशील रूप से कैसे सेट करूं? अधिक विशेष रूप से, टेक्स्टबॉक्स पर?

मैं XAML को मेरे लिए काम करने देना पसंद करूंगा ताकि मैं कोड-बैक के साथ खेलने से बच सकूं। मैं एमवीवीएम पैटर्न का उपयोग कर रहा हूं और दृश्य-मॉडल के बीच की सीमाओं को रखना चाहता हूं और जितना संभव हो उतना धुंधला दिखाना चाहता हूं।

धन्यवाद!

उत्तर

2

एक ऐसा तरीका बन सकता है जो TextBox विरासत में प्राप्त हो और उस वर्ग में आपकी अपनी निर्भरता संपत्ति बनाएं जो सेट होने पर StringFormat पर प्रतिनिधि हो। तो अपने XAML में TextBox का उपयोग करने के बजाय आप विरासत वाले टेक्स्टबॉक्स का उपयोग करेंगे और बाध्यकारी में अपनी निर्भरता संपत्ति सेट करेंगे।

+1

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

+0

मैं वही काम करने की कोशिश कर रहा हूं, लेकिन मुझे यकीन नहीं है कि इसे संभालने के लिए संलग्न गुणों को कैसे सेट अप करें। मैंने एक नया प्रश्न पोस्ट किया: http://stackoverflow.com/q/24119097/65461 –

1

बस MyTextBoxClass.Value के बजाय MyTextBoxClass के उदाहरण पर टेक्स्टबॉक्स को बाध्य करें और मान और प्रारूपस्ट्रिंग से स्ट्रिंग बनाने के लिए एक मान कनवर्टर का उपयोग करें।

एक अन्य समाधान एक multivalue कनवर्टर जो दोनों मूल्य और FormatString करने के लिए बाध्य होगा उपयोग करने के लिए है।

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

+0

MyTextBoxClass उदाहरण के लिए बाध्यकारी कुछ मैंने कोशिश की है, लेकिन ValueConverter में कनवर्टबैक विधि एक समस्या होगी क्योंकि कई, कई गुण हैं कि मेरे पास टेक्स्टबॉक्स ऑब्जेक्ट पर कोई स्थान नहीं है। तो, मुझे टेक्स्टबॉक्स से वापस आने वाली अपूर्ण वस्तु मिल जाएगी। मैं बहु मान कनवर्टर में देखता हूं। हालांकि, FormatString बाध्यकारी नहीं है क्योंकि यह एक निर्भरता संपत्ति है, इसलिए मुझे यकीन नहीं है कि यह काम करने जा रहा है। –

+0

यह कैसे काम करना चाहिए? जब टेक्स्टबॉक्स को डाटाबेसिंग का उपयोग करके अद्यतन किया जाता है तो टेक्स्ट स्वरूपण का उपयोग करके स्वरूपित किया जाता है। जब कोई उपयोगकर्ता टेक्स्टबॉक्स अपडेट करता है तो वह कोई भी टेक्स्ट दर्ज कर सकता है जो FormatString के स्वरूपण के साथ असंगत हो सकता है। क्या वह ठीक है? क्या आप वाकई मास्क किए गए टेक्स्टबॉक्स का उपयोग नहीं करना चाहते हैं? साथ ही, FormatString किसी अन्य सार्वजनिक संपत्ति के रूप में बाध्यकारी है। –

+0

"स्वरूपस्ट्रिंग किसी भी अन्य सार्वजनिक संपत्ति के रूप में बाध्यकारी है" समझाएं कि फिर आपको एक त्रुटि क्यों मिलेगी जो कहती है कि "ए 'बाध्यकारी' प्रकार 'बाध्यकारी' प्रकार की 'स्ट्रिंगफॉर्मैट' प्रॉपर्टी पर सेट नहीं की जा सकती है। 'बाध्यकारी' केवल तभी हो सकता है एक निर्भरता ऑब्जेक्ट की निर्भरता प्रजनन पर सेट करें। " – jpierson

1

कोई एक संलग्न व्यवहार बना सकता है जो बाध्यकारी को उस प्रारूप के साथ प्रतिस्थापित कर सकता है जिसमें स्वरूपस्ट्रिंग निर्दिष्ट है। यदि FormatString निर्भरता गुण तब बाध्यकारी एक बार फिर अपडेट किया जाएगा। यदि बाध्यकारी अद्यतन किया गया है तो स्वरूपण को उस बाध्यकारी पर लागू किया जाएगा।

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

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

शेष समाधान है कि द्विदिश स्वरूपण और unformatting के लिए काम किया जाएगा निम्नलिखित करना चाहिए:

  • कि पाठ बॉक्स कि जेकब क्रिस्टेंसेन तरह वांछित स्वरूपण व्यवहार का सुझाव दिया है फैली हुई है एक कस्टम नियंत्रण लिखें।
  • एक कस्टम वैल्यू कनवर्टर लिखें जो या तो निर्भरता ऑब्जेक्ट या फ्रेमवर्क एलिमेंट से प्राप्त होता है और इसमें एक प्रारूपस्ट्रिंग निर्भरता प्रॉपर्टी होती है। यदि आप निर्भरता ऑब्जेक्ट रूट पर जाना चाहते हैं तो मेरा मानना ​​है कि आप "वर्चुअल शाखा" तकनीक के साथ OneWayToSource बाइंडिंग का उपयोग करके स्वरूप को स्ट्रिंगस्ट्रिंग प्रॉपर्टी में धक्का दे सकते हैं। इसके बजाय अन्य आसान तरीका फ्रेमवर्क एलिमेंट से प्राप्त हो सकता है और अपने मूल्य कनवर्टर को अपने अन्य नियंत्रणों के साथ दृश्य पेड़ में रख सकता है ताकि जब आप ElementName द्वारा आवश्यकता हो तो बस इसे बाध्य कर सकें।
  • इस पोस्ट के शीर्ष पर उल्लेखित एक जैसा व्यवहार जैसा प्रयोग करें, लेकिन स्वरूपस्ट्रिंग को सेट करने के बजाय इसके बजाय दो संलग्न गुण हैं, एक कस्टम वैल्यू कनवर्टर के लिए और एक पैरामीटर के लिए जो वैल्यू कनवर्टर को पास किया जाएगा । फिर स्वरूपस्ट्रिंग जोड़ने के लिए मूल बाध्यकारी को संशोधित करने के बजाय आप कनवर्टर और कनवर्टर पैरामीटर को बाध्यकारी में जोड़ देंगे। व्यक्तिगत रूप से मुझे लगता है कि इस विकल्प के परिणामस्वरूप सबसे अधिक पठनीय और अंतर्ज्ञानी परिणाम होगा क्योंकि संलग्न व्यवहार अधिक स्वच्छ होते हैं, फिर भी टेक्स्टबॉक्स के अलावा अन्य स्थितियों में उपयोग करने के लिए पर्याप्त लचीला है।
2

इस कोड (DefaultValueConverter.cs @ referencesource.microsoft.com से प्रेरित) एक दो तरह से किसी पाठ बॉक्स या इसी तरह के नियंत्रण के लिए बाध्य के लिए काम करता है, जब तक FormatString एक राज्य है कि वापस परिवर्तित किया जा सकता में स्रोत संपत्ति के toString() संस्करण को छोड़ देता है के रूप में। (यानी "#, 0.00" जैसे प्रारूप ठीक है क्योंकि "1,234.56" को वापस पार्स किया जा सकता है, लेकिन FormatString = "कुछ उपसर्ग टेक्स्ट #, 0.00" "कुछ उपसर्ग टेक्स्ट 1,234.56" में परिवर्तित हो जाएंगे जिन्हें वापस पार्स नहीं किया जा सकता है।)

XAML:

<TextBox> 
    <TextBox.Text> 
     <MultiBinding Converter="{StaticResource ToStringFormatConverter}" 
       ValidatesOnDataErrors="True" NotifyOnValidationError="True" TargetNullValue=""> 
      <Binding Path="Property" TargetNullValue="" /> 
      <Binding Path="PropertyStringFormat" Mode="OneWay" /> 
     </MultiBinding> 
    </TextBox.Text> 
</TextBox> 

नोट TargetNullValue नकल करता है, तो स्रोत संपत्ति अशक्त हो सकता है।

सी #:

/// <summary> 
/// Allow a binding where the StringFormat is also bound to a property (and can vary). 
/// </summary> 
public class ToStringFormatConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values.Length == 1) 
      return System.Convert.ChangeType(values[0], targetType, culture); 
     if (values.Length >= 2 && values[0] is IFormattable) 
      return (values[0] as IFormattable).ToString((string)values[1], culture); 
     return null; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     var targetType = targetTypes[0]; 
     var nullableUnderlyingType = Nullable.GetUnderlyingType(targetType); 
     if (nullableUnderlyingType != null) { 
      if (value == null) 
       return new[] { (object)null }; 
      targetType = nullableUnderlyingType; 
     } 
     try { 
      object parsedValue = ToStringFormatConverter.TryParse(value, targetType, culture); 
      return parsedValue != DependencyProperty.UnsetValue 
       ? new[] { parsedValue } 
       : new[] { System.Convert.ChangeType(value, targetType, culture) }; 
     } catch { 
      return null; 
     } 
    } 

    // Some types have Parse methods that are more successful than their type converters at converting strings 
    private static object TryParse(object value, Type targetType, CultureInfo culture) 
    { 
     object result = DependencyProperty.UnsetValue; 
     string stringValue = value as string; 

     if (stringValue != null) { 
      try { 
       MethodInfo mi; 
       if (culture != null 
        && (mi = targetType.GetMethod("Parse", 
         BindingFlags.Public | BindingFlags.Static, null, 
         new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider) }, null)) 
        != null) { 
        result = mi.Invoke(null, new object[] { stringValue, NumberStyles.Any, culture }); 
       } 
       else if (culture != null 
        && (mi = targetType.GetMethod("Parse", 
         BindingFlags.Public | BindingFlags.Static, null, 
         new[] { typeof(string), typeof(IFormatProvider) }, null)) 
        != null) { 
        result = mi.Invoke(null, new object[] { stringValue, culture }); 
       } 
       else if ((mi = targetType.GetMethod("Parse", 
         BindingFlags.Public | BindingFlags.Static, null, 
         new[] { typeof(string) }, null)) 
        != null) { 
        result = mi.Invoke(null, new object[] { stringValue }); 
       } 
      } catch (TargetInvocationException) { 
      } 
     } 

     return result; 
    } 
}