如何绑定自定义UserControl的数据

时间:2019-01-21 17:41:40

标签: c# .net wpf mvvm

所以我只是设置一个项目,并添加了一个自定义的UserControl,如下所示。

<Grid>
        <ItemsControl ItemsSource="{Binding UserViewModel.Users}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <controls:UserCard/>
                        <TextBlock Text="{Binding Name}"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

        </ItemsControl>
    </Grid>

如您所见,我尝试绑定Text属性,但它没有绑定。 现在,可能会有很多原因说明它的行为,所以我将尝试缩小范围。

我创建了一个BaseViewModel,它将保存我的ViewModel,看起来像这样。

public class BaseViewModel : ObservableObject
    {
        public UserViewModel UserViewModel { get; set; } = new UserViewModel();
    }

然后我像这样设置ViewModel

public class UserViewModel : ObservableObject
    {
        public ObservableCollection<User> Users { get; set; } = new ObservableCollection<User>();

        public UserViewModel()
        {
            Users.Add(new User{Name = "Riley"});
            Users.Add(new User{Name = "Riley1"});
        }
    }

简单,现在我有一个类似这样的ObservableObject并处理了INPC

public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

在我的MainView.xaml中 我已经像这样设置了DataContext

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new BaseViewModel();
    }
}

与UserControl完全相同

这是我实际添加UserControl的地方,以便它显示在MainWindow中

<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <controls:UserCard/>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

    </ItemsControl>

现在的问题是它不绑定数据,我想显示模型中的Name属性,但不显示它,并且我不确定为什么,如果我尝试将其绑定到直接在MainView中使用TextBlock属性,效果很好。

我不确定为什么会这样,我想理解为什么。 我需要利用DependencyProperties吗?还是我创建BaseViewModel的新实例的一种情况?我哪里出错了?

1 个答案:

答案 0 :(得分:1)

您的MainViewWindow包含一个带有绑定ItemsSource="{Binding UserViewModel.Users}"的ItemsControl,每个项目都显示一个<controls:UserCard/>。但是您的用户控件随后尝试使用"{Binding UserViewModel.Users}"再次绑定到列表。您为什么要在另一个列表中显示一个列表?

我怀疑这里的问题是您认为自定义UserControl的DataContext仍指向BaseViewModel,就像其父级一样。不是。 ItemsControl中每个项目的DataContext指向列表中它自己的关联元素,即类型为User的实例。

已更新:假设您有一个带有子视图模型列表的主视图模型,如下所示:

public class MainViewModel : ViewModelBase
{
    public MyChildViewModel[] MyItems { get; } =
    {
        new MyChildViewModel{MyCustomText = "Tom" },
        new MyChildViewModel{MyCustomText = "Dick" },
        new MyChildViewModel{MyCustomText = "Harry" }
    };
}

public class MyChildViewModel
{
    public string MyCustomText { get; set; }
}

假设您将MainWindow的DataContext设置为MainViewModel的实例并添加ListView:

<ListView ItemsSource="{Binding MyItems}" />

如果执行此操作,则会看到以下内容:

enter image description here

这里发生的是ListView正在为列表中的三个元素中的每个创建一个容器(类型为ContentPresenter),并将每个人的DataContext设置为指向其自己的MyChildViewModel实例。默认情况下,ContentPresenter只是在其DataContext上调用“ ToString()”,因此您只看到它指向的类的名称。如果像这样向您的MyChildViewModel中添加ToString()运算符:

public override string ToString()
{
    return $"MyChildViewModel: {this.MyCustomText}";
}

...然后您会看到显示的内容:

enter image description here

您还可以完全覆盖ListViewItem的模板,并且由于它已经指向与其关联的MyChildViewModel实例,因此您可以直接绑定到其属性:

<ListView ItemsSource="{Binding MyItems}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <!-- One of these gets created for each element in the list -->
            <Border BorderBrush="Black" BorderThickness="1" Background="CornflowerBlue" CornerRadius="5" Padding="5">
                <TextBlock Text="{Binding MyCustomText}" Foreground="Yellow" />
            </Border>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

将哪个显示更改为:

enter image description here

有道理吗?