【问题标题】:How do I bind the data of a custom UserControl如何绑定自定义 UserControl 的数据
【发布时间】:2019-01-21 17:41:39
【问题描述】:

所以我只是设置了一个项目并添加了一个看起来像这样的自定义 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"});
        }
    }

简单,现在我确实有一个看起来像这样并处理 INPC 的 ObservableObject

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 属性,但它没有显示它,我不知道为什么,如果我尝试将它绑定到 TextBlock 属性直接在 MainView 中它工作正常。

我不确定它为什么会这样,我想了解原因。 我需要使用 DependencyProperties 吗?还是只是我创建了 BaseViewModel 的新实例?我哪里做错了?

【问题讨论】:

  • 我认为您的用户控件没有正确的 XAML...看起来它是 MainWindow 的 XAML,其中包含您的测试 TextBlock。

标签: c# .net wpf mvvm


【解决方案1】:

您的MainViewWindow 包含一个带有ItemsSource="{Binding UserViewModel.Users}" 绑定的ItemsControl,每个项目都以&lt;controls:UserCard/&gt; 显示。但是您的用户控件随后尝试使用"{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}" />

如果您这样做,您将看到以下内容:

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

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

...然后您会看到显示的是:

您还可以完全覆盖 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>

这会将显示更改为:

有意义吗?

【讨论】:

  • 哦!所以它从 XAML 中的父级继承 DataContext
  • 在大多数情况下,是的。一个值得注意的例外,适用于您上面显示的情况,是 ListView 和 ItemsCollection 等,其中每个子项都将其 DataContext 设置为列表元素。
  • 所以对于 ListViews 和 ItemsCollection 这不适用吗?对不起,如果我误解了这一点。
  • ListView(例如)将继承其父项的 DataContext,如果您不显式覆盖它,但其所有子项(即 ListBoxViewItem)的 DataContext 将设置为其相应项,无论其父项如何ListBox 的ItemSource 设置为。
  • 假设您的 MainWindow 将其 DataContext 设置为 MainViewModel,并且您的 MainViewModel 有一个名为 MyItems 的项目列表,它是 MyChildViewModel 的数组。如果您将 ListView 放在主窗口上,那么它将从其父窗口(即 MainViewModel)继承它的 DataContext。如果该 ListView 将其 ItemsSource 绑定到“MyItems”,那么它将为您的每个 MyChildViewModel 创建一个子 ListBoxViewItem,并且每个将其 DataContext 设置为它自己的 MyChildViewModel 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
  • 2011-03-17
  • 2020-06-16
  • 2010-12-04
  • 2013-01-20
相关资源
最近更新 更多