C# 绑定到UserControl内部ListView的ItemsSource和SelectedValue
我的目标是重用C# 绑定到UserControl内部ListView的ItemsSource和SelectedValue,c#,wpf,listview,user-controls,itemssource,C#,Wpf,Listview,User Controls,Itemssource,我的目标是重用ListView和我设计的UserControl中的几个其他控件 为了简洁起见,假设我有一个像这样的Person类,以及它的实例列表 public class Person { public string Name { get; set; } public string City { get; set; } } 我的主窗口: <Window x:Class="ReusableListView.MainWindow" ... W
ListView
和我设计的UserControl
中的几个其他控件
为了简洁起见,假设我有一个像这样的Person
类,以及它的实例列表
public class Person
{
public string Name { get; set; }
public string City { get; set; }
}
我的主窗口
:
<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;
}
}
以及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)中拼凑而成的。以下是我的问题和疑问
UserControl
列表中不显示任何内容。有什么问题吗SelectedPerson
属性绑定到usercontrol.
以便它知道如何根据选择显示正确的City
所以这个让我感兴趣。我把代码弄得有点乱,我发现为了让它工作,我必须按照马克的建议为MainWindow设置DataContext。因此,在主窗口构造函数中,您可以
DataContext = this;
我还发现依赖项属性设置的方式存在问题。您已将其设置为对象。如果将其设置为IEnumerable,它将工作。我相信有一种更通用的方法可以做到这一点,然而,这应该会让你走上正确的道路。问题是ItemsSource无法使用对象。它需要数不清
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。我希望这是有帮助的 所以这个让我感兴趣。我把代码弄得有点乱,我发现为了让它工作,我必须按照马克的建议为MainWindow设置DataContext。因此,在主窗口构造函数中,您可以
DataContext = this;
我还发现依赖项属性设置的方式存在问题。您已将其设置为对象。如果将其设置为IEnumerable,它将工作。我相信有一种更通用的方法可以做到这一点,然而,这应该会让你走上正确的道路。问题是ItemsSource无法使用对象。它需要数不清
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。我希望这是有帮助的 此外,您没有设置窗口的DataContext,例如
DataContext = this;
您应该考虑直接从ListVIEW或更简单的ListBox派生控件,因为这样您就可以直接访问其所有有用的属性。
与UserControl不同的是,XAML在ResourceDictionaryThemes/Generic.XAML
中是默认样式,当您将自定义控件添加到WPF项目时会自动生成该样式
控件的代码,将基类从控件更改为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:
<local:MyListBox ItemsSource="{Binding PersonList}"
SelectedItem="{Binding SelectedPerson}"
DisplayMemberPath="Name"
SelectedValuePath="City">
如果您不希望在派生的ListBox中具有其他属性,那么您也可以根本不派生控件,而在声明控件模板时只将其分配给ListBox:
<Window.Resources>
<ControlTemplate x:Key="MyListBoxTemplate">
...
</ControlTemplate>
</Window.Resources>
...
<ListBox Template="{StaticResource MyListBoxTemplate}"
ItemsSource="{Binding PersonList}"
SelectedItem="{Binding SelectedPerson}"
DisplayMemberPath="Name"
SelectedValuePath="City">
...
...
此外,您没有设置窗口的DataContext,如
DataContext = this;
您应该考虑直接从ListVIEW或更简单的ListBox派生控件,因为这样您就可以直接访问其所有有用的属性。
与UserControl不同的是,XAML在ResourceDictionaryThemes/Generic.XAML
中是默认样式,当您将自定义控件添加到WPF项目时会自动生成该样式
控件的代码,将基类从控件更改为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:
<local:MyListBox ItemsSource="{Binding PersonList}"
SelectedItem="{Binding SelectedPerson}"
DisplayMemberPath="Name"
SelectedValuePath="City">
如果您不希望在派生的ListBox中具有其他属性,那么您也可以根本不派生控件,而在声明控件模板时只将其分配给ListBox:
<Window.Resources>
<ControlTemplate x:Key="MyListBoxTemplate">
...
</ControlTemplate>
</Window.Resources>
...
<ListBox Template="{StaticResource MyListBoxTemplate}"
ItemsSource="{Binding PersonList}"
SelectedItem="{Binding SelectedPerson}"
DisplayMemberPath="Name"
SelectedValuePath="City">
...
...
在我看来,您并没有将主窗口的DataContext
分配到任何地方。@jsanalytics,在我的实际程序中,我确实使用了ViewModel。这只是为了简单地发布一个更简单的MCVE。永远不要依赖于列表视图
或组合框
选择的项目始终选择绑定属性!这种行为驱动着WinForms。很好,您没有在这里使用MvvM标记,因为这里没有。在我看来,您不会在任何地方分配主窗口的DataContext
。@jsanalytics,在我的实际程序中