मैं WPF बाइंडिंग को रिलेटिव सोर्स के साथ कैसे उपयोग करूं?


जवाबों:


783

यदि आप वस्तु पर किसी अन्य संपत्ति के लिए बाध्य करना चाहते हैं:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}

यदि आप पूर्वजों पर संपत्ति प्राप्त करना चाहते हैं:

{Binding Path=PathToProperty,
    RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}

यदि आप टेम्प्लेटेड पैरेंट पर प्रॉपर्टी प्राप्त करना चाहते हैं (तो आप कंट्रोलटेमप्लेट में 2 तरह से बाइंडिंग कर सकते हैं)

{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}

या, छोटा (यह केवल OneWay बाइंडिंग के लिए काम करता है):

{TemplateBinding Path=PathToProperty}

15
इसके लिए "{बाइंडिंग पाथ = PathToProperty, RelativeSource = {RelativeSource AncestorType = {x: Type typeOfAncestor}}}", ऐसा लगता है कि इसे "पूर्वज" से पहले "मोड = फाइंडऑन्टर" की आवश्यकता है
EdwardM

1
किस तकनीक के लिए? WPF में, जब आप निर्दिष्ट करते हैं तो यह अनुमान लगाया जाता है AncestorType
अबे हेइडेब्रच

2
मैं @EdwardM से सहमत हूं। जब मैं छोड़ता हूं FindAncestor, इससे पहले AncestorType, मुझे निम्न त्रुटि मिलती है: "RelativeSource FindAncestor मोड में नहीं है"। (वीएस २०१३, कम्यूनिटी वर्जन में)
३-४

1
@kmote, इसने मेरे लिए काम किया है .net 3.0 से, और मैंने एक बार फिर से सत्यापित किया कि यह kaxaml में इस तरह से काम करता है ... फिर से, आप किस तकनीक का उपयोग कर रहे हैं? WPF / Silverlight / UWP के लिए XAML प्रोसेसर अलग है, इसलिए आपके पास विभिन्न तकनीकों पर अलग-अलग परिणाम हो सकते हैं। आपने वीएस कम्युनिटी का भी उल्लेख किया है, इसलिए शायद यह एक आईडीई चेतावनी है, लेकिन रनटाइम पर काम करता है?
अबे हेइडेब्रेट

