क्या मूल्य कन्वर्टर्स की तुलना में वे अधिक परेशान हैं?


20

मैं कई मूल्य रूपांतरणों की आवश्यकता वाले विचारों के साथ एक WPF आवेदन पर काम कर रहा हूँ। प्रारंभ में, मेरा दर्शन ( एक्सएएमएल चेले पर इस जीवंत बहस से प्रेरित होकर ) यह था कि मुझे दृश्य की डेटा आवश्यकताओं का समर्थन करने के बारे में कड़ाई से विचार मॉडल बनाना चाहिए । इसका मतलब यह था कि डेटा को विज़न, ब्रश, साइज़ आदि चीजों में बदलने के लिए आवश्यक वैल्यू कन्वर्सेन्स को वैल्यू कन्वर्टर्स और मल्टी-वैल्यू कन्वर्टर्स के साथ संभाला जाएगा। वैचारिक रूप से, यह काफी सुरुचिपूर्ण लग रहा था। दृश्य मॉडल और दृश्य दोनों का एक अलग उद्देश्य होगा और अच्छी तरह से डिकोड किया जाएगा। "डेटा" और "लुक" के बीच एक स्पष्ट रेखा खींची जाएगी।

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

मूल्य कन्वर्टर्स का उपयोग करने की वास्तविकता केवल स्पष्ट रूप से पृथक चिंताओं के स्पष्ट मूल्य को मापने के लिए प्रतीत नहीं होती है। मूल्य कन्वर्टर्स के साथ मेरा सबसे बड़ा मुद्दा यह है कि वे उपयोग करने के लिए थकाऊ हैं। आपको एक नया वर्ग बनाना होगा, लागू करना होगा IValueConverterया सही प्रकार IMultiValueConverterसे मान या मान डालना होगा object, DependencyProperty.Unsetकम से कम (बहु-मूल्य कन्वर्टर्स के लिए) परीक्षण करना है , रूपांतरण तर्क लिखना है, कनवर्टर को संसाधन शब्दकोश में पंजीकृत करना है [नीचे अपडेट देखें ], और अंत में, XAML का उपयोग करते हुए कन्वर्टर को हुक अप करें (जिसमें बाध्यकारी (एस) और कनवर्टर के नाम दोनों के लिए जादू के तार का उपयोग करने की आवश्यकता होती है)[नीचे अपडेट देखें])। डिबगिंग प्रक्रिया कोई पिकनिक नहीं है, क्योंकि त्रुटि संदेश अक्सर गुप्त होते हैं, खासकर विज़ुअल स्टूडियो के डिज़ाइन मोड / एक्सप्रेशन ब्लेंड में।

यह कहना नहीं है कि वैकल्पिक - सभी मूल्य रूपांतरण के लिए दृश्य मॉडल को जिम्मेदार बनाना - एक सुधार है। यह बहुत अच्छी तरह से घास दूसरी तरफ हरियाली होने का मामला हो सकता है। चिंताओं के सुरुचिपूर्ण पृथक्करण को खोने के अलावा, आपको व्युत्पन्न गुणों का एक समूह लिखना होगा और यह सुनिश्चित करना होगा कि RaisePropertyChanged(() => DerivedProperty)आधार गुणों की स्थापना करते समय आप कर्तव्यनिष्ठा से कॉल करें , जो एक अप्रिय रखरखाव मुद्दा साबित हो सकता है।

निम्नलिखित एक प्रारंभिक सूची है जिसे मैंने रूपांतरण मॉडल को बदलने के लिए पेशेवरों और विपक्षों को एक साथ रखा और रूपांतरण कन्वर्टर्स को संभालने और मान कन्वर्टर्स के साथ दूर करने की अनुमति दी:

  • पेशेवरों:
    • बहु-कन्वर्टर्स समाप्त होने के बाद से कम कुल बाइंडिंग
    • कम जादू तार (बंधन पथ + कनवर्टर संसाधन नाम )
    • प्रत्येक कन्वर्टर को पंजीकृत नहीं करना (प्लस इस सूची को बनाए रखना)
    • प्रत्येक कनवर्टर लिखने के लिए कम काम (कोई कार्यान्वयन इंटरफेस या आवश्यक कास्टिंग)
    • रूपांतरणों की सहायता के लिए आसानी से निर्भरता को इंजेक्ट कर सकते हैं (जैसे, रंग तालिकाओं)
    • XAML मार्कअप कम क्रिया है और पढ़ने में आसान है
    • कनवर्टर का पुन: उपयोग संभव है (हालाँकि कुछ नियोजन की आवश्यकता है)
    • DependencyProperty.Unset (बहु-मूल्य कन्वर्टर्स के साथ मैंने देखी गई समस्या) के साथ कोई रहस्यमय मुद्दे नहीं

