NAMED सामग्री के साथ WPF UserControl कैसे बनाएं


100

मेरे पास संलग्न कमांड और तर्क के साथ नियंत्रण का एक सेट है जो लगातार उसी तरह से पुन: उपयोग किया जाता है। मैंने एक उपयोगकर्ता नियंत्रण बनाने का फैसला किया जो सभी सामान्य नियंत्रण और तर्क रखता है।

हालाँकि मुझे कंटेंट को होल्ड करने में सक्षम होने के लिए नियंत्रण की भी आवश्यकता है। मैंने निम्नलिखित कोशिश की:

<UserControl.ContentTemplate>
    <DataTemplate>
        <Button>a reused button</Button>
        <ContentPresenter Content="{TemplateBinding Content}"/>
        <Button>a reused button</Button>
    </DataTemplate>
</UserControl.ContentTemplate>

हालाँकि ऐसा लगता है कि उपयोगकर्ता नियंत्रण के अंदर रखी गई किसी भी सामग्री का नाम नहीं दिया जा सकता है। उदाहरण के लिए यदि मैं निम्नलिखित तरीके से नियंत्रण का उपयोग करता हूं:

<lib:UserControl1>
     <Button Name="buttonName">content</Button>
</lib:UserControl1>

मुझे निम्न त्रुटि प्राप्त हुई:

तत्व 'बटन' पर नाम विशेषता मान 'बटननाम' सेट नहीं कर सकता। 'बटन' तत्व 'यूजरकंट्रोल 1' के दायरे में है, जो पहले से ही एक नाम पंजीकृत था जब इसे दूसरे दायरे में परिभाषित किया गया था।

यदि मैं बटन को हटाता हूं, तो यह संकलित करता है, हालांकि मुझे सामग्री का नाम देने में सक्षम होना चाहिए। इसे कैसे प्राप्त किया जा सकता है?


यह एक संयोग है। मैं बस यह सवाल पूछने वाला था! मेरी भी यही समस्या है। UserControl में सामान्य UI पैटर्न को फैक्टर करना, लेकिन नाम से सामग्री UI का संदर्भ लेना चाहते हैं।
मैकेनिर

3
इस व्यक्ति को अपने कस्टम नियंत्रण की XAML फ़ाइल से छुटकारा पाने और कस्टम नियंत्रण के UI को प्रोग्रामेटिक रूप से बनाने से संबंधित एक समाधान मिला । इस ब्लॉग पोस्ट में इस विषय पर कहने के लिए और अधिक है।
मैकेनिर

2
आप रिसोर्सबॉडी तरीके का उपयोग क्यों नहीं करते? इसमें DataTemplate को परिभाषित करें। या नियंत्रण को इनहेरिट करने के लिए बेसऑन कीवर्ड का उपयोग करें। WPF में कोड-पीछे यूआई करने से पहले बस कुछ रास्तों का पालन
करूंगा

जवाबों:


46

इसका उत्तर यह करने के लिए UserControl का उपयोग नहीं करना है।

एक वर्ग बनाएं जो ContentControl का विस्तार करता है

public class MyFunkyControl : ContentControl
{
    public static readonly DependencyProperty HeadingProperty =
        DependencyProperty.Register("Heading", typeof(string),
        typeof(HeadingContainer), new PropertyMetadata(HeadingChanged));

    private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((HeadingContainer) d).Heading = e.NewValue as string;
    }

    public string Heading { get; set; }
}

फिर सामग्री निर्दिष्ट करने के लिए एक शैली का उपयोग करें

<Style TargetType="control:MyFunkyControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="control:MyFunkyContainer">
                <Grid>
                    <ContentControl Content="{TemplateBinding Content}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

और अंत में - इसका उपयोग करें

<control:MyFunkyControl Heading="Some heading!">            
    <Label Name="WithAName">Some cool content</Label>
</control:MyFunkyControl>

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

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

8
@greenoldman @Badiboy मुझे लगता है कि मुझे पता है कि यह आपके लिए काम क्यों नहीं किया। आपने शायद एक मौजूदा कोड UserControlको विरासत से बदल दिया है ContentControl। हल करने के लिए, बस नई कक्षा जोड़ें ( CS के साथ XAML नहीं )। और फिर यह (उम्मीद है) काम करेगा। अगर आपको पसंद है, तो मैंने एक छोटा VS2010 समाधान
13