6
बस यहाँ ध्यान देना चाहता था कि यदि आप RelativeSource के DataContext में किसी प्रॉपर्टी से बंधना चाहते हैं तो आपको इसे स्पष्ट रूप से निर्दिष्ट करना होगा {Binding Path=DataContext.SomeProperty, RelativeSource=...:। यह मेरे लिए एक नौसिखिया के रूप में कुछ अप्रत्याशित था जब मैं एक DataTemplate के भीतर एक माता-पिता के DataContext को बांधने की कोशिश कर रहा था।
DrEsperanto

133
Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

के डिफ़ॉल्ट विशेषता RelativeSourceहै Modeसंपत्ति। मान्य मूल्यों का एक पूरा सेट यहाँ ( MSDN से ) दिया गया है:

  • PreviousData आप पिछले डेटा मद (नहीं है कि नियंत्रण है कि डेटा आइटम शामिल है) डेटा आइटम की सूची में प्रदर्शित किया जा रहा आबद्ध होने देता है।

  • TemplatedParent उस तत्व को संदर्भित करता है जिससे टेम्पलेट (जिसमें डेटा-बाउंड तत्व मौजूद है) लागू किया जाता है। यह टेम्प्लेटबाइंडिंगटेक्स्टेंशन सेट करने के समान है और केवल तभी लागू होता है जब बाइंडिंग एक टेम्प्लेट के भीतर हो।

  • सेल्फ रिफ़रर्स उस एलिमेंट पर जिस पर आप बाइंडिंग सेट कर रहे हैं और आपको उसी एलीमेंट पर उस एलिमेंट की एक प्रॉपर्टी को दूसरी प्रॉपर्टी से बाँधने की अनुमति देता है।

  • FindAncestor डेटा-बाउंड तत्व की मूल श्रृंखला में पूर्वज को संदर्भित करता है। आप इसका उपयोग किसी विशिष्ट प्रकार के पूर्वज या उसके उपवर्गों को बांधने के लिए कर सकते हैं। यदि आप AncestorType और / या AncestorLevel को निर्दिष्ट करना चाहते हैं तो यह आपके द्वारा उपयोग की जाने वाली विधा है।


128

यहाँ MVVM आर्किटेक्चर के संदर्भ में एक अधिक दृश्य व्याख्या है:

यहाँ छवि विवरण दर्ज करें


19
क्या मैं कुछ भुल गया? आप कैसे विचार कर सकते हैं कि एक सरल और स्पष्ट ग्राफिक? 1: बाएँ के अर्थ पर बक्से वास्तव में दाईं ओर वाले से संबंधित नहीं हैं (ViewModel के अंदर एक .cs फ़ाइल क्यों है) 2: ये DataContext तीर बिंदु क्या करते हैं? 3: क्यों संदेश संपत्ति ViewModel1 में नहीं है? और सबसे महत्वपूर्ण बात यह है कि विंडो के डेटाकोटेक्स्ट में जाने के लिए आपको एक रिलेटिव सोर्स बंधन की आवश्यकता क्यों है यदि टेक्स्टब्लॉक में पहले से ही डेटाकोटेक्स्ट है? मैं स्पष्ट रूप से यहाँ कुछ याद कर रहा हूँ इसलिए या तो मैं बहुत गूंगा हूँ या यह ग्राफिक उतना सरल और स्पष्ट नहीं है जितना हर कोई सोचता है! कृपया मुझे
बताएं

2
@ MarkusHütter आरेख एक समूह को एक नेस्टेड दृश्य और संबंधित ViewModels दिखा रहा है। View1 का DataContext ViewModel1 है, लेकिन यह BaseViewModel की संपत्ति से बाइंड करना चाहता है। क्योंकि BaseViewModel बेस व्यू (जो कि एक विंडो है) का डेटा कॉन्टेक्स्ट है, यह ऐसा पहला पेरेंट कंटेनर ढूंढकर कर सकता है जो विंडो है और इसका डेटा कॉन्टेक्स्ट लेता है।
मैर्केल जूल

6
@MatthewCargille मैं बहुत अच्छी तरह से क्या यह है पता है चाहिए मतलब है, कि मेरी बात नहीं थी। लेकिन अपने आप को किसी ऐसे व्यक्ति की स्थिति में रखें जो एक्सएएमएल और एमवीवीएम को अच्छी तरह से नहीं जानता है और आप देखेंगे कि यह सरल और स्पष्ट नहीं है
Markus Hütter

1
मुझे @ MarkusHütter के साथ सहमत होना होगा, वैसे, बाईं ओर का बंधन इस तरह सरल हो सकता है: {Binding Message}(थोड़ा और सरल ...)
florien

@florien मुझे ऐसा नहीं लगता, कम से कम मेरे उपयोग के मामले के लिए। मेरे पास एक DataTemplate है जिसे ड्रॉपडाउन मेनू (डेटाबेस से लोड) के लिए विकल्पों की एक सूची प्राप्त करने के लिए MainWindow के DataContext (मेरा viewmodel वर्ग) को संदर्भित करने की आवश्यकता है। DataTemplate एक मॉडल ऑब्जेक्ट के लिए बाध्य है जिसे डेटाबेस से भी लोड किया गया है, लेकिन इसमें केवल चयनित विकल्प तक पहुंच है। मुझे Path=DataContext.Messageकाम करने के लिए बाध्य होने के लिए स्पष्ट रूप से सेट करना पड़ा । यह समझ में आता है, यह देखते हुए कि आप चौड़ाई / ऊंचाई / आदि के सापेक्ष संबंध कर सकते हैं। एक नियंत्रण की।
DrEsperanto

47

Bechir Bejaoui अपने लेख में WPF में RelativeSources के उपयोग के मामलों को उजागर करता है :

RelativeSource एक मार्कअप एक्सटेंशन है जो विशेष रूप से बाध्यकारी मामलों में उपयोग किया जाता है, जब हम किसी दिए गए ऑब्जेक्ट की संपत्ति को किसी अन्य ऑब्जेक्ट की स्वयं की संपत्ति में बाँधने का प्रयास करते हैं, जब हम किसी वस्तु की संपत्ति को उसके किसी अन्य रिश्तेदार माता-पिता से बाँधने की कोशिश करते हैं; कस्टम नियंत्रण विकास के मामले में और अंत में एक बाध्य डेटा की एक श्रृंखला का उपयोग करने के मामले में XAML के एक टुकड़े के लिए एक निर्भरता संपत्ति के मूल्य को बाध्य करते समय। उन सभी स्थितियों को रिश्तेदार स्रोत मोड के रूप में व्यक्त किया जाता है। मैं उन सभी मामलों को एक-एक कर उजागर करूंगा।

  1. मोड स्व:

इस मामले की कल्पना करें, एक आयत जो हम चाहते हैं कि इसकी ऊँचाई हमेशा इसकी चौड़ाई के बराबर हो, एक वर्ग जो कहता है। हम तत्व नाम का उपयोग करके ऐसा कर सकते हैं

<Rectangle Fill="Red" Name="rectangle" 
                Height="100" Stroke="Black" 
                Canvas.Top="100" Canvas.Left="100"
                Width="{Binding ElementName=rectangle,
                Path=Height}"/>

