मैं कई मूल्य रूपांतरणों की आवश्यकता वाले विचारों के साथ एक WPF आवेदन पर काम कर रहा हूँ। प्रारंभ में, मेरा दर्शन ( एक्सएएमएल चेले पर इस जीवंत बहस से प्रेरित होकर ) यह था कि मुझे दृश्य की डेटा आवश्यकताओं का समर्थन करने के बारे में कड़ाई से विचार मॉडल बनाना चाहिए । इसका मतलब यह था कि डेटा को विज़न, ब्रश, साइज़ आदि चीजों में बदलने के लिए आवश्यक वैल्यू कन्वर्सेन्स को वैल्यू कन्वर्टर्स और मल्टी-वैल्यू कन्वर्टर्स के साथ संभाला जाएगा। वैचारिक रूप से, यह काफी सुरुचिपूर्ण लग रहा था। दृश्य मॉडल और दृश्य दोनों का एक अलग उद्देश्य होगा और अच्छी तरह से डिकोड किया जाएगा। "डेटा" और "लुक" के बीच एक स्पष्ट रेखा खींची जाएगी।
खैर, यह रणनीति "पुराने कॉलेज की कोशिश" देने के बाद, मुझे कुछ संदेह हो रहे हैं कि क्या मैं इस तरह से विकास जारी रखना चाहता हूं। मैं वास्तव में मूल्य कन्वर्टर्स को डंप करने और दृश्य मॉडल के हाथों में वर्ग मान को (लगभग) सभी मूल्य रूपांतरण के लिए जिम्मेदारी देने पर जोर दे रहा हूं।
मूल्य कन्वर्टर्स का उपयोग करने की वास्तविकता केवल स्पष्ट रूप से पृथक चिंताओं के स्पष्ट मूल्य को मापने के लिए प्रतीत नहीं होती है। मूल्य कन्वर्टर्स के साथ मेरा सबसे बड़ा मुद्दा यह है कि वे उपयोग करने के लिए थकाऊ हैं। आपको एक नया वर्ग बनाना होगा, लागू करना होगा IValueConverter
या सही प्रकार IMultiValueConverter
से मान या मान डालना होगा object
, DependencyProperty.Unset
कम से कम (बहु-मूल्य कन्वर्टर्स के लिए) परीक्षण करना है , रूपांतरण तर्क लिखना है, कनवर्टर को संसाधन शब्दकोश में पंजीकृत करना है [नीचे अपडेट देखें ], और अंत में, XAML का उपयोग करते हुए कन्वर्टर को हुक अप करें (जिसमें बाध्यकारी (एस) और कनवर्टर के नाम दोनों के लिए जादू के तार का उपयोग करने की आवश्यकता होती है)[नीचे अपडेट देखें])। डिबगिंग प्रक्रिया कोई पिकनिक नहीं है, क्योंकि त्रुटि संदेश अक्सर गुप्त होते हैं, खासकर विज़ुअल स्टूडियो के डिज़ाइन मोड / एक्सप्रेशन ब्लेंड में।
यह कहना नहीं है कि वैकल्पिक - सभी मूल्य रूपांतरण के लिए दृश्य मॉडल को जिम्मेदार बनाना - एक सुधार है। यह बहुत अच्छी तरह से घास दूसरी तरफ हरियाली होने का मामला हो सकता है। चिंताओं के सुरुचिपूर्ण पृथक्करण को खोने के अलावा, आपको व्युत्पन्न गुणों का एक समूह लिखना होगा और यह सुनिश्चित करना होगा कि RaisePropertyChanged(() => DerivedProperty)
आधार गुणों की स्थापना करते समय आप कर्तव्यनिष्ठा से कॉल करें , जो एक अप्रिय रखरखाव मुद्दा साबित हो सकता है।
निम्नलिखित एक प्रारंभिक सूची है जिसे मैंने रूपांतरण मॉडल को बदलने के लिए पेशेवरों और विपक्षों को एक साथ रखा और रूपांतरण कन्वर्टर्स को संभालने और मान कन्वर्टर्स के साथ दूर करने की अनुमति दी:
- पेशेवरों:
- बहु-कन्वर्टर्स समाप्त होने के बाद से कम कुल बाइंडिंग
- कम जादू तार (बंधन पथ
+ कनवर्टर संसाधन नाम) प्रत्येक कन्वर्टर को पंजीकृत नहीं करना (प्लस इस सूची को बनाए रखना)- प्रत्येक कनवर्टर लिखने के लिए कम काम (कोई कार्यान्वयन इंटरफेस या आवश्यक कास्टिंग)
- रूपांतरणों की सहायता के लिए आसानी से निर्भरता को इंजेक्ट कर सकते हैं (जैसे, रंग तालिकाओं)
- XAML मार्कअप कम क्रिया है और पढ़ने में आसान है
- कनवर्टर का पुन: उपयोग संभव है (हालाँकि कुछ नियोजन की आवश्यकता है)
- DependencyProperty.Unset (बहु-मूल्य कन्वर्टर्स के साथ मैंने देखी गई समस्या) के साथ कोई रहस्यमय मुद्दे नहीं
* स्ट्राइकथ्रू लाभ दिखाते हैं जो गायब हो जाते हैं यदि आप मार्कअप एक्सटेंशन का उपयोग करते हैं (नीचे अपडेट देखें)
- विपक्ष:
- दृश्य मॉडल और दृश्य के बीच मजबूत युग्मन (उदाहरण के लिए, गुणों को दृश्यता और ब्रश जैसी अवधारणाओं से निपटना चाहिए)
- देखने में हर बंधन के लिए प्रत्यक्ष मानचित्रण की अनुमति देने के लिए अधिक कुल गुण
(नीचे अपडेट 2 देखें)RaisePropertyChanged
प्रत्येक व्युत्पन्न संपत्ति के लिए बुलाया जाना चाहिए- यदि रूपांतरण एक UI तत्व की संपत्ति पर आधारित है, तब भी उसे कन्वर्टर्स पर निर्भर रहना चाहिए
इसलिए, जैसा कि आप शायद बता सकते हैं, मुझे इस मुद्दे पर कुछ नाराज़गी है। मैं केवल इस बात को समझने में संकोच कर रहा हूं कि केवल यह महसूस करने के लिए कि कोडिंग प्रक्रिया अक्षम और थकाऊ है या नहीं, मैं मूल्य परिवर्तक का उपयोग करता हूं या अपने दृश्य मॉडल में कई मूल्य रूपांतरण गुणों को उजागर करता हूं।
क्या मैं किसी भी पेशेवरों / विपक्ष को याद कर रहा हूं? उन लोगों के लिए जिन्होंने मूल्य रूपांतरण के दोनों साधनों की कोशिश की है, जो आपने पाया कि आपके लिए और क्यों बेहतर काम किया? क्या कोई अन्य विकल्प हैं? (शिष्यों ने टाइप डिस्क्रिप्टर प्रदाताओं के बारे में कुछ उल्लेख किया, लेकिन मैं इस बारे में बात नहीं कर सका कि वे किस बारे में बात कर रहे थे। इस पर किसी भी जानकारी की सराहना की जाएगी।)
अपडेट करें
मुझे आज पता चला है कि मूल्य कन्वर्टर्स को पंजीकृत करने की आवश्यकता को समाप्त करने के लिए "मार्कअप एक्सटेंशन" नामक कुछ का उपयोग करना संभव है। वास्तव में, यह न केवल उन्हें पंजीकृत करने की आवश्यकता को समाप्त करता है, बल्कि जब आप टाइप करते हैं, तो यह वास्तव में एक कनवर्टर का चयन करने के लिए प्रखरता प्रदान करता है Converter=
। यहाँ लेख है कि मुझे शुरू कर दिया गया है: http://www.wpftutorial.net/ValueConverters.html ।
मार्कअप एक्सटेंशन का उपयोग करने की क्षमता ऊपर मेरे पेशेवरों और विपक्ष लिस्टिंग और चर्चा में कुछ हद तक संतुलन बदलती है (स्ट्राइकथ्रू देखें)।
इस रहस्योद्घाटन के परिणामस्वरूप, मैं एक हाइब्रिड प्रणाली के साथ प्रयोग कर रहा हूं जहां मैं कन्वर्टर्स का उपयोग BoolToVisibility
करता हूं और जो मैं कॉल करता हूं MatchToVisibility
और अन्य सभी रूपांतरणों के लिए व्यू मॉडल। MatchToVisibility मूल रूप से एक कनवर्टर है जो मुझे जांचता है कि क्या बाध्य मान (आमतौर पर एक एनम) XAML में निर्दिष्ट एक या अधिक मानों से मेल खाता है।
उदाहरण:
Visibility="{Binding Status, Converter={vc:MatchToVisibility
IfTrue=Visible, IfFalse=Hidden, Value1=Finished, Value2=Canceled}}"
मूल रूप से स्थिति समाप्त हो गई है या रद्द कर दी गई है, तो मूल रूप से यह क्या जाँच करता है। यदि यह है, तो दृश्यता "विज़िबल" के लिए सेट हो जाती है। अन्यथा, यह "हिडन" के लिए सेट हो जाता है। यह एक बहुत ही सामान्य परिदृश्य निकला, और इस कनवर्टर के होने से मुझे अपने दृश्य मॉडल (प्लस संबंधित RaisePropertyChanged स्टेटमेंट्स) पर लगभग 15 संपत्तियां बचाई गईं। ध्यान दें कि जब आप टाइप करते हैं Converter={vc:
, तो "MatchToVisibility" एक अंतर्मुखी मेनू में दिखाई देता है। यह त्रुटियों की संभावना को कम करता है और मूल्य कन्वर्टर्स को कम थकाऊ बनाता है (आपको अपने इच्छित मान कनवर्टर का नाम याद रखने या देखने की आवश्यकता नहीं है)।
यदि आप उत्सुक हैं, तो मैं नीचे दिए गए कोड को डालूँगा। के इस कार्यान्वयन में से एक महत्वपूर्ण विशेषता MatchToVisibility
है कि यह अगर सीमित मान एक है देखने के लिए जांच करता है enum
, और अगर यह है, यह जाँच करता है सुनिश्चित करने के लिए Value1
, Value2
आदि भी एक ही प्रकार के enums हैं। यह एक डिज़ाइन-टाइम और रन-टाइम चेक प्रदान करता है कि क्या एनम के किसी भी मान को गलत किया गया है। इसे संकलन-समय पर जांच में सुधार करने के लिए, आप इसके बजाय निम्नलिखित का उपयोग कर सकते हैं (यदि मैंने इसे हाथ से टाइप किया है तो कृपया मुझे क्षमा करें यदि मैंने कोई गलती की है):
Visibility="{Binding Status, Converter={vc:MatchToVisibility
IfTrue={x:Type {win:Visibility.Visible}},
IfFalse={x:Type {win:Visibility.Hidden}},
Value1={x:Type {enum:Status.Finished}},
Value2={x:Type {enum:Status.Canceled}}"
हालांकि यह सुरक्षित है, यह मेरे लिए इसके लायक होने की क्रिया मात्र है। अगर मैं ऐसा करने जा रहा हूं तो मैं सिर्फ व्यू मॉडल पर एक संपत्ति का उपयोग कर सकता हूं। वैसे भी, मुझे पता है कि डिज़ाइन-टाइम चेक उन परिदृश्यों के लिए पूरी तरह से पर्याप्त है जो मैंने अब तक की कोशिश की है।
यहाँ के लिए कोड है MatchToVisibility
[ValueConversion(typeof(object), typeof(Visibility))]
public class MatchToVisibility : BaseValueConverter
{
[ConstructorArgument("ifTrue")]
public object IfTrue { get; set; }
[ConstructorArgument("ifFalse")]
public object IfFalse { get; set; }
[ConstructorArgument("value1")]
public object Value1 { get; set; }
[ConstructorArgument("value2")]
public object Value2 { get; set; }
[ConstructorArgument("value3")]
public object Value3 { get; set; }
[ConstructorArgument("value4")]
public object Value4 { get; set; }
[ConstructorArgument("value5")]
public object Value5 { get; set; }
public MatchToVisibility() { }
public MatchToVisibility(
object ifTrue, object ifFalse,
object value1, object value2 = null, object value3 = null,
object value4 = null, object value5 = null)
{
IfTrue = ifTrue;
IfFalse = ifFalse;
Value1 = value1;
Value2 = value2;
Value3 = value3;
Value4 = value4;
Value5 = value5;
}
public override object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var ifTrue = IfTrue.ToString().ToEnum<Visibility>();
var ifFalse = IfFalse.ToString().ToEnum<Visibility>();
var values = new[] { Value1, Value2, Value3, Value4, Value5 };
var valueStrings = values.Cast<string>();
bool isMatch;
if (Enum.IsDefined(value.GetType(), value))
{
var valueEnums = valueStrings.Select(vs => vs == null ? null : Enum.Parse(value.GetType(), vs));
isMatch = valueEnums.ToList().Contains(value);
}
else
isMatch = valueStrings.Contains(value.ToString());
return isMatch ? ifTrue : ifFalse;
}
}
यहाँ के लिए कोड है BaseValueConverter
// this is how the markup extension capability gets wired up
public abstract class BaseValueConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public abstract object Convert(
object value, Type targetType, object parameter, CultureInfo culture);
public virtual object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
यहाँ ToEnum विस्तार विधि है
public static TEnum ToEnum<TEnum>(this string text)
{
return (TEnum)Enum.Parse(typeof(TEnum), text);
}
अपडेट २
जब से मैंने इस प्रश्न को पोस्ट किया है, मैं एक ओपन-सोर्स प्रोजेक्ट के साथ आया हूं जो गुणों और निर्भर गुणों के लिए NotifyPropertyChanged कोड को इंजेक्ट करने के लिए "IL बुनाई" का उपयोग करता है। यह जोश स्मिथ के दृष्टिकोण मॉडल को "स्टेरॉयड पर वैल्यू कन्वर्टर" के रूप में लागू करता है, जो एक पूर्ण हवा है। आप बस "ऑटो-इंप्लीमेंटेड प्रॉपर्टीज़" का उपयोग कर सकते हैं, और बाकी बुनकर करेंगे।
उदाहरण:
अगर मैं यह कोड दर्ज करता हूं:
public string GivenName { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenName, FamilyName);
}
}
... यह क्या संकलित है:
string givenNames;
public string GivenNames
{
get { return givenName; }
set
{
if (value != givenName)
{
givenNames = value;
OnPropertyChanged("GivenName");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenName, FamilyName);
}
}
कोड की मात्रा में यह एक बहुत बड़ी बचत है, जिसे आपको लिखना, पढ़ना, स्क्रॉल करना, इत्यादि अधिक महत्वपूर्ण है, हालांकि, यह आपको यह पता लगाने से बचाता है कि आपकी निर्भरताएं क्या हैं। आप नए "प्रॉपर्टी हो जाता है" जोड़ सकते हैं जैसे FullName
बिना RaisePropertyChanged()
कॉल के जोड़ने के लिए निर्भरता की श्रृंखला को श्रमसाध्य रूप से ऊपर ले जाने के ।
इस ओपन-सोर्स प्रोजेक्ट को क्या कहा जाता है? मूल संस्करण को "NotifyPropertyWeaver" कहा जाता है, लेकिन स्वामी (साइमन पॉटर) ने तब से IL बुनकरों की एक पूरी श्रृंखला की मेजबानी के लिए "Fody" नामक एक मंच बनाया है। इस नए प्लेटफॉर्म के तहत NotifyPropertyWeaver के बराबर को PropertyChanged.Fody कहा जाता है।
- फ़ॉडी सेटअप निर्देश: http://code.google.com/p/fody/wiki/SampleUsage ("PropertyChanged" के साथ "Virtuosity" बदलें)
- PropertyChanged.Fody प्रोजेक्ट साइट: http://code.google.com/p/propertychanged/
यदि आप NotifyPropertyWeaver के साथ जाना पसंद करते हैं (जो कि स्थापित करने के लिए थोड़ा सरल है, लेकिन जरूरी नहीं कि भविष्य में बग फिक्स से परे इसे अपडेट किया जाए), यहां प्रोजेक्ट साइट है: http://code.google.com/p/ notifypropertyweaver /
किसी भी तरह से, इन आईएल बुनकर समाधान स्टेरॉयड बनाम मूल्य कन्वर्टर्स पर व्यू मॉडल के बीच बहस में पूरी तरह से पथरी को बदल देते हैं।
MatchToVisibility
कुछ सरल मोड स्विच को सक्षम करने के लिए एक सुविधाजनक तरीका लग रहा था (मेरे पास विशेष रूप से एक टन भागों के साथ एक दृश्य है जिसे चालू और बंद किया जा सकता है। ज्यादातर मामलों में, x:Name
मोड से मिलान करने के लिए दृश्य के अनुभागों को लेबल (साथ ) भी दिया जाता है। वे इसके अनुरूप हैं।) यह वास्तव में मेरे साथ नहीं हुआ कि यह "व्यावसायिक तर्क" है, लेकिन मैं आपकी टिप्पणी को कुछ विचार दूंगा।
BooleanToVisibility
एक मान लेता है जो दृश्यता (सच / गलत) से संबंधित है और इसे दूसरे में अनुवाद करता है। यह एक आदर्श उपयोग की तरह लगता हैValueConverter
। दूसरी ओर,MatchToVisibility
व्यापार तर्क को एन्कोडिंग हैView
(किस प्रकार के आइटम दिखाई देने चाहिए)। मेरी राय में इस तर्क को नीचे धकेल दिया जाना चाहिएViewModel
, या इससे भी आगे जिसे मैं कहता हूंEditModel
। उपयोगकर्ता क्या देख सकता है परीक्षण के तहत कुछ होना चाहिए।