* स्ट्राइकथ्रू लाभ दिखाते हैं जो गायब हो जाते हैं यदि आप मार्कअप एक्सटेंशन का उपयोग करते हैं (नीचे अपडेट देखें)

  • विपक्ष:
    • दृश्य मॉडल और दृश्य के बीच मजबूत युग्मन (उदाहरण के लिए, गुणों को दृश्यता और ब्रश जैसी अवधारणाओं से निपटना चाहिए)
    • देखने में हर बंधन के लिए प्रत्यक्ष मानचित्रण की अनुमति देने के लिए अधिक कुल गुण
    • RaisePropertyChanged प्रत्येक व्युत्पन्न संपत्ति के लिए बुलाया जाना चाहिए (नीचे अपडेट 2 देखें)
    • यदि रूपांतरण एक 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 कहा जाता है।

यदि आप NotifyPropertyWeaver के साथ जाना पसंद करते हैं (जो कि स्थापित करने के लिए थोड़ा सरल है, लेकिन जरूरी नहीं कि भविष्य में बग फिक्स से परे इसे अपडेट किया जाए), यहां प्रोजेक्ट साइट है: http://code.google.com/p/ notifypropertyweaver /

किसी भी तरह से, इन आईएल बुनकर समाधान स्टेरॉयड बनाम मूल्य कन्वर्टर्स पर व्यू मॉडल के बीच बहस में पूरी तरह से पथरी को बदल देते हैं।


बस एक नोट: BooleanToVisibilityएक मान लेता है जो दृश्यता (सच / गलत) से संबंधित है और इसे दूसरे में अनुवाद करता है। यह एक आदर्श उपयोग की तरह लगता है ValueConverter। दूसरी ओर, MatchToVisibilityव्यापार तर्क को एन्कोडिंग है View(किस प्रकार के आइटम दिखाई देने चाहिए)। मेरी राय में इस तर्क को नीचे धकेल दिया जाना चाहिए ViewModel, या इससे भी आगे जिसे मैं कहता हूं EditModel। उपयोगकर्ता क्या देख सकता है परीक्षण के तहत कुछ होना चाहिए।
स्कॉट व्हिटलॉक

@ सच, ​​यह एक अच्छी बात है। मैं अभी जिस ऐप पर काम कर रहा हूं, वह वास्तव में "व्यवसाय" ऐप नहीं है, जहां उपयोगकर्ताओं के लिए अलग-अलग अनुमति स्तर हैं, इसलिए मैं उन लाइनों के साथ नहीं सोच रहा था। MatchToVisibilityकुछ सरल मोड स्विच को सक्षम करने के लिए एक सुविधाजनक तरीका लग रहा था (मेरे पास विशेष रूप से एक टन भागों के साथ एक दृश्य है जिसे चालू और बंद किया जा सकता है। ज्यादातर मामलों में, x:Nameमोड से मिलान करने के लिए दृश्य के अनुभागों को लेबल (साथ ) भी दिया जाता है। वे इसके अनुरूप हैं।) यह वास्तव में मेरे साथ नहीं हुआ कि यह "व्यावसायिक तर्क" है, लेकिन मैं आपकी टिप्पणी को कुछ विचार दूंगा।
devuxer

