Как передать свойства Silverlight usercontrol родительскому элементу? - PullRequest
0 голосов
/ 29 марта 2011

Мне бы хотелось, чтобы некоторые свойства пользовательского элемента управления были доступны для родительской страницы. Я создал небольшой образец, чтобы проиллюстрировать то, что я ищу.

Я пытаюсь использовать шаблон MVVM и все механизмы привязки для его достижения.

USERCONTROL XAML

<UserControl x:Class="TestCustomUserControl.MyControls.UserNameControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestCustomUserControl.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
    <local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
<Grid x:Name="NameCtrlRoot" Background="White" DataContext="{StaticResource TheViewModel}">
    <StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:" />
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name: "/>
            <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtFirstName" Text="{Binding FirstName, Mode=TwoWay}">
                <i:Interaction.Behaviors>
                    <!-- This behavior updates the binding after a specified delay
                                instead of the user having to lose focus on the TextBox. -->
                    <local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
                </i:Interaction.Behaviors>
            </TextBox>
            <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtLastName" Text="{Binding LastName, Mode=TwoWay}">
                <i:Interaction.Behaviors>
                    <!-- This behavior updates the binding after a specified delay
                                instead of the user having to lose focus on the TextBox. -->
                    <local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
                </i:Interaction.Behaviors>
            </TextBox>
            <TextBlock Grid.Row="3" Grid.Column="0" Text="fullname inside control:" />
            <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
            <Border Height="1" Background="Black" Grid.Column="2" Grid.Row="4" />
        </Grid>
    </StackPanel>
</Grid>

вышеуказанный пользовательский контроль привязан к следующему VIEWMODEL

public class BaseViewModel : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;


    protected void NotifyPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

public class UserNameViewModel : BaseViewModel
{
    private String _firstName;
    public String FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            NotifyPropertyChanged("FirstName");
            OnNameChange();
        }
    }

    private String _lastName;
    public String LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            NotifyPropertyChanged("LastName");
            OnNameChange();
        }
    }

    private void OnNameChange()
    {
        FullName = String.Format("{0} {1}", FirstName, LastName);
    }

    public String _fullName;
    public String FullName
    {
        get { return _fullName; }
        set { 
            _fullName = value;
            NotifyPropertyChanged("FullName");
        }
    }
}

Страница потребителя, которая использует вышеуказанный USERCONTROL

<navigation:Page xmlns:my="clr-namespace:TestCustomUserControl.MyControls"  x:Class="TestCustomUserControl.Views.ConsumeName" 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       mc:Ignorable="d"
       xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
       d:DesignWidth="640" d:DesignHeight="480"
       Title="ConsumeName Page">
<Grid x:Name="LayoutRoot">
    <StackPanel>
        <my:UserNameControl x:Name="MyNameControl"/>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="10" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Full Name in Parent: " />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding FullName, ElementName=MyNameControl}"/>
        </Grid>
    </StackPanel>
</Grid>

Вот мой вопрос сейчас. Если вы посмотрите на модель представления, связанную с пользовательским управлением, у нее есть свойство FullName, и я бы хотел, чтобы оно было открыто через Usercontrol, чтобы я мог получить к нему доступ со страницы потребления. Это как потребляющая страница хочет получить доступ к некоторым свойствам usercontrol. Я не совсем уверен, как этого добиться. Я хотел бы придерживаться модели MVVM.

Ответы [ 2 ]

0 голосов
/ 21 апреля 2011

Я отвечаю на свой вопрос.Но прежде чем ответить на мой вопрос, просто хочу кое-что прояснить.Я пытался создать полностью инкапсулированный UserControl с его собственной ViewModel.И где бы ни использовался пользовательский контроль, потребитель не должен ничего знать о внутренней модели представления пользовательского контроля.Единственная опция связи, которую я хочу, чтобы потребитель имел, - это установка некоторых свойств и использование механизма привязки.Таким образом, я решил свою проблему так: я создал свойство зависимостей внутри UserControl и устанавливал его всякий раз, когда что-то меняется в viewmodel usercontrol.

UserNameControl.xaml (usercontrol)

<UserControl x:Class="TestCustomUserControl.MyControls.UserNameControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestCustomUserControl.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
    <local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
