在UserControl内部绑定到另一个UserControl的数据绑定

3
在我的项目中,我需要将数据绑定到另一个用户控件中的用户控件。为了简洁起见,我创建了一个概念上类似但非常简单的项目。
想象一下,我正在创建一个电话簿应用程序,其中包含两个用户控件,看起来像下面这样。

enter image description here

窗口中的大蓝色框将是一个UserControl,它显示所有者的姓名(Jane Doe),其中每个黄色框也是一个UserControl,它们显示联系人的姓名和电话号码。
我有两个类来保存相关数据,如下所示:
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 用户控件 (蓝色框):

在这里,我使用 ItemsControl 动态绑定 UCPerson 用户控件,以便在运行时添加任意数量的用户控件。

<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。我对数据绑定还不太熟悉,似乎无法找出正确的方法。
这里我做错了什么?

威尔,我有点困惑,因为那篇文章是我来这里时一直在跟进的。我想我需要回去从头开始学习数据绑定。 - Sach
1
@Will,谢谢!我会查看一下。你们非常有帮助。 - Sach
2
@EdPlunkett 哎呀,糟糕,你说得对。删除混淆的注释以防止混淆,而不是因为尴尬,我保证。 - user1228
1
@Will 有哪些令人困惑的评论?我什么都没看到。 - 15ee8f99-57ff-4f92-890c-b56153
1个回答

6

这个过程可以被大大简化。

首先,要去掉你的UserControl中每一个CanvasCanvas不仅仅是一个中立的面板/容器控制Canvas会导致所有内容重叠在一起。只有在想要任意定位和潜在重叠子项时才使用Canvas。 WPF布局通常使用"流动"和相对位置。标准布局父元素是StackPanel, Grid, WrapPanel和偶尔的UniformGrid。你可以省略ItemsControlItemsPanelTemplate,因为默认值已经是竖直方向的StackPanel


1
@Will 这是数据上下文。 - 15ee8f99-57ff-4f92-890c-b56153
1
@Sach,很遗憾你选择的那个模型并不理想。公正地说,它已经有六年的历史了。LayoutRoot.DataContext = this; 这个方法比备受诟病的 DataContext = this; 好多了(我认为有些人误以为你在使用后者),但是这种做法在现今并不被视为良好的实践。 - 15ee8f99-57ff-4f92-890c-b56153
1
@Sach,像我上面所示的那样,为UC提供视图模型非常有效,但有些情况下,您可能还想添加一两个依赖属性,仅用于纯粹的可视化内容:例如,父视图可能希望传递一些画笔或样式以应用于UC的某些部分。因此,您可以为这些内容编写依赖属性,并在UC XAML内绑定,如{Binding BlahStyleProp,RelativeSource={RelativeSource AncestorType=UserControl}}。UC上的DP并不是坏事 - 只是对您正在做的事情没有必要。 - 15ee8f99-57ff-4f92-890c-b56153
我已删除了ItemsPanel,但结果仍然相同。用户控件似乎像屏幕截图中一样堆叠在一起。 - Sach
1
@Sach 我懂了!就是那些画布(Canvases)!把所有的画布都去掉。它们会对布局产生奇怪的影响,不仅仅是一个中性的面板控件。 - 15ee8f99-57ff-4f92-890c-b56153
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接