उदाहरण: कहते हैं कि आपके पास एक स्टीरियो सिस्टम था जो या तो रेडियो, सीडी या एमपी 3 मोड में हो सकता है। मान लें कि UI के विभिन्न हिस्सों में प्रत्येक मोड के लिए दृश्य हैं। आप या तो (1) यह देखने का निर्णय कर सकते हैं कि कौन सा ग्राफिक्स किस मोड के अनुरूप है और उन्हें तदनुसार चालू / बंद करें, (2) प्रत्येक मोड मान के लिए व्यू मॉडल पर गुण उजागर करें (जैसे, IsModeRadio, IsModeCD), या (3) उजागर करें प्रत्येक ग्राफिकल तत्व / समूह (जैसे, IsRadioLightOn, IsCDButtonGroupOn) के लिए दृश्य मॉडल पर गुण। (1) मेरे विचार से स्वाभाविक रूप से फिट लग रहा था, क्योंकि इसमें पहले से ही जागरूकता है। आप इस मामले में क्या सोचते हैं?
devuxer

यह सबसे लंबा सवाल है, मैंने कभी पूरे एसई में देखा है! :]
trejder

जवाबों:


10

मैंने ValueConvertersकुछ मामलों में इस्तेमाल किया है और ViewModelदूसरों में तर्क डाला है। मेरी भावना यह है कि परत ValueConverterका एक हिस्सा बन जाता है View, इसलिए यदि तर्क वास्तव में Viewतब का हिस्सा है, तो इसे वहां रख दें ViewModel

व्यक्तिगत रूप से मुझे इस तरह की समस्याओं से ViewModelनिपटने में कोई समस्या नहीं दिखती है View, जैसे Brushकि क्योंकि मेरे अनुप्रयोगों में ViewModelकेवल परीक्षण योग्य और बाँधने योग्य सतह के रूप में मौजूद है View। हालांकि, कुछ लोग ViewModel(मैं नहीं) में बहुत सारे व्यावसायिक तर्क रखते हैं और उस मामले ViewModelमें उनके व्यापार परत के एक हिस्से की तरह अधिक है, इसलिए उस स्थिति में मैं वहां WPF- विशिष्ट सामान नहीं चाहूंगा।

मैं एक अलग जुदाई पसंद करता हूँ:

  • View- WPF सामान, कभी कभी untestable (XAML और कोड-पीछे की तरह), लेकिन यह भी ValueConverterहै
  • ViewModel - परीक्षण योग्य और बाइंडेबल वर्ग जो कि WPF- विशिष्ट भी है
  • EditModel - व्यापार परत का हिस्सा जो हेरफेर के दौरान मेरे मॉडल का प्रतिनिधित्व करता है
  • EntityModel - व्यवसाय परत का वह हिस्सा जो मेरे मॉडल का प्रतिनिधित्व करता है
  • Repository- EntityModelडेटाबेस की दृढ़ता के लिए जिम्मेदार

तो, जिस तरह से मैं यह करता हूं, मेरे पास ValueConverterएस के लिए बहुत कम उपयोग है

जिस तरह से मैं आपके कुछ "कॉन" से दूर हो गया, वह मेरे ViewModelबहुत सामान्य बनाने के लिए है। उदाहरण के लिए, ViewModelमेरे पास ChangeValueViewModelएक लेबल संपत्ति और एक मूल्य संपत्ति को लागू करता है। पर Viewवहाँ एक है Labelकि लेबल संपत्ति को बांधता है और एक TextBoxहै कि मूल्य संपत्ति को बांधता है।

मैं तो एक है ChangeValueViewजो टाइप DataTemplateका बंद है ChangeValueViewModel। जब भी WPF देखता है कि ViewModelयह लागू होता है View। मेरी के निर्माता ChangeValueViewModelबातचीत तर्क से अपने राज्य को ताज़ा करने की जरूरत है लेता है EditModel(आम तौर पर सिर्फ एक में गुजर Func<string>) और एक्शन लेना उपयोगकर्ता संपादित करता है जब मूल्य (सिर्फ एक की जरूरत है Actionकि में कुछ तर्क निष्पादित करता है EditModel)।

