UserControl выставляя несколько свойств контента! Как это было бы интересно! - PullRequest
3 голосов
/ 15 января 2010

Я пытаюсь создать 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 . Например, 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();
        }
    }
}

Я, вероятно, реализую это совершенно неправильно. У кого-нибудь есть идеи, как я мог заставить это работать должным образом? Как я могу получить доступ к этим элементам интерфейса по имени из родительского элемента управления? Любые предложения о том, как сделать это лучше? Спасибо!

1 Ответ

3 голосов
/ 15 января 2010

Вы определенно можете достичь своей цели, но вам нужно выбрать другой подход.

В вашем решении вы пытаетесь получить свойство зависимости для UIElement - и поскольку оно никогда не устанавливается, а значение по умолчанию равно null, поэтому вы получаете исключение NullReference. Возможно, вы могли бы пойти дальше, изменив значение по умолчанию с нуля на новый TextBox или что-то в этом роде, но даже если бы это работало, это все равно было бы похоже на хак.

В Silverlight вы должны сами реализовать свойства зависимости. Однако вы не реализовали их должным образом - я обычно использую этот генератор свойств зависимости .

Одна из замечательных особенностей 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>

Это должно сработать!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...