लेकिन इस उपरोक्त मामले में हम बाध्यकारी वस्तु के नाम को इंगित करने के लिए बाध्य हैं, अर्थात् आयत। हम RelativeSource का उपयोग करके एक ही उद्देश्य तक अलग-अलग तरीके से पहुंच सकते हैं

<Rectangle Fill="Red" Height="100" 
               Stroke="Black" 
               Width="{Binding RelativeSource={RelativeSource Self},
               Path=Height}"/>

उस स्थिति के लिए हम बाध्यकारी वस्तु के नाम का उल्लेख करने के लिए बाध्य नहीं हैं और चौड़ाई हमेशा ऊँचाई के बराबर होगी जब भी ऊँचाई बदली जाएगी।

यदि आप चौड़ाई को ऊँचाई का आधा भाग बनाना चाहते हैं तो आप इसे बाइंडिंग मार्कअप एक्सटेंशन में एक कनवर्टर जोड़कर कर सकते हैं। चलिए अब एक और मामले की कल्पना करते हैं:

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
               Path=Parent.ActualWidth}"/>

उपरोक्त मामले का उपयोग किसी दिए गए तत्व की दी गई संपत्ति को उसके प्रत्यक्ष माता-पिता में से किसी एक के लिए टाई करने के लिए किया जाता है क्योंकि यह तत्व एक संपत्ति रखता है जिसे माता-पिता कहा जाता है। यह हमें एक अन्य रिश्तेदार स्रोत मोड की ओर ले जाता है जो फाइंडअनस्टर एक है।

  1. मोड फाइंडअनॉन्सर

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

<Canvas Name="Parent0">
    <Border Name="Parent1"
             Width="{Binding RelativeSource={RelativeSource Self},
             Path=Parent.ActualWidth}"
             Height="{Binding RelativeSource={RelativeSource Self},
             Path=Parent.ActualHeight}">
        <Canvas Name="Parent2">
            <Border Name="Parent3"
            Width="{Binding RelativeSource={RelativeSource Self},
           Path=Parent.ActualWidth}"
           Height="{Binding RelativeSource={RelativeSource Self},
              Path=Parent.ActualHeight}">
               <Canvas Name="Parent4">
               <TextBlock FontSize="16" 
               Margin="5" Text="Display the name of the ancestor"/>
               <TextBlock FontSize="16" 
                 Margin="50" 
            Text="{Binding RelativeSource={RelativeSource  
                       FindAncestor,
                       AncestorType={x:Type Border}, 
                       AncestorLevel=2},Path=Name}" 
                       Width="200"/>
                </Canvas>
            </Border>
        </Canvas>
     </Border>
   </Canvas>

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

इसलिए AncestorLevel = 2 को AncestorLevel = 1 में बदलने का प्रयास करें और देखें कि क्या होता है। फिर पूर्वज के प्रकार को पूर्वज टाइप से बदलने का प्रयास करें।

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

  1. TemplatedParent

यह मोड किसी दिए गए ControlTemplate प्रॉपर्टी को उस कंट्रोल की प्रॉपर्टी से टाई करने में सक्षम करता है, जिस पर ControlTemplate लागू होता है। इस मुद्दे को अच्छी तरह से समझने के लिए यहाँ एक उदाहरण है

<Window.Resources>
<ControlTemplate x:Key="template">
        <Canvas>
            <Canvas.RenderTransform>
                <RotateTransform Angle="20"/>
                </Canvas.RenderTransform>
            <Ellipse Height="100" Width="150" 
                 Fill="{Binding 
            RelativeSource={RelativeSource TemplatedParent},
            Path=Background}">

              </Ellipse>
            <ContentPresenter Margin="35" 
                  Content="{Binding RelativeSource={RelativeSource  
                  TemplatedParent},Path=Content}"/>
        </Canvas>
    </ControlTemplate>
