UserControl公开多个内容属性!这将是多么令人兴奋!

时间:2010-01-14 22:49:19

标签: silverlight xaml user-controls

我正在尝试创建一个希望能够公开多个内容属性的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元素。例如,当我尝试访问时, lstListOfStuff txtItemName 都是 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元素?关于如何做得更好的任何建议?谢谢!

1 个答案:

答案 0 :(得分:3)

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

在您的解决方案中,您尝试为UIElement设置依赖项属性 - 并且因为它永远不会被设置并且默认值为null,这就是您获得NullReference异常的原因。您可以将默认值从null更改为新的TextBox或类似的东西,但即使它确实有效,它仍然会感觉像是黑客。

在Silverlight中,您必须自己实现Dpendency Properties。但是你没有实现它们 - 我倾向于使用这个dependency property generator来实现它们。

DP的一大优点是它们支持变更通知。因此,考虑到这一点,你需要做的就是让你的样本工作是定义为DPs: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>

这应该可以解决问题!