简而言之:我有一个 ListView,当我选择 ListView 的一个项目时,该项目应该在详细的 UserControl 中显示和编辑。
我有一个窗口 (ViewMain) 和一个用户控件 (UserControlEmployees),它有一个 ListView 和另一个用户控件 (UserControlEmployeeDetails)。 ListView 的项目由第三个 UserControl (UserControlEmployee) 显示。 UserControlEmployees 有两个依赖属性:一个 ObservableCollection (Employees) 和一个单个 Employee (SelectedEmployee)。 ViewModel 将 ObservableCollection 传递给 UserControlEmployees。 UserControlEmployees 然后将Employees 传递给ListView。 ListView 的 SelectedItem 绑定(bind)到 SelectedEmployee。
像这样的东西:
SelectedEmployee 也应该绑定(bind)到 UserControlEmployeeDetails。所以我尝试将 ViewModelEmployeeDetail 和 ListView 的 SelectedItem 绑定(bind)到同一个依赖属性。
我猜这个问题在 UserControlEmployees 中:
我的想法是 control.ControlEmployeesListView.SelectedItem = e.NewValue as Employee;将 SelectedItem 绑定(bind)到 SelectedEmployee。但这不起作用,我不知道我还能如何绑定(bind)它。通常我会在 XAML 中做类似的事情,但在这种情况下我无权访问它。
编辑
我注意到我忘记将 ListView SelectedItem 设置为 Binding。
<ListView
x:Name="ControlEmployeesListView"
Grid.Row="0"
SelectedItem="{Binding Mode=TwoWay}">
我解决了这个问题,但现在我得到了这个异常:System.Windows.Markup.XamlParseException: ''在 'System.Windows.Data.Binding' 上提供值引发了异常。'行号“26”和行位置“17”。
内部异常
InvalidOperationException:双向绑定(bind)需要 Path 或 XPath。
/编辑
UserControlEmployees.xaml
<UserControl
x:Class="TestNestedUserControls.View.UserControls.UserControlEmployees"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="clr-namespace:TestNestedUserControls.View.UserControls"
d:DesignHeight="25"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- ListView -->
<ListView Grid.Row="0">
<ListView x:Name="ControlEmployeesListView" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<uc:UserControlEmployeeListItem EmployeeListItem="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ListView>
<!-- Details -->
<uc:UserControlEmployeeDetails x:Name="ControlUserControlEmployeeDetails" Grid.Row="1" />
<!-- SelectedEmployee="{Binding}" -->
</Grid>
</UserControl>
这就是它的 UserControlEmployees.xaml.cs 中的代码using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TestNestedUserControls.Model;
namespace TestNestedUserControls.View.UserControls
{
/// <summary>
/// Interaction logic for UserControlEmployees.xaml
/// </summary>
public partial class UserControlEmployees : UserControl, INotifyPropertyChanged
{
public UserControlEmployees()
{
InitializeComponent();
}
// List Items
public ObservableCollection<Employee> Employees
{
get { return (ObservableCollection<Employee>)GetValue(EmployeesProperty); }
set
{
SetValue(EmployeesProperty, value);
NotifyPropertyChanged();
}
}
public static readonly DependencyProperty EmployeesProperty =
DependencyProperty.Register(nameof(Employees), typeof(ObservableCollection<Employee>), typeof(UserControlEmployees), new PropertyMetadata(default, SetNew));
private static void SetNew(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as UserControlEmployees;
if (control != null)
{
control.ControlEmployeesListView.ItemsSource = e.NewValue as ObservableCollection<Employee>;
}
}
//Selected Item
public Employee SelectedEmployee
{
get { return (Employee)GetValue(EmployeeProperty); }
set
{
SetValue(EmployeeProperty, value);
NotifyPropertyChanged();
}
}
public static readonly DependencyProperty EmployeeProperty =
DependencyProperty.Register(nameof(SelectedEmployee), typeof(Employee), typeof(UserControlEmployees), new PropertyMetadata(default, SetNewSelected));
private static void SetNewSelected(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as UserControlEmployees;
if (control != null)
{
control.ControlUserControlEmployeeDetails.EmployeeDetail = e.NewValue as Employee;
control.ControlEmployeesListView.SelectedItem = e.NewValue as Employee;
}
}
#region INotifyPropertyChanged ⬇️
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion ⬆️
}
}
最佳答案
修复绑定(bind)错误:错误信息提供了错误解释和解决方法。简单设置Binding.Path
.
<ListView SelectedItem="{Binding Path=., Mode=TwoWay}">
请注意 Selector.SelectedItem
绑定(bind) TwoWay
默认情况下。所以写下就足够了:<ListView SelectedItem="{Binding}">
从绑定(bind)来看,它看起来像您的
DataContext
是错的。由于所有用户控件都使用相同的数据进行操作,例如员工的集合和选定的员工,所有用户控件应共享相同的 DataContext
这是保存源集合的 View 模型。这个 View 模型还应该定义一个
SelectedEmployee
ControlEmployeesListView
的属性(property)( ListView
)和 UserControlEmployeeDetails
都可以绑定(bind)到。由于
UserControlEmployees
内部不对员工集合进行操作,不需要专门的Employee
和 SelectedEmployee
属性(property)。仅当用户控件旨在可重用时,它才能或应该具有这些属性。但是当它只在这个特定的上下文中使用时,你知道DataContext
您可以提前避开它们并直接绑定(bind)到 UserControl.DataContext
.Control
, UserControl
或 DependencyObject
一般不应该实现INotifyPropertyChanged
但将它们的属性实现为 DependecyProperty
. set
和 get
DependencyProperty
的方法只是 DependencyObject.SetValue
的包装和 DependencyObject.GetValue
.这些包装器仅由您的自定义代码调用,但从不由框架调用。由于
DependencyProperty
提供自己的通知机制,包装器只是设置它们关联的 DependencyProperty
,将自动发出更改通知。因此调用NotifyPropertyChanged()
在每个 setter 中都是多余的。还有一点是你的
SetNew...
属性更改回调。他们只是将新值委托(delegate)给控件。这应该在数据绑定(bind)的帮助下完成。我也想知道这是什么嵌套的
<ListView><ListView /></ListView>
是关于。也删除它(这甚至可以编译吗?)。DependencyProperty
字段应与注册属性同名:SelectedEmployeeProperty
而不是 EmployeeProperty
.以下示例显示了如何正确连接数据。它基于您的代码并使用
Emloyees
的专用属性和 SelectedEmployee
.在您的场景中,删除这些属性并直接绑定(bind)到 DataContext
似乎很合理。 (这是 View 模型)。但这取决于用户控制的目的。但这也会简化代码。MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Employee> Employees { get; set; }
private Employee selectedEmployee;
public Employee SelectedEmployee
{
get => this.selectedEmployee;
set
{
this.selectedEmployee = value;
OnPropertyChanged();
}
}
}
UserControlEmployees.xaml.cs public partial class UserControlEmployees : UserControl
{
public UserControlEmployees()
{
InitializeComponent();
}
public IEnumerable<Employee> Employees
{
get => (IEnumerable<Employee>) GetValue(EmployeesProperty);
set => SetValue(EmployeesProperty, value);
}
public static readonly DependencyProperty EmployeesProperty = DependencyProperty.Register(
nameof(Employees),
typeof(IEnumerable<Employee>),
typeof(UserControlEmployees),
new PropertyMetadata(default));
}
public Employee SelectedEmployee
{
get => (Employee) GetValue(SelectedEmployeeProperty);
set => SetValue(SelectedEmployeeProperty, value);
}
// Configure to bind TwoWay by default
public static readonly DependencyProperty SelectedEmployeeProperty = DependencyProperty.Register(
nameof(SelectedEmployee),
typeof(Employee),
typeof(UserControlEmployees),
new FrameworkPropertyMetadata(
default,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
UserControlEmployees.xaml <UserControl>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- ListView -->
<ListView Grid.Row="0"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=Employees}"
SelectedItem="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=SelectedEmployee}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type local:Employee}">
<uc:UserControlEmployeeListItem EmployeeListItem="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- Details -->
<uc:UserControlEmployeeDetails Grid.Row="1"
SelectedEmployee="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=SelectedEmployee}" />
</Grid>
</UserControl>
MainWndow.xaml <Window>
<Window.DataContext>
<MainViewModel />
</Window.DataContext>
<UserControlEmployees Employees="{Binding Employees}"
SelectedEmployee="{Binding SelectedEmployee}" />
</Window>
关于c# - 嵌套的 UserControls 通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63810851/