</Window.Resources>
    <Canvas Name="Parent0">
    <Button   Margin="50" 
              Template="{StaticResource template}" Height="0" 
              Canvas.Left="0" Canvas.Top="0" Width="0">
        <TextBlock FontSize="22">Click me</TextBlock>
    </Button>
 </Canvas>

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


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

34

WPF में RelativeSourceबाध्यकारी तीन propertiesसेट करने के लिए उजागर करता है :

1. मोड: यह enumचार मान हो सकते हैं:

ए। पिछलाडेटा ( value=0): यहpropertyबाउंडके पिछले मान को असाइन करता है

ख। TemplatedParent ( value=1): इसका उपयोगtemplatesकिसी भी नियंत्रणको परिभाषित करते समय किया जाता हैऔर का मान / संपत्ति के लिए बाध्य करना चाहता हैcontrol

उदाहरण के लिए, परिभाषित करें ControlTemplate:

  <ControlTemplate>
        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
 </ControlTemplate>

सी। स्व ( value=2): जब हमस्वयंसेselfया किसीसे बंधना चाहते हैंproperty

उदाहरण के लिए: संदेश की जाँच की राज्य checkboxके रूप में CommandParameter, जबकि स्थापित करने CommandपरCheckBox

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />

घ। FindAncestor ( value=3): जब किसी माता-पिता सेcontrol में बंधना चाहता हूंVisual Tree

उदाहरण के लिए: बाइंड एक checkboxमें recordsएक है, तो grid, अगर header checkboxचेक किया गया है

<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />

2. पूर्वज: जब मोड FindAncestorतब किस प्रकार के पूर्वज को परिभाषित करता है

RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}

3. पूर्वज: जब मोडFindAncestorतब पूर्वज का स्तर क्या है (यदि दो समान प्रकार के माता-पिता हैंvisual tree)

RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}

उपरोक्त सभी उपयोग के मामले हैं RelativeSource binding

यहाँ एक संदर्भ लिंक दिया गया है


2
बहुत बढ़िया .. यह मेरे लिए काम कर रहा है: <DataGridCheckBoxColumn Header = "Paid" Width = "35" Binding = "{B बंधन RelativeSource = {RelativeSource मोड = FindAncestor, AncestorType = {x: Type Window}}, Path = DataContext.SelectedBuyer.Iraider। , मोड = OneWay} "/> जहां मैं मूल विंडो की
माइकल

21

टेंपलेटेडParent न भूलें:

<Binding RelativeSource="{RelativeSource TemplatedParent}"/>

या

{Binding RelativeSource={RelativeSource TemplatedParent}}

16

यह ध्यान देने योग्य है कि सिल्वरलाइट की इस सोच के लिए ठोकर खाने वालों के लिए:

सिल्वरलाइट केवल इन सब कमांड्स में से एक सबसे छोटा उपसमूह प्रदान करता है


हाँ, मैं भी SL समर्थन के लिए देख रहा था। : यह वोट करें connect.microsoft.com/VisualStudio/feedback/details/480603/...
TravisWhidden

16

मैंने WPF के बाइंडिंग सिंटैक्स को सरल बनाने के लिए एक लाइब्रेरी बनाई, जिसमें RelativeSource का उपयोग करना आसान है। यहाँ कुछ उदाहरण हैं। इससे पहले:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
{Binding Path=Text, ElementName=MyTextBox}

उपरांत:

{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}

यहाँ एक उदाहरण है कि कैसे विधि बंधन को सरलीकृत किया जाता है। इससे पहले:

// C# code
private ICommand _saveCommand;
public ICommand SaveCommand {
 get {
  if (_saveCommand == null) {
   _saveCommand = new RelayCommand(x => this.SaveObject());
  }
  return _saveCommand;
 }
}

private void SaveObject() {
 // do something
}

// XAML
{Binding Path=SaveCommand}

उपरांत:

// C# code
private void SaveObject() {
 // do something
}

// XAML
{BindTo SaveObject()}

आप पुस्तकालय यहाँ पा सकते हैं: http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html

'BEFORE' उदाहरण में ध्यान दें कि मैं विधि कोड को उपयोग करने के लिए उस कोड को पहले से ही अनुकूलित कर RelayCommandरहा था जिसका उपयोग करके मैंने अंतिम बार WPF का मूल भाग नहीं देखा था। इसके बिना 'BEFORE' का उदाहरण और भी लंबा होता।


