绑定到UserControl内部的ListView的ItemsSource和SelectedValue

时间:2019-05-09 21:34:44

标签: c# wpf listview user-controls itemssource

我的目标是重用我设计的ListView内部的UserControl和几个其他控件。

为简洁起见,假设我有一个Person这样的类,以及它的实例列表。

public class Person
{
    public string Name { get; set; }
    public string City { get; set; }
}

我的MainWindow

<Window x:Class="ReusableListView.MainWindow"
        ...
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="600" Width="600">
    <Grid>        
        <local:UCListView Margin="8"
                          ItemsSource="{Binding PersonList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</Window>

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private ObservableCollection<Person> _personList = null;
    public ObservableCollection<Person> PersonList
    {
        get { return _personList; }
        set { _personList = value; OnPropertyChanged("PersonList"); }
    }

    private Person _selectedPerson = null;
    public Person SelectedPerson
    {
        get { return _selectedPerson; }
        set { _selectedPerson = value; OnPropertyChanged("SelectedPerson"); }
    }

    public MainWindow()
    {
        InitializeComponent();
        PersonList = GetPeople();
    }

    private ObservableCollection<Person> GetPeople()
    {
        var list = new ObservableCollection<Person>
        {
            new Person() { Name = "Jane", City = "NYC" },
            new Person() { Name = "John", City = "LA" }
        };
        return list;
    }
}

我想将Name的{​​{1}}属性显示为Person内我ListView中的单个项目,然后在其右侧显示所选人的UserControl财产。所以我的City看起来像这样:

UserControl

以及后面的<UserControl x:Class="ReusableListView.UCListView" ... x:Name="MyListViewUC" d:DesignHeight="500" d:DesignWidth="580"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <ListView Grid.Column="0" MinWidth="256" Margin="8" DataContext="{Binding ElementName=MyListViewUC}" ItemsSource="{Binding ItemsSource}"> <ListView.ItemTemplate> <DataTemplate> <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Width="Auto" Margin="8" Background="Pink" Text="{Binding Name}"/> </DataTemplate> </ListView.ItemTemplate> </ListView> <TextBox Grid.Column="1" Margin="8" Background="PaleGreen"/> </Grid> </UserControl> 代码:

UserControl

上面的代码与我在网上看到的大多数示例(包括SO)结合在一起。这是我的问题和疑问。

  1. 运行此命令时,public partial class UCListView : UserControl { public UCListView() { InitializeComponent(); } public object ItemsSource { get { return GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(UCListView), new PropertyMetadata(null)); } 列表中未显示任何内容。似乎是什么问题?
  2. 如何将UserControl属性绑定到SelectedPerson,以便它知道如何根据选择显示正确的UserContro.

2 个答案:

答案 0 :(得分:1)

这使我感兴趣。我弄乱了代码,发现为了完成这项工作,我必须按照Mark的建议为MainWindow设置DataContext。因此,在MainWindow构造函数中,您只需输入

DataContext = this;

我还发现,您的依赖项属性设置方式存在问题。您已将其设置为对象。如果将其设置为IEnumerable,它将起作用。我敢肯定,有一种更通用的方法可以做到这一点,但是,这应该使您走上正确的道路。问题是ItemsSource无法使用对象。它需要IEnumerable。

public IEnumerable ItemsSource
{
    get { return (IEnumerable)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
    nameof(ItemsSource), typeof(IEnumerable), typeof(UCListView));

您需要做的最后一件事是创建一个依赖对象以通过DisplayMemberPath传递,或者在您的用户控件中静态设置它。我只是静态地设置了它,但是您可能想要创建一个依赖属性来传递它,以便它可以是动态的。

<ListView Grid.Column="0" MinWidth="256" Margin="8"
          x:Name="listView"
          DataContext="{Binding ElementName=MyListViewUC}"
          DisplayMemberPath="Name"
          ItemsSource="{Binding ItemsSource}"/>

您将必须删除ItemTemplate。我希望这会有所帮助!

答案 1 :(得分:1)

除了您错过了设置Window的DataContext之类的

DataContext = this;

您应该考虑直接从ListView或更简单的ListBox派生您的控件,因为那样您就可以直接访问其所有有用的属性。

与UserControl的区别在于XAML处于ResourceDictionary Themes/Generic.xaml中的默认样式,该样式是在将自定义控件添加到WPF项目时自动生成的。

控件的代码,您可以在其中将基类从Control更改为ListBox:

public class MyListBox : ListBox
{
    static MyListBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(MyListBox),
            new FrameworkPropertyMetadata(typeof(MyListBox)));
    }
}

Generic.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:...">

    <Style TargetType="local:MyListBox">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyListBox">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>

                            <ScrollViewer Grid.Column="0">
                                <ItemsPresenter/>
                            </ScrollViewer>

                            <TextBlock Grid.Column="1"
                                       Text="{TemplateBinding SelectedValue}"/>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

您将像其他任何ListBox一样使用MyListBox:

<local:MyListBox ItemsSource="{Binding PersonList}"
                 SelectedItem="{Binding SelectedPerson}"
                 DisplayMemberPath="Name"
                 SelectedValuePath="City">

如果您不打算在派生的ListBox中拥有其他属性,则也完全不能派生一个控件,而在声明它时将ControlTemplate分配给ListBox:

<Window.Resources>
    <ControlTemplate x:Key="MyListBoxTemplate">
        ...
    </ControlTemplate>
</Window.Resources>
...

<ListBox Template="{StaticResource MyListBoxTemplate}"
         ItemsSource="{Binding PersonList}"
         SelectedItem="{Binding SelectedPerson}"
         DisplayMemberPath="Name"
         SelectedValuePath="City">