【问题标题】:UserControl exposing multiple content properties! How exciting that would be!UserControl 暴露多个内容属性!那将是多么令人兴奋!
【发布时间】:2010-01-14 22:49:19
【问题描述】:

我正在尝试创建一个希望能够公开多个内容属性的 UserControl。但是,我被失败缠身!

我们的想法是创建一个出色的用户控件(我们将其称为 MultiContent),它公开两个内容属性,以便我可以执行以下操作:

    <local:MultiContent>
        <local:MultiContent.ListContent>
            <ListBox x:Name="lstListOfStuff" Width="50" Height="50" />                
        </local:MultiContent.ListContent>
        <local:MultiContent.ItemContent>
            <TextBox x:Name="txtItemName" Width="50" />
        </local:MultiContent.ItemContent>
    </local:MultiContent>

这将非常有用,现在我可以根据情况更改 ListContent 和 ItemContent,并将通用功能分解到 MultiContent 用户控件中。

但是,按照我目前的实现方式,我无法访问 MultiContent 控件的这些内容属性内的 UI 元素。例如,当我尝试访问 lstListOfStufftxtItemName 时,它们都是 null

public MainPage() {
    InitializeComponent();
    this.txtItemName.Text = "Item 1"; // <-- txtItemName is null, so this throws an exception
}

这是我实现 MultiContent 用户控件的方式:

XAML:MultiContent.xaml

<UserControl x:Class="Example.MultiContent"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <ContentControl x:Name="pnlList" Grid.Column="0" />
        <ContentControl x:Name="pnlItem" Grid.Column="1" />
    </Grid>
</UserControl>

代码隐藏:MultiContent.xaml.cs

// Namespaces Removed
namespace Example
{
    public partial class MultiContent : UserControl
    {
        public UIElement ListContent
        {
            get { return (UIElement)GetValue(ListContentProperty); }
            set 
            {
                this.pnlList.Content = value;
                SetValue(ListContentProperty, value); 
            }
        }
        public static readonly DependencyProperty ListContentProperty =
            DependencyProperty.Register("ListContent", typeof(UIElement), typeof(MultiContent), new PropertyMetadata(null));

        public UIElement ItemContent
        {
            get { return (UIElement)GetValue(ItemContentProperty); }
            set 
            {
                this.pnlItem.Content = value;
                SetValue(ItemContentProperty, value); 
            }
        }
        public static readonly DependencyProperty ItemContentProperty =
            DependencyProperty.Register("ItemContent", typeof(UIElement), typeof(MultiContent), new PropertyMetadata(null));


        public MultiContent()
        {
            InitializeComponent();
        }
    }
}

我可能完全错误地实现了这一点。有谁知道我怎样才能让它正常工作?如何从父控件按名称访问这些 UI 元素?关于如何更好地做到这一点的任何建议?谢谢!

【问题讨论】:

    标签: silverlight xaml user-controls


    【解决方案1】:

    你绝对可以实现你的目标,但你需要采取不同的方法。

    在您的解决方案中,您尝试为 UIElement 设置依赖项属性 - 因为它永远不会被设置并且默认值为 null,这就是您获得 NullReference 异常的原因。您可能可以通过将默认值从 null 更改为 new TextBox 或类似的方式继续操作,但即使它确实有效,它仍然会让人感觉像是一个 hack。

    在 Silverlight 中,您必须自己实现依赖属性。但是,您还没有按应有的方式实现它们-我倾向于使用dependency property generator 来实现它们。

    DP 的一大优点是它们支持更改通知。因此,考虑到这一点,您需要做的就是为 DP 定义:ItemContent 和 ListContent 与 Content(对象)具有相同的类型,并且当框架通知您它们中的任何一个已更改时,只需更新您的文本框!所以这里是执行此操作的代码:

    MultiContent.xaml:

       <Grid x:Name="LayoutRoot" Background="White">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100" />
                <ColumnDefinition Width="100" />
            </Grid.ColumnDefinitions>
            <ContentControl x:Name="pnlList" Grid.Column="0" />
            <ContentControl x:Name="pnlItem" Grid.Column="1" />
        </Grid>    
    

    MultiContent.xaml.cs:

    namespace MultiContent
    {
        public partial class MultiContent : UserControl
        {
            #region ListContent
    
            /// <summary>
            /// ListContent Dependency Property
            /// </summary>
            public object ListContent
            {
                get { return (object)GetValue(ListContentProperty); }
                set { SetValue(ListContentProperty, value); }
            }
            /// <summary>
            /// Identifies the ListContent Dependency Property.
            /// </summary>
            public static readonly DependencyProperty ListContentProperty =
                DependencyProperty.Register("ListContent", typeof(object),
                typeof(MultiContent), new PropertyMetadata(null, OnListContentPropertyChanged));
    
            private static void OnListContentPropertyChanged
              (object sender, DependencyPropertyChangedEventArgs e)
            {
                MultiContent m = sender as MultiContent;
                m.OnPropertyChanged("ListContent");
            }
    
            #endregion
    
            #region ItemContent
    
            /// <summary>
            /// ItemContent Dependency Property
            /// </summary>
            public object ItemContent
            {
                get { return (object)GetValue(ItemContentProperty); }
                set { SetValue(ItemContentProperty, value); }
            }
            /// <summary>
            /// Identifies the ItemContent Dependency Property.
            /// </summary>
            public static readonly DependencyProperty ItemContentProperty =
                DependencyProperty.Register("ItemContent", typeof(object),
                typeof(MultiContent), new PropertyMetadata(null, OnItemContentPropertyChanged));
    
            private static void OnItemContentPropertyChanged
              (object sender, DependencyPropertyChangedEventArgs e)
            {
                MultiContent m = sender as MultiContent;
                m.OnPropertyChanged("ItemContent");
            }
    
            #endregion
    
            /// <summary>
            ///  Event called when any chart property changes
            ///  Note that this property is not used in the example but is good to have if you plan to extend the class!
            /// </summary>
            public event PropertyChangedEventHandler PropertyChanged;
    
            /// <summary>
            ///  Called to invoke the property changed event
            /// </summary>
            /// <param name="propertyName">The property that has changed</param>
            protected void OnPropertyChanged(string propertyName)
            {
                if (propertyName == "ListContent")
                {
                    // The ListContent property has been changed, let's update the control!
                    this.pnlList.Content = this.ListContent;
                }
                if (propertyName == "ItemContent")
                {
                    // The ListContent property has been changed, let's update the control!
                    this.pnlItem.Content = this.ItemContent;
                }
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
    
            public MultiContent()
            {
                InitializeComponent();
            }
        }
    }
    

    MainPage.xaml:

        <Grid x:Name="LayoutRoot" Background="White">
            <local:MultiContent>
                <local:MultiContent.ListContent>
                    <ListBox x:Name="lstListOfStuff" Width="50" Height="50" />
                </local:MultiContent.ListContent>
                <local:MultiContent.ItemContent>
                    <TextBox x:Name="txtItemName" Width="50" />
                </local:MultiContent.ItemContent>
            </local:MultiContent>
        </Grid>
    

    这应该可以解决问题!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-26
      • 2019-02-17
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 2021-06-26
      • 1970-01-01
      相关资源
      最近更新 更多