2
इस तरह के हाथ से पकड़े जाने वाले व्यायाम एक्सएएमएल की कमजोरी को प्रदर्शित करते हैं; रास्ता बहुत जटिल है।
dudeNumber4

16

कुछ उपयोगी बिट्स और टुकड़े:

यहां बताया गया है कि यह अधिकतर कोड में कैसे किया जाता है:

Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);

मैंने बड़े पैमाने पर कोड रिहाइंड में बाइंडिंग रिलेटिव सोर्स से इसे कॉपी किया ।

इसके अलावा, MSDN पृष्ठ बहुत अच्छा है जहाँ तक उदाहरण चलते हैं: RelativeSource Class


5
WPF की मेरी अस्पष्ट स्मृति है कि कोड में बाइंडिंग करना आमतौर पर हालांकि सबसे अच्छी बात नहीं है।
नाथन कूपर

12

मैंने बस सिल्वरलाइट में एक मूल तत्व के डेटोनेक्स्ट को एक्सेस करने के लिए एक और समाधान पोस्ट किया जो मेरे लिए काम करता है। इसका उपयोग करता है Binding ElementName


10

मैंने हर उत्तर नहीं पढ़ा, लेकिन मैं सिर्फ एक बटन के रिश्तेदार स्रोत कमांड बंधन के मामले में इस जानकारी को जोड़ना चाहता हूं।

जब आप किसी रिश्तेदार स्रोत का उपयोग करते हैं Mode=FindAncestor , तो बंधन जैसा होना चाहिए:

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"

यदि आप अपने पथ में DataContext नहीं जोड़ते हैं, तो निष्पादन समय पर यह संपत्ति को पुनः प्राप्त नहीं कर सकता है।


9

यह इस पैटर्न के उपयोग का एक उदाहरण है जो मेरे लिए खाली डेटाग्रिड्स पर काम करता था।

<Style.Triggers>
    <DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
        <Setter Property="Background">
            <Setter.Value>
                <VisualBrush Stretch="None">
                    <VisualBrush.Visual>
                        <TextBlock Text="We did't find any matching records for your search..." FontSize="16" FontWeight="SemiBold" Foreground="LightCoral"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Setter.Value>
        </Setter>
    </DataTrigger>
</Style.Triggers>

6

यदि कोई तत्व दृश्य पेड़ का हिस्सा नहीं है, तो RelativeSource कभी काम नहीं करेगा।

इस मामले में, आपको एक अलग तकनीक का प्रयास करने की आवश्यकता है, जो थॉमस लेवेस्क द्वारा अग्रणी है।

उसके पास अपने ब्लॉग पर [WPF] के तहत समाधान है कि DataContext विरासत में नहीं मिलने पर डेटा को कैसे बांधें । और यह पूरी तरह से शानदार ढंग से काम करता है!

इस घटना की संभावना नहीं है कि उनका ब्लॉग नीचे है, अपेंडिक्स ए में उनके लेख की एक प्रति शामिल है ।

कृपया यहाँ टिप्पणी न करें, कृपया सीधे उनके ब्लॉग पोस्ट पर टिप्पणी करें ।

परिशिष्ट A: ब्लॉग पोस्ट का दर्पण

WPF में DataContext संपत्ति बेहद आसान है, क्योंकि यह स्वचालित रूप से उस तत्व के सभी बच्चों को विरासत में मिला है जहां आप इसे असाइन करते हैं; इसलिए आपको इसे उस प्रत्येक तत्व पर फिर से सेट करने की आवश्यकता नहीं है जिसे आप बांधना चाहते हैं। हालांकि, कुछ मामलों में DataContext सुलभ नहीं है: यह उन तत्वों के लिए होता है जो दृश्य या तार्किक पेड़ का हिस्सा नहीं हैं। फिर उन तत्वों पर संपत्ति बांधना बहुत मुश्किल हो सकता है ...

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

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding ShowPrice,
                Converter={StaticResource visibilityConverter}}"/>

दुर्भाग्य से, ShowPrice के मूल्य को बदलने का कोई प्रभाव नहीं है, और कॉलम हमेशा दिखाई देता है ... क्यों? यदि हम Visual Studio में आउटपुट विंडो को देखते हैं, तो हम निम्नलिखित लाइन को नोटिस करते हैं:

System.Windows.Data Error: 2: टारगेट एलिमेंट के लिए FrameworkElement या FrameworkContentElement गवर्निंग नहीं कर सकता। BindingExpression: पथ = ShowPrice; DataItem = बातिल; लक्ष्य तत्व 'डेटाग्रिड टेक्स्टकॉल्यूम' (हैशकोड = 32685253) है; लक्ष्य संपत्ति 'दृश्यता' है (टाइप 'दृश्यता')

संदेश बल्कि गूढ़ है, लेकिन अर्थ वास्तव में काफी सरल है: WPF को पता नहीं है कि DataContext प्राप्त करने के लिए कौन सा फ्रेमवर्क उपयोग करना है, क्योंकि स्तंभ DataGrid के दृश्य या तार्किक ट्री से संबंधित नहीं है।

हम वांछित परिणाम प्राप्त करने के लिए बाध्यकारी को ट्विक करने का प्रयास कर सकते हैं, उदाहरण के लिए डेटाग्रिड में RelativeSource की स्थापना करके:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding DataContext.ShowPrice,
                Converter={StaticResource visibilityConverter},
                RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

या हम ShowPrice से बंधे एक चेकबॉक्स को जोड़ सकते हैं, और तत्व के नाम को निर्दिष्ट करके IsChecked संपत्ति पर कॉलम दृश्यता को बांधने का प्रयास कर सकते हैं:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding IsChecked,
                Converter={StaticResource visibilityConverter},
                ElementName=chkShowPrice}"/>

लेकिन इनमें से कोई भी वर्कअराउंड काम नहीं करता है, हम हमेशा एक ही परिणाम प्राप्त करते हैं ...

इस बिंदु पर, ऐसा लगता है कि केवल व्यवहार्य दृष्टिकोण कोड-पीछे में कॉलम दृश्यता को बदलना होगा, जिसे हम आमतौर पर एमवीवीएम पैटर्न का उपयोग करते समय बचना पसंद करते हैं ... लेकिन मैं इतनी जल्दी हार नहीं मानने वाला हूं, कम से कम नहीं जबकि there पर विचार करने के लिए अन्य विकल्प हैं

हमारी समस्या का समाधान वास्तव में काफी सरल है, और फ्रीज़ेबल वर्ग का लाभ उठाता है। इस वर्ग का प्राथमिक उद्देश्य उन वस्तुओं को परिभाषित करना है जिनके पास एक परिवर्तनीय और केवल-पढ़ने के लिए स्थिति है, लेकिन हमारे मामले में दिलचस्प विशेषता यह है कि फ़्रीज़ेबल ऑब्जेक्ट डेटाकोटेक्स्ट को तब भी विरासत में प्राप्त कर सकते हैं, जब वे दृश्य या तार्किक पेड़ में न हों। मैं सटीक तंत्र को नहीं जानता जो इस व्यवहार को सक्षम बनाता है, लेकिन हम इसका फायदा उठाने के लिए अपना बाध्यकारी कार्य करने जा रहे हैं ...

विचार एक वर्ग बनाने के लिए है (मैंने इसे उन कारणों के लिए बाइंडप्रॉक्सी कहा है जो बहुत जल्द स्पष्ट हो जाना चाहिए) जो फ्रीज़ेबल को विरासत में मिला है और डेटा निर्भरता संपत्ति घोषित करता है:

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

हम तब DataGrid के संसाधनों में इस वर्ग का एक उदाहरण घोषित कर सकते हैं, और Data Data को वर्तमान DataContext में बाँध सकते हैं:

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

बाध्यकारी के लिए स्रोत के रूप में इस बाइंडप्रॉक्सी ऑब्जेक्ट (StaticResource के साथ आसानी से सुलभ) को निर्दिष्ट करने के लिए अंतिम चरण है:

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding Data.ShowPrice,
                Converter={StaticResource visibilityConverter},
                Source={StaticResource proxy}}"/>

ध्यान दें कि बाइंडिंग पथ "डेटा" के साथ उपसर्ग किया गया है, क्योंकि पथ अब बाइंडिंगप्रॉक्सी ऑब्जेक्ट के सापेक्ष है।

बाइंडिंग अब सही तरीके से काम करती है, और शोप्राइस प्रॉपर्टी के आधार पर कॉलम को ठीक से दिखाया या छिपाया गया है।

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