<Grid x:Name="NameCtrlRoot" Background="White" DataContext="{StaticResource TheViewModel}">
    <StackPanel>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:" />
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name: "/>
            <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtFirstName" Text="{Binding FirstName, Mode=TwoWay}">
                <i:Interaction.Behaviors>
                    <!-- This behavior updates the binding after a specified delay
                                instead of the user having to lose focus on the TextBox. -->
                    <local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
                </i:Interaction.Behaviors>
            </TextBox>
            <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtLastName" Text="{Binding LastName, Mode=TwoWay}">
                <i:Interaction.Behaviors>
                    <!-- This behavior updates the binding after a specified delay
                                instead of the user having to lose focus on the TextBox. -->
                    <local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
                </i:Interaction.Behaviors>
            </TextBox>
            <TextBlock Grid.Row="3" Grid.Column="0" Text="fullname inside control:" />
            <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
            <Border Height="1" Background="Black" Grid.Column="2" Grid.Row="4" />
        </Grid>
    </StackPanel>
</Grid>

BaseViewModel.cs

public class BaseViewModel : INotifyPropertyChanged
{
    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    protected void NotifyPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

UserNameViewModel.cs

 public class UserNameViewModel : BaseViewModel
{
    private String _firstName;
    public String FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            NotifyPropertyChanged("FirstName");
            OnNameChange();
        }
    }

    private String _lastName;
    public String LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            NotifyPropertyChanged("LastName");
            OnNameChange();
        }
    }

    public Action NameChanged { get; set; }
    private void OnNameChange()
    {
        FullName = String.Format("{0} {1}", FirstName, LastName);
        if(NameChanged != null) NameChanged.Invoke();
    }

    private String _fullName;
    public String FullName
    {
        get { return _fullName; }
        set { 
            _fullName = value;
            NotifyPropertyChanged("FullName");
        }
    }
}

UserNameControl.xaml.cs (код контроля пользователя с декларацией DependencyProperty)

public partial class UserNameControl : UserControl
{
    private UserNameViewModel _TheViewModel;
    public UserNameControl()
    {
        InitializeComponent();
        _TheViewModel = Resources["TheViewModel"] as UserNameViewModel;
        _TheViewModel.NameChanged = OnNameChanged;
    }

    public String SelectedFullName
    {
        get { return (String) GetValue(SelectedFullNameProperty); }
        set { SetValue(SelectedFullNameProperty, value); }
    }
    public static readonly DependencyProperty SelectedFullNameProperty =
        DependencyProperty.Register("SelectedFullName", typeof (String), typeof (UserNameControl), null);

    private void OnNameChanged()
    {
        SelectedFullName = _TheViewModel.FullName;
    }
}

ConsumeName.xaml (потребительская страница навигации, пользователь выше контроля пользователя и перетаскивает SelectedFullName в UserFullName)

<navigation:Page xmlns:my="clr-namespace:TestCustomUserControl.MyControls"  x:Class="TestCustomUserControl.Views.ConsumeName" 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       mc:Ignorable="d"
       xmlns:local="clr-namespace:TestCustomUserControl.ViewModels"
       xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
       d:DesignWidth="640" d:DesignHeight="480"
       Title="ConsumeName Page">
<navigation:Page.Resources>
    <local:ConsumeNameViewModel x:Key="TheConsumeNameViewModel"/>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot" DataContext="{StaticResource TheConsumeNameViewModel}">
    <StackPanel>
        <my:UserNameControl x:Name="MyNameControl" SelectedFullName="{Binding UserFullName, Mode=TwoWay}" />
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="10" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Full Name in Parent: " />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding UserFullName}"/>
        </Grid>
    </StackPanel>
</Grid>

ConsumeNameViewModel.cs

public class ConsumeNameViewModel : BaseViewModel
{

    private string _UserFullName;

    public string UserFullName
    {
        get { return _UserFullName; }
        set
        {
            if (value != _UserFullName)
            {
                _UserFullName = value;
                NotifyPropertyChanged("UserFullName");
            }
        }
    }

}
0 голосов
/ 29 марта 2011

вы уже удалили StaticResource, поэтому вы можете использовать его в обоих представлениях.Сделайте это

<UserControl.Resources>
    <local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>

на «странице имени потребителя».Если вы просто добавляете DataContext в свою таблицу

<Grid x:Name="LayoutRoot" DataContext="{StaticResource TheViewModel}">

, это должно сработать.(Вам больше не нужен ElementName = MyNameControl).

my: UserNameControl должен наследовать DataContext.Если нет, вы должны добавить его здесь.

<my:UserNameControl DataContext="{StaticResource TheViewModel}"/>

Это должно работать.

Прямо сейчас локально: UserNameViewModel с ключом TheViewModel достижимо только там, где вы его определили.Если вы определите его в своем app.xaml, вы сможете получить к нему доступ из любого места в приложении.

Надеюсь, это поможет.

BR,

TJ

...