.net - मैं रिलेवेटसोर्स के साथ डब्ल्यूपीएफ बाइंडिंग का उपयोग कैसे करूं?




wpf xaml (10)

मैं WPF बाइंडिंग के साथ RelativeSource स्रोत का उपयोग कैसे करूं और विभिन्न उपयोग-मामले क्या हैं?


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

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

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

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

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

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

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

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


एमवीवीएम आर्किटेक्चर के संदर्भ में यहां एक और दृश्य स्पष्टीकरण दिया गया है:


टेम्पलेटेड माता-पिता को मत भूलना:

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

या

{Binding RelativeSource={RelativeSource TemplatedParent}}

बेचिर बेजाउई ने यहां अपने लेख में डब्ल्यूपीएफ में संबंधित स्रोतों के उपयोग के मामलों का खुलासा किया:

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

  1. मोड स्व:

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

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

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

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

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

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

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

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

  1. मोड FindAncestor

इस मामले में, किसी दिए गए तत्व की एक संपत्ति अपने माता-पिता, कोर के साथ बंधी जाएगी। उपरोक्त मामले के साथ मुख्य अंतर यह तथ्य है कि, संपत्ति को बांधने के लिए पदानुक्रम में पूर्वजों के प्रकार और पूर्वज पद को निर्धारित करने के लिए आप पर निर्भर है। वैसे 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>

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

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

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

  1. TemplatedParent

यह मोड नियंत्रण नियंत्रक प्रॉपर्टी को उस नियंत्रण की एक संपत्ति से जोड़ता है जो 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>

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


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


मैंने हर जवाब नहीं पढ़ा लेकिन मैं सिर्फ एक बटन के सापेक्ष स्रोत कमांड बाइंडिंग के मामले में यह जानकारी जोड़ना चाहता हूं। जब आप Mode=FindAncestor साथ सापेक्ष स्रोत का उपयोग करते हैं, तो बाध्यकारी इस प्रकार होना चाहिए:

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

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


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

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

उनके पास [डब्लूपीएफ] के तहत अपने ब्लॉग पर समाधान है जब डेटाकॉन्टेक्स्ट विरासत में नहीं मिला है, तो डेटा से कैसे जुड़ें । और यह बिल्कुल शानदार काम करता है!

संभवतः उस घटना में जहां उसका ब्लॉग नीचे है, परिशिष्ट ए में उनके लेख की दर्पण प्रति है।

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

परिशिष्ट ए: ब्लॉग पोस्ट का मिरर

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

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

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

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

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

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

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

<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}"/>

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

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

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

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

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.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

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

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

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

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


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

<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>

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

1. मोड: यह एक enum कि चार मूल्य हो सकता है:

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

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

पूर्व। ControlTemplate परिभाषित करें

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

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

पूर्व। checkbox पर Command सेट करते समय Command CommandParameter रूप में checkbox की checkbox स्थिति भेजें

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

घ। FindAncestor ( value=3 ): जब Visual Tree में अभिभावक control से बाध्य होना चाहते हैं।

पूर्व। अगर grid , यदि header checkbox है तो records में 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 : जब मोड FindAncestor है FindAncestor तो फिर किस प्रकार का एन्सेक्टर (यदि visual tree में दो समान प्रकार के माता-पिता हैं)

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

RelativeSource binding लिए सभी उपयोग-मामले हैं।

यहां एक रेफरी लिंक है


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

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

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

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

  • स्वयं उस तत्व को संदर्भित करता है जिस पर आप बाध्यकारी सेट कर रहे हैं और आपको उस तत्व की एक संपत्ति को उसी तत्व पर किसी अन्य प्रॉपर्टी से बांधने की अनुमति देता है।

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








relativesource