1
कई सालों बाद और मैं चाहता हूं कि मैं इसे फिर से उखाड़ सकूं :)
ड्रू नोक्स

2
मुझे लगता है HeadingContainerऔर होने MyFunkyContainerका मतलब है MyFunkyControl?
मार्टिन श्नाइडर

20

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

मैकडायर के रूप में जेडी के ब्लॉग पर समाधान , लगता है कि सबसे अच्छा समझौता है। XDL में नियंत्रण को अभी भी परिभाषित करने की अनुमति देने के लिए JD के समाधान का विस्तार करने का एक तरीका इस प्रकार हो सकता है:

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);

        var grid = new Grid();
        var content = new ContentPresenter
                          {
                              Content = Content
                          };

        var userControl = new UserControlDefinedInXAML();
        userControl.aStackPanel.Children.Add(content);

        grid.Children.Add(userControl);
        Content = grid;           
    }

ऊपर के मेरे उदाहरण में मैंने UserControlDefinedInXAML नामक एक उपयोगकर्ता नियंत्रण बनाया है जो XAML का उपयोग करके किसी भी सामान्य उपयोगकर्ता नियंत्रण की तरह परिभाषित किया गया है। मेरे UserControlDefinedInXAML में मेरे पास एक StackPanel है जिसे aStackPanel कहा जाता है जिसके भीतर मैं चाहता हूं कि मेरी नामित सामग्री दिखाई दे।


मुझे पता चला है कि सामग्री के पुन: पालन के इस तंत्र का उपयोग करते समय मुझे डेटा बाइंडिंग समस्याएं मिलती हैं। डेटा-बाइंडिंग सही ढंग से सेट किया गया लगता है, लेकिन डेटा स्रोत से नियंत्रणों की प्रारंभिक आबादी ठीक से काम नहीं करती है। मुझे लगता है कि समस्या उन नियंत्रणों तक सीमित है जो सामग्री प्रस्तुतकर्ता के सीधे बच्चे नहीं हैं।
मैकेनिर

के बाद से इस के साथ प्रयोग करने का मौका नहीं था, लेकिन मुझे UserControlDefinedInXAML (उपर्युक्त उदाहरण से) या कंटेंटप्रेशर में अब तक जोड़े गए नियंत्रणों को परिभाषित करने में कोई समस्या नहीं है। मुझे केवल कोड के माध्यम से डेटाबाइंडिंग किया गया है (XAML नहीं - निश्चित नहीं कि अगर इससे कोई फर्क पड़ता है)।
रयान

ऐसा लगता है कि यह एक फर्क पड़ता है। मैंने अभी आपके द्वारा वर्णित मामले में अपने डेटा बाइंडिंग के लिए XAML का उपयोग करने की कोशिश की और यह काम नहीं करता है। लेकिन अगर मैं इसे कोड में सेट करता हूं, तो यह काम करता है!
रयान

3

एक और विकल्प जो मैंने उपयोग किया है वह है Nameसंपत्ति को बस में सेट करनाLoaded घटना ।

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

private void Button_Loaded(object sender, RoutedEventArgs e)
{
    Button b = sender as Button;
    b.Name = "buttonName";
}

2
यदि आप ऐसा करते हैं, तो नाम का उपयोग करने वाले बाइंडिंग काम नहीं करेंगे ... जब तक आप कोड में बाइंडिंग सेट नहीं करते।
टॉवर

3

कभी-कभी आपको सी # से तत्व को संदर्भित करने की आवश्यकता हो सकती है। उपयोग के मामले के आधार पर, आप x:Uidइसके बजाय एक सेट कर सकते हैं x:Nameऔर तत्वों को एक्सेस कर सकते हैं जैसे कि यूआईडी डुप्लीकेट में यूआईडी द्वारा पाएं ऑब्जेक्ट जैसे यूआईडी फाइंडर विधि ।


1

आप उपयोगकर्ता नियंत्रण के अंदर सेट नाम के लिए इस सहायक का उपयोग कर सकते हैं:

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
namespace UI.Helpers
{
    public class UserControlNameHelper
    {
        public static string GetName(DependencyObject d)
        {
            return (string)d.GetValue(UserControlNameHelper.NameProperty);
        }