अभिभावक ViewModel(स्क्रीन के लिए) EditModelइसके निर्माता में एक लेता है और उचित प्राथमिक ViewModelजैसे कि तत्काल का उपयोग करता है ChangeValueViewModel। चूंकि माता-पिता ViewModelउपयोगकर्ता को कोई भी बदलाव करने के लिए कार्रवाई करने के लिए इंजेक्शन लगा रहा है, इसलिए यह इन सभी कार्यों को रोक सकता है और अन्य कार्रवाई कर सकता है। इसलिए, ChangeValueViewModelजैसे दिख सकता है के लिए इंजेक्शन संपादित कार्रवाई :

(string newValue) =>
{
    editModel.SomeField = newValue;
    foreach(var childViewModel in this.childViewModels)
    {
        childViewModel.RefreshStateFromEditModel();
    }
}

जाहिर है कि foreachलूप को अन्यत्र रिफ्लेक्ट किया जा सकता है, लेकिन यह क्या कार्रवाई करता है, इसे मॉडल पर लागू करें, फिर (मॉडल ने अपने राज्य को किसी अज्ञात तरीके से अपडेट किया है), सभी बच्चों ViewModelको अपने राज्य से जाने और प्राप्त करने के लिए कहता है फिर से मॉडल। यदि राज्य बदल गया है, तो वे अपनी PropertyChangedघटनाओं को निष्पादित करने के लिए जिम्मेदार हैं , आवश्यकतानुसार।

कि, एक सूची बॉक्स और एक विवरण पैनल के बीच की बातचीत को काफी अच्छी तरह से संभालता है। जब उपयोगकर्ता एक नई पसंद का चयन करता है, तो यह विकल्प के EditModelसाथ अद्यतन करता है , और EditModelविस्तार पैनल के लिए उजागर किए गए गुणों के मूल्यों को बदलता है। जो ViewModelबच्चे विस्तार पैनल की जानकारी प्रदर्शित करने के लिए ज़िम्मेदार होते हैं, वे स्वचालित रूप से अधिसूचित हो जाते हैं कि उन्हें नए मूल्यों की जाँच करने की आवश्यकता होती है, और यदि वे बदल गए हैं, तो वे अपनी PropertyChangedघटनाओं को आग लगा देते हैं।


/ मंजूरी। यह बहुत सुंदर है कि मेरा कैसे दिखता है।
इयान

+1। आपके उत्तर के लिए धन्यवाद, स्कॉट, मेरे पास बहुत कुछ वही परतें हैं जो आप करते हैं, और मैं दृश्य मॉडल में व्यावसायिक तर्क भी नहीं रखता हूं। (रिकॉर्ड के लिए, मैं EntityFramework Code First का उपयोग कर रहा हूं, और मेरे पास एक सेवा परत है जो दृश्य मॉडल और इकाई मॉडल के बीच अनुवाद करती है, और इसके विपरीत।) तो, यह देखते हुए, मुझे लगता है कि आप कह रहे हैं कि बहुत लागत नहीं है। दृश्य मॉडल परत में सभी / अधिकांश रूपांतरण तर्क रखने के लिए।
devuxer

@ डैनएम - हां, मैं सहमत हूं। मैं ViewModelपरत में रूपांतरण को प्राथमिकता दूंगा। हर कोई मेरे साथ सहमत नहीं है, लेकिन यह इस बात पर निर्भर करता है कि आपकी वास्तुकला कैसे काम करती है।
स्कॉट व्हिटलॉक

2
मैं पहले पैराग्राफ को पढ़ने के बाद +1 कहने जा रहा था, लेकिन फिर मैंने आपके 2 को पढ़ा और ViewModels में दृश्य-विशिष्ट कोड डालने से असहमत था। एक अपवाद है अगर ViewModel विशेष रूप से एक सामान्य दृश्य के पीछे जाने के लिए बनाया गया है (जैसे कि CalendarViewModelएक CalendarViewUserControl के लिए, या एक के DialogViewModelलिए DialogView)। यह सिर्फ मेरी राय है, हालांकि :)
राहेल

@ राचेल - ठीक है, अगर आपने मेरे दूसरे पैराग्राफ को पढ़ना जारी रखा, तो आप देखेंगे कि वास्तव में मैं क्या कर रहा था। :) मेरे ViewModels में कोई व्यावसायिक तर्क नहीं है ।
स्कॉट व्हिटलॉक

