Привязка к ItemsSource и SelectedValue объекта ListView внутри UserControl - PullRequest
0 голосов
/ 10 мая 2019

Моя цель состоит в том, чтобы повторно использовать 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 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 позади:

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));
}

Приведенный выше код сшит воедино из большинства примеров, которые я видел онлайн, включая SO.Вот мои проблемы и вопросы.

  1. Когда я запускаю это, ничего не отображается в списке UserControl.В чем проблема?
  2. Как связать свойство SelectedPerson с UserContro., чтобы он знал, как отобразить правильный City на основе выбора?

Ответы [ 2 ]

1 голос
/ 10 мая 2019

Помимо того, что вы пропустили установку DataContext окна, например

DataContext = this;

, вам следует рассмотреть возможность получения вашего элемента управления непосредственно из ListView или более простого ListBox, потому что тогда вы получите прямой доступ ко всем его полезнымproperties.

Разница между 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>

Вы будете использовать MyListBox, как и любой другой ListBox:

<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">
1 голос
/ 10 мая 2019

Так что это меня заинтересовало. Я немного покопался в коде и обнаружил, что для этой работы мне нужно было установить DataContext для MainWindow, как предложил Марк. Так что в конструкторе 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}"/>

Вам нужно будет удалить шаблон элемента. Я надеюсь, что это полезно!

...