DataBinding到UserControl内的UserControl

时间:2018-01-03 19:07:00

标签: c# wpf xaml data-binding user-controls

在我的项目中,我需要DataBind到驻留在另一个UserControl中的UserControl。为了简洁起见,我创建了一个概念相似但非常简单的项目。

想象一下,我正在创建一个电话簿应用程序,其中包含两个用户控件,如下所示。

enter image description here

窗口中的大蓝框是一个UserControl,它显示所有者的名字(Jane Doe),其中的每个黄色框也是UserControls,它显示联系人'姓名和电话号码。

我有两个类,我持有相关数据,如下所示:

public class Person
{
    public string Name { get; set; }
    public string Phone { get; set; }
    public Person() { }
}

public class PhoneBook
{
    public string OwnerName { get; set; }
    public ObservableCollection<Person> ContactList { get; set; }
    public PhoneBook() { }
}

在我的MainWindow中,我使用ViewModel并绑定到PhoneBook UserControl,如下所示:

<Window x:Class="UserControlDataBinding.MainWindow"
        Title="MainWindow" Height="300" Width="350"
        DataContext="{Binding Source={StaticResource mainViewModelLocator},Path=ViewModelPhoneBook}">
    <Grid>
        <local:UCPhoneBook x:Name="ucPhoneBook" MainPhoneBook="{Binding PhoneBookData}"></local:UCPhoneBook>
    </Grid>
</Window>

PhoneBookData是ViewModel上的PhoneBook类的实例。

我的两个用户控件,他们的DependancyProperties如下所示。

UCPhoneBook UserControl(蓝框):

我在这里使用ItemsControl动态绑定UCPerson UserControls,这样我就可以在运行时添加尽可能多的内容。

<UserControl x:Class="UserControlDataBinding.UCPhoneBook"
             d:DesignHeight="300" d:DesignWidth="450">
    <Canvas x:Name="ucCanvasPhoneBook" Background="White">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <GroupBox Grid.Row="0" Header="Phonebook">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Label Grid.Row="0" Name="lblOwnerName" 
                           Content="{Binding Path=MainPhoneBook.OwnerName}">
                    </Label>
                </Grid>
            </GroupBox>

            <ItemsControl Grid.Row="1"
                          ItemsSource="{Binding PhoneBookData.ContactList}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:Person}">
                        <local:UCPerson PersonData="{Binding Person}"></local:UCPerson>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Vertical"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </Grid>
    </Canvas>
</UserControl>

DependancyProperty

public partial class UCPhoneBook : UserControl
{
    private static readonly DependencyProperty PhoneBookProperty = DependencyProperty.Register("MainPhoneBook", typeof(PhoneBook), typeof(UCPhoneBook), new PropertyMetadata(null));
    public PhoneBook MainPhoneBook
    {
        get { return (PhoneBook)GetValue(PhoneBookProperty); }
        set { SetValue(PhoneBookProperty, value); }
    }

    public UCPhoneBook()
    {
        InitializeComponent();
        ucCanvasPhoneBook.DataContext = this;
    }
}

UCPerson UserControl(黄色方框):

<UserControl x:Class="UserControlDataBinding.UCPerson"
             d:DesignHeight="26" d:DesignWidth="400">
    <Canvas x:Name="ucCanvasPerson" Background="WhiteSmoke">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0" Name="lblName" 
                   HorizontalAlignment="Left" VerticalAlignment="Center"
                   Content="{Binding Name}"></Label>
            <Label Grid.Column="2" Name="lblPhone" 
                   HorizontalAlignment="Right" VerticalAlignment="Center"
                   Content="{Binding Phone}"></Label>
        </Grid>
    </Canvas>
</UserControl>

DependancyProperty

public partial class UCPerson : UserControl
{
    private static readonly DependencyProperty PersonProperty = DependencyProperty.Register("PersonData", typeof(Person), typeof(UCPerson), new PropertyMetadata(null));
    public Person PersonData
    {
        get { return (Person)GetValue(PersonProperty); }
        set { SetValue(PersonProperty, value); }
    }

    public UCPerson()
    {
        InitializeComponent();
        ucCanvasPerson.DataContext = this;
    }
}

当我运行它时,我可以看到第一个UserControl(蓝色框)顶部的所有者名称就好了。但是,它似乎没有正确绑定UCPerson用户控件,我得到一个空列表,如下所示:

enter image description here

我的猜测是我没有正确绑定到第一个UserControl中的ItemsControl。我对DataBinding很陌生,似乎无法弄清楚正确的方法是什么。

我在这里做错了什么?

1 个答案:

答案 0 :(得分:4)

这一切都可以大大简化。

首先,摆脱UserControls中的每个Canvas。 Canvas isn't just a neutral panel/container control。画布将使一切都叠加。只有在想要任意定位儿童并且可能叠加时才使用画布。 WPF布局通常使用“流程”和相对定位。标准布局父级是StackPanel,Grid,WrapPanel和偶尔的UniformGrid。您可以省略ItemsControl的ItemsPanelTemplate,因为默认值已经是垂直方向的StackPanel。

首先修复UCPerson.xaml.cs:

public partial class UCPerson : UserControl
{
    public UCPerson()
    {
        InitializeComponent();
    }
}

然后修复datatemplate:

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:UCPerson />
                </DataTemplate>
            </ItemsControl.ItemTemplate>

usercontrol的DataContext应该是它显示的viewmodel。 ItemsControl中的每个项目都是Person。在ItemsControl的ItemTemplate中,DataContext是项目 - Person。 DataTemplate中的UserControl将继承该DataContext。

无需设置DataTemplate的DataType。这不是在这里使用的。

当我们在这里时,其他用户控件将受益于相同的处理:

public partial class UCPhoneBook : UserControl
{
    public UCPhoneBook()
    {
        InitializeComponent();
    }
}

XAML(部分)

                <Label Grid.Row="0" Name="lblOwnerName" 
                       Content="{Binding OwnerName}">
                </Label>
            </Grid>
        </GroupBox>

        <ItemsControl Grid.Row="1"
                      ItemsSource="{Binding ContactList}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:UCPerson />
                </DataTemplate>
            </ItemsControl.ItemTemplate>

MainWindow.xaml

<Window x:Class="UserControlDataBinding.MainWindow"
        Title="MainWindow" Height="300" Width="350"
        DataContext="{Binding Source={StaticResource mainViewModelLocator},Path=ViewModelPhoneBook}">
    <Grid>
        <local:UCPhoneBook DataContext="{Binding PhoneBookData}" />
    </Grid>
</Window>

顺便说一句,你在哪里得到这个同一财产有两个名字的概念?