        public static void SetName(DependencyObject d, string val)
        {
            d.SetValue(UserControlNameHelper.NameProperty, val);
        }

        public static readonly DependencyProperty NameProperty =
            DependencyProperty.RegisterAttached("Name",
                typeof(string),
                typeof(UserControlNameHelper),
                new FrameworkPropertyMetadata("",
                    FrameworkPropertyMetadataOptions.None,
                    (d, e) =>
                    {
                        if (!string.IsNullOrEmpty((string)e.NewValue))
                        {
                            string[] names = e.NewValue.ToString().Split(new char[] { ',' });

                            if (d is FrameworkElement)
                            {
                                ((FrameworkElement)d).Name = names[0];
                                Type t = Type.GetType(names[1]);
                                if (t == null)
                                    return;
                                var parent = FindVisualParent(d, t);
                                if (parent == null)
                                    return;
                                var p = parent.GetType().GetProperty(names[0], BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);
                                p.SetValue(parent, d, null);
                            }
                        }
                    }));

        public static DependencyObject FindVisualParent(DependencyObject child, Type t)
        {
            // get parent item
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            // we’ve reached the end of the tree
            if (parentObject == null)
            {
                var p = ((FrameworkElement)child).Parent;
                if (p == null)
                    return null;
                parentObject = p;
            }

            // check if the parent matches the type we’re looking for
            DependencyObject parent = parentObject.GetType() == t ? parentObject : null;
            if (parent != null)
            {
                return parent;
            }
            else
            {
                // use recursion to proceed with next level
                return FindVisualParent(parentObject, t);
            }
        }
    }
}

और आपके विंडो या कंट्रोल कोड के पीछे आपको संपत्ति द्वारा नियंत्रण सेट करना:

 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

    }

    public Button BtnOK { get; set; }
}

आपकी खिड़की xaml:

    <Window x:Class="user_Control_Name.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:test="clr-namespace:user_Control_Name"
            xmlns:helper="clr-namespace:UI.Helpers" x:Name="mainWindow"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <test:TestUserControl>
                <Button helper:UserControlNameHelper.Name="BtnOK,user_Control_Name.MainWindow"/>
            </test:TestUserControl>
            <TextBlock Text="{Binding ElementName=mainWindow,Path=BtnOK.Name}"/>
        </Grid>
    </Window>

UserControlNameHelper प्रॉपर्टी पर नियंत्रण के लिए अपना नियंत्रण नाम और अपना वर्ग नाम प्राप्त करें।


0

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

    public FrameworkElement First
    {
        get
        {
            if (Controls.Count > 0)
            {
                return Controls[0];
            }
            return null;
        }
    }

यह मुझे XAML में बाल तत्वों तक पहुंचने में सक्षम बनाता है:

<TextBlock Text="{Binding First.SelectedItem, ElementName=Taxcode}"/>

0
<Popup>
    <TextBox Loaded="BlahTextBox_Loaded" />
</Popup>

कोड के पीछे:

public TextBox BlahTextBox { get; set; }
private void BlahTextBox_Loaded(object sender, RoutedEventArgs e)
{
    BlahTextBox = sender as TextBox;
}

Microsoft के लिए इस मुद्दे को ठीक करने के लिए वास्तविक समाधान होगा, साथ ही साथ अन्य सभी टूटे हुए दृश्य पेड़ों आदि के साथ हाइपोथेटिक रूप से बोलना होगा।


0

अभी तक एक और समाधान: तत्व को RelativeSource के रूप में देखें ।


0

नाम नियंत्रण का एक गुच्छा रखने में मुझे TabControl का उपयोग करने में एक ही समस्या थी।

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

TabItem नियंत्रण की सामग्री के रूप में, एक साधारण नियंत्रण का उपयोग करें और तदनुसार ControlTemplate सेट करें:

<Control Template="{StaticResource MyControlTemplate}"/>

आपके पीछे कोड से टेम्पलेट के अंदर उन नामित नाम तक पहुँचने के लिए दृश्य पेड़ का उपयोग करने की आवश्यकता होगी।

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