8

यदि रूपांतरण कुछ दृश्य-संबंधी है, जैसे कि किसी वस्तु की दृश्यता तय करना, किस छवि को प्रदर्शित करना है या किस ब्रश रंग का उपयोग करना है, यह निर्धारित करना, मैंने हमेशा अपने कन्वर्टर्स को व्यू में रखा है।

यदि इसका व्यवसाय-संबंधी, जैसे कि यह निर्धारित करना कि यदि कोई फ़ील्ड नकाब किया जाना चाहिए, या यदि किसी उपयोगकर्ता को कार्रवाई करने की अनुमति है, तो रूपांतरण मेरे ViewModel में होता है।

आपके उदाहरणों से, मुझे लगता है कि आपको WPF का एक बड़ा टुकड़ा याद आ रहा है DataTriggers:। आप सशर्त मानों का निर्धारण करने के लिए कन्वर्टर्स का उपयोग कर रहे हैं, लेकिन कन्वर्टर्स वास्तव में एक डेटा प्रकार को दूसरे में परिवर्तित करने के लिए होना चाहिए।

ऊपर आपके उदाहरण में

उदाहरण: कहते हैं कि आपके पास एक स्टीरियो सिस्टम था जो या तो रेडियो, सीडी या एमपी 3 मोड में हो सकता है। मान लें कि UI के विभिन्न हिस्सों में प्रत्येक मोड के लिए दृश्य हैं। आप या तो (1) यह देखने का निर्णय कर सकते हैं कि कौन सा ग्राफिक्स किस मोड के अनुरूप है और उन्हें तदनुसार चालू / बंद करें, (2) प्रत्येक मोड मान के लिए व्यू मॉडल पर गुण उजागर करें (जैसे, IsModeRadio, IsModeCD), या (3) उजागर करें प्रत्येक ग्राफिकल तत्व / समूह (जैसे, IsRadioLightOn, IsCDButtonGroupOn) के दृश्य मॉडल पर गुण। (1) मेरे विचार से स्वाभाविक रूप से फिट लग रहा था, क्योंकि इसमें पहले से ही जागरूकता है। आप इस मामले में क्या सोचते हैं?

मैं यह DataTriggerनिर्धारित करने के लिए कि किस छवि को प्रदर्शित करने के लिए एक का उपयोग करूंगा , न कि ए Converter। एक कनवर्टर एक डेटा प्रकार को दूसरे में परिवर्तित करने के लिए है, जबकि एक ट्रिगर का उपयोग मूल्य के आधार पर कुछ गुणों को निर्धारित करने के लिए किया जाता है।

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{StaticResource RadioImage}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Mode}" Value="CD">
            <Setter Property="Source" Value="{StaticResource CDImage}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Mode}" Value="MP3">
            <Setter Property="Source" Value="{StaticResource MP3Image}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

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

<Style x:Key="RadioImageStyle">
    <Setter Property="Source" Value="{Binding ImageFilePath, 
            Converter={StaticResource StringPathToBitmapConverter}}" />
</Style>

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


+1। आप कुछ अच्छे अंक जुटाएं। मैंने पहले भी ट्रिगर्स का उपयोग किया है, लेकिन मेरे मामले में, मैं छवियों के स्रोतों (जो एक संपत्ति हैं) को स्विच नहीं कर रहा हूं, मैं पूरे Gridतत्वों को स्विच कर रहा हूं । मैं अपने दृश्य मॉडल में डेटा के आधार पर अग्रभूमि / पृष्ठभूमि / स्ट्रोक के लिए सेट ब्रश जैसी चीजों को करने का प्रयास कर रहा हूं और एक विशिष्ट रंग पैलेट को कॉन्फिगर फाइल में परिभाषित किया गया है। मुझे यकीन नहीं है कि यह एक ट्रिगर या एक कनवर्टर के लिए एक महान फिट है। एकमात्र समस्या जो मुझे अब तक देखने के मॉडल में अधिकांश व्यू लॉजिक लगाने के साथ हो रही है, सभी RaisePropertyChanged()कॉल को वायरिंग कर रही है।
18

@DanM मैं वास्तव में उन सभी चीजों को करूंगा DataTrigger, यहां तक ​​कि ग्रिड के तत्वों को स्विच करना। आमतौर पर मैं ऐसी जगह रखता हूं ContentControlजहां मेरी गतिशील सामग्री होनी चाहिए और ContentTemplateट्रिगर में स्वैप करना चाहिए । यदि आप रुचि रखते हैं तो मेरे पास निम्न लिंक पर एक उदाहरण है (नीचे स्क्रॉल करके अनुभाग पर जाएं Using a DataTrigger) rachel53461.wordpress.com/2011/05/28/…
राहेल

मैंने पहले डेटा टेम्प्लेट और सामग्री नियंत्रण का उपयोग किया है, लेकिन मुझे ट्रिगर्स की कभी आवश्यकता नहीं है, क्योंकि मेरे पास प्रत्येक दृश्य के लिए हमेशा एक अद्वितीय दृश्य मॉडल है। वैसे भी, आपकी तकनीक सही समझ में आती है और काफी सुरुचिपूर्ण है, लेकिन यह बहुत ही क्रियात्मक है। MatchToVisibility के साथ, इसे छोटा किया जा सकता है: <TextBlock Text="I'm a Person" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Person}}"और<TextBlock Text="I'm a Business" Visibility={Binding ConsumerType, Converter={vc:MatchToVisibility IfTrue=Visible, IfFalse=Hidden, Value1=Business}}"
21

1

यह इस बात पर निर्भर करता है कि आप क्या परीक्षण कर रहे हैं , अगर कुछ भी।

कोई परीक्षण नहीं: वसीयत में इंटरमिक्स व्यू कोड w / ViewModel (आप बाद में हमेशा रिफ्लेक्टर कर सकते हैं)।
ViewModel और / या कम पर टेस्ट: कन्वर्टर्स का उपयोग करें।
मॉडल लेयर्स और / या निम्न पर टेस्ट: विस्मयादिबोधक देखें कोड w / ViewModel पर होगा

ViewModel दृश्य के लिए मॉडल को सार करता है । व्यक्तिगत रूप से, मैं ब्रश आदि के लिए ViewModel का उपयोग करूंगा और कन्वर्टर्स को छोड़ दूंगा। परत पर परीक्षण करें जहां डेटा अपने " शुद्धतम " रूप में है (यानी। मॉडल परतें )।


2
परीक्षण के बारे में दिलचस्प बिंदु, लेकिन मुझे लगता है कि मैं यह नहीं देख रहा हूं कि दृश्य मॉडल में कनवर्टर तर्क कैसे दृश्य मॉडल की परीक्षण क्षमता को नुकसान पहुंचाता है? मैं निश्चित रूप से वास्तविक यूआई नियंत्रण लगाने का सुझाव नहीं दे रहा हूं से दृश्य मॉडल में । बस देखने-विशिष्ट गुण की तरह है Visibility, SolidColorBrushऔर Thickness
devuxer

@ डैनएम: यदि आप व्यू-प्रथम दृष्टिकोण का उपयोग कर रहे हैं , तो कोई समस्या नहीं है । हालाँकि, कुछ ViewModel-first दृष्टिकोण का उपयोग करते हैं जहाँ ViewModel एक दृश्य का संदर्भ देता है, यह समस्याग्रस्त हो सकता है
जेक बर्गर

हाय जे, निश्चित रूप से एक दृष्टिकोण-पहला दृष्टिकोण। दृश्य को दृश्य मॉडल के बारे में कुछ भी नहीं पता है सिवाय इसके कि गुणों के नाम के अलावा उसे बांधने की जरूरत है। फॉलो करने के लिए धन्यवाद। +1।

0

यह संभवतः आपके द्वारा बताए गए सभी मुद्दों को हल नहीं करेगा, लेकिन विचार करने के लिए दो बिंदु हैं:

सबसे पहले, आपको अपनी पहली रणनीति में कनवर्टर कोड को कहीं और रखना होगा। क्या आप दृश्य या दृश्य मॉडल के उस हिस्से पर विचार करते हैं? यदि यह दृश्य का हिस्सा है, तो दृश्य-मॉडल के बजाय दृश्य-विशिष्ट गुणों को दृश्य में क्यों न रखें?

दूसरा, यह लगता है कि आपके गैर-कनवर्टर डिज़ाइन वास्तविक वस्तुओं के गुणों को संशोधित करने का प्रयास करते हैं जो पहले से मौजूद हैं। ऐसा लगता है कि वे पहले से ही INotifyPropertyChanged को लागू करते हैं, इसलिए उपयोग करने के लिए बाइंड करने के लिए दृश्य-विशिष्ट आवरण ऑब्जेक्ट क्यों नहीं बनाते हैं? यहाँ एक सरल उदाहरण दिया गया है:

public class RealData
{
    private bool mIsInteresting;
    public bool IsInteresting
    {
        get { return mIsInteresting; }
        set 
        {
            if (mIsInteresting != null) 
            {
                mIsInteresting = value;
                RaisePropertyChanged("IsInteresting");
            }
        }
    }
}

public class RealDataView
{
    private RealData mRealData;

    public RealDataView(RealData data)
    {
        mRealData = data;
        mRealData.PropertyChanged += OnRealDataPropertyChanged;
    }

    public Visibility IsVisiblyInteresting
    {
       get { return mRealData.IsInteresting ? Visibility.Visible : Visibility.Hidden; }
       set { mRealData.IsInteresting = (value == Visibility.Visible); }
    }

    private void OnRealDataPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "IsInteresting") 
        {
            RaisePropertyChanged(this, "IsVisiblyInteresting");
        }
    }
}

मेरा मतलब यह नहीं था कि मैं अपनी इकाई मॉडल के गुणों को सीधे दृश्य या दृश्य मॉडल में बदल रहा हूं। दृश्य मॉडल निश्चित रूप से मेरी इकाई मॉडल परत की तुलना में एक अलग परत है। वास्तव में, मैंने अब तक जो काम किया है, वह केवल पढ़ने के विचारों पर है। यह कहने के लिए नहीं है कि मेरे आवेदन में कोई संपादन शामिल नहीं होगा, लेकिन मुझे संपादन के लिए उपयोग किए जाने वाले नियंत्रणों पर कोई रूपांतरण नहीं दिखाई दे रहा है (इसलिए मान लें कि सभी बाइंडिंग एक-तरफ़ा हैं, सूचियों में चयनों को छोड़कर)। हालांकि "डेटा विचारों" के बारे में अच्छी बात है। यह एक अवधारणा थी जिसे एक्सएएमएल शिष्यों के पद में उठाया गया था जिसे मैंने अपने प्रश्न के शीर्ष पर संदर्भित किया था।
देवकर १ '

0

कभी-कभी वर्चुअलाइजेशन का लाभ उठाने के लिए वैल्यू कन्वर्टर का उपयोग करना अच्छा होता है।

इसका एक उदाहरण है कि एक परियोजना में जहां हमें एक ग्रिड में सैकड़ों हजारों कोशिकाओं के लिए बिटमास्क डेटा प्रदर्शित करना था। जब हमने हर एक सेल के लिए व्यू मॉडल में बिटमास्क को डिकोड किया तो प्रोग्राम को लोड करने में बहुत समय लग गया।

लेकिन जब हमने एक वैल्यू कन्वर्टर बनाया, जो एक एकल सेल को उस समय के एक अंश में लोड किए गए प्रोग्राम को डिकोड करता है और बस उतना ही रिस्पॉन्सिबल था, क्योंकि कन्वर्टर को केवल तब बुलाया जाता है, जब उपयोगकर्ता किसी विशेष सेल को देख रहा होता है (और उसे केवल कॉल करने की आवश्यकता होती है किसी भी समय उपयोगकर्ता द्वारा ग्रिड पर अपना दृश्य स्थानांतरित करने पर अधिकतम तीस गुना)।

मुझे नहीं पता कि एमवीवीएम शिकायत कैसे हल करता है, लेकिन इसने लोड समय में 95% की कटौती की।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.