我正在学习XAML和WPF,并且仍在努力理解数据绑定,大部分时间都在摸索直到它起作用。
我正在使用组合框来查找“siteid”和“roleid”的相应表的“name”值,同时在DataGrid中显示用户表。
表单如预期地工作,即以正确的方式显示了带有组合框的datagrid,并且也成功更新了用户记录,但是我在输出窗口中获得以下错误消息,这让我感到疯狂:
System.Windows.Data Error: 17 : Cannot get 'Name' value (type 'String') from '' (type 'DataRowView'). BindingExpression:Path=Name; DataItem='DataRowView' (HashCode=25172842); target element is 'ComboBox' (Name=''); target property is 'NoTarget' (type 'Object') RowNotInTableException:'System.Data.RowNotInTableException: This row has been removed from a table and does not have any data. BeginEdit() will allow creation of new data in this row.
问题1:导致此错误的原因是什么?
datagrid+组合框正确显示,那么下面的XAML哪里出错了呢?我看不出来!
我的(简单)测试项目设置并没有什么特别之处:
- VS2013、WPF、.NET45 + entity framework6.1.3(最新版本)和SQL2012
- 用户表:ID(pk)、用户名、密码、SiteID(FK)、RoleID(FK)
- sites表:ID(pk)、名称、描述
- roles表:ID(pk)、名称、描述
XAML:
<UserControl.Resources>
<local:TestProjectDataSet x:Key="testProjectDataSet"/>
<CollectionViewSource x:Key="usersViewSource" Source="{Binding Users, Source={StaticResource testProjectDataSet}}"/>
<CollectionViewSource x:Key="rolesViewSource" Source="{Binding Roles, Source={StaticResource testProjectDataSet}}"/>
<CollectionViewSource x:Key="sitesViewSource" Source="{Binding Sites, Source={StaticResource testProjectDataSet}}"/>
</UserControl.Resources>
<Grid Style="{StaticResource ContentRoot}" DataContext="{StaticResource usersViewSource}">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBlock Text="User Management:" Style="{StaticResource Heading2}" Grid.Row="0"/>
<DataGrid x:Name="UsersDataGrid" ItemsSource="{Binding}" EnableRowVirtualization="True" Grid.Row="1" RowDetailsVisibilityMode="VisibleWhenSelected" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="idColumn" Width="SizeToHeader" IsReadOnly="True" Header="Id" Binding="{Binding Id}" />
<DataGridTextColumn x:Name="usernameColumn" Width="Auto" Header="Username" Binding="{Binding Username}"/>
<DataGridTextColumn x:Name="passwordColumn" Width="Auto" Header="Password" />
<DataGridTemplateColumn x:Name="siteNameColumn" Header="Site" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource sitesViewSource}}"
DisplayMemberPath="Code"
SelectedValuePath="Id"
SelectedValue="{Binding SiteId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn x:Name="roleNameColumn" Header="Role" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource rolesViewSource}}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding RoleId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Save" Width="100" Height="50" Click="Button_Click" Grid.Row="2" />
</Grid>
</UserControl>
问题2:有更好的方法来完成所有这些吗?
我必须使用数据集、表适配器和CollectionViewSources以及一些代码后台。
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace TestProjectUI.Pages.Admin
{
/// <summary>
/// Interaction logic for ManageUsersPage.xaml
/// </summary>
public partial class ManageUsersPage : UserControl
{
private TestProjectDataSet _database;
private TestProjectDataSetTableAdapters.UsersTableAdapter _usersAdapter;
private TestProjectDataSetTableAdapters.RolesTableAdapter _rolesAdapter;
private TestProjectDataSetTableAdapters.SitesTableAdapter _sitesAdapter;
private CollectionViewSource _usersViewSource;
private CollectionViewSource _rolesViewSource;
private CollectionViewSource _sitesViewSource;
public ManageUsersPage()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
_database = ((TestProjectDataSet)(FindResource("magazineInventoryDataSet")));
_usersAdapter = new TestProjectDataSetTableAdapters.UsersTableAdapter();
_usersAdapter.Fill(_database.Users);
_usersViewSource = ((CollectionViewSource)(FindResource("usersViewSource")));
_usersViewSource.View.MoveCurrentToFirst();
_rolesAdapter = new TestProjectDataSetTableAdapters.RolesTableAdapter();
_rolesAdapter.Fill(_database.Roles);
_rolesViewSource = ((CollectionViewSource)(FindResource("rolesViewSource")));
_rolesViewSource.View.MoveCurrentToFirst();
_sitesAdapter = new TestProjectDataSetTableAdapters.SitesTableAdapter();
_sitesAdapter.Fill(_database.Sites);
_sitesViewSource = ((CollectionViewSource)(FindResource("sitesViewSource")));
_sitesViewSource.View.MoveCurrentToFirst();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_usersAdapter.Update(_database.Users);
}
}
}
我曾以为可以在纯XAML中完成所有这些而不需要后台的代码,但迄今为止我还没有成功实现(绑定错误?!)。
如果有人能展示一个更好的方法或改进上述代码,我将不胜感激。
最近我又回到了C#,经过多年的Ruby之后,现在WPF/XAML让我感到很棘手!