我的UI上显示了两个数据网格。当我在datagrid 1上选择一个特定的行时,我想在datagrid 2上显示datagrid 1的详细信息。我正在从数据库中填充datagrid数据。 这是两个数据库表结构。
注意:表格都由数据库中的personid映射
这是迄今为止我尝试过的代码
Baseclass.cs
public class Baseclass
{
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T member, T value, [CallerMemberName] string propertyName = null)
{
member = value;
this.RaiseNotification(propertyName);
}
protected void RaiseNotification(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
person.cs
public class person : Baseclass
{
private int personID;
public int PersonID
{
get { return personID; }
set { this.SetProperty<int>(ref this.personID, value); }
}
private string firstName;
public string FirstName
{
get { return firstName; }
set { this.SetProperty<string>(ref this.firstName, value); }
}
private string lastName;
public string LastName
{
get { return lastName; }
set { this.SetProperty<string>(ref this.lastName, value); }
}
Model _personModel = new Model();
private ObservableCollection<person> _person = new ObservableCollection<person>();
public ObservableCollection<person> Getpersons
{
get { return _person; }
set { _person = value; OnPropertyChanged("GetPersons"); }
}
public person()
{
initializeload();
}
private void initializeload()
{
try
{
DataTable table = _personModel.getData();
for (int i = 0; i < table.Rows.Count; ++i)
Getpersons.Add(new person
{
PersonID = Convert.ToInt32(table.Rows[i][0]),
FirstName = table.Rows[i][1].ToString(),
LastName = table.Rows[i][2].ToString(),
});
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyname)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyname));
}
public class Model
{
public DataTable getData()
{
DataTable ndt = new DataTable();
SqlConnection sqlcon = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
sqlcon.Open();
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM [Person].[dbo].[persons]", sqlcon);
da.Fill(ndt);
da.Dispose();
sqlcon.Close();
return ndt;
}
}
}
PersonDetail类
public class PersonDetails : Baseclass
{
private int personID;
public int PersonID
{
get { return personID; }
set { this.SetProperty<int>(ref this.personID, value); }
}
private string address;
public string Address
{
get { return address; }
set { this.SetProperty<string>(ref this.address, value); }
}
private string pos;
public string Position
{
get { return pos; }
set { this.SetProperty<string>(ref this.pos, value); }
}
DetailsModel _detailModel = new DetailsModel();
private ObservableCollection<PersonDetails> _details = new ObservableCollection<PersonDetails>();
public ObservableCollection<PersonDetails> GetDetails
{
get { return _details; }
set { _details = value; OnPropertyChanged("GetDetails"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyname)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyname));
}
public PersonDetails()
{
initializeload();
}
private void initializeload()
{
try
{
DataTable table = _detailModel.getData();
for (int i = 0; i < table.Rows.Count; ++i)
GetDetails.Add(new PersonDetails
{
PersonID = Convert.ToInt32(table.Rows[i][0]),
Address = table.Rows[i][1].ToString(),
Position = table.Rows[i][2].ToString(),
});
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public class DetailsModel
{
public DataTable getData()
{
DataTable ndt = new DataTable();
SqlConnection sqlcon = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
sqlcon.Open();
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM [Person].[dbo].[personDetails]", sqlcon);
da.Fill(ndt);
da.Dispose();
sqlcon.Close();
return ndt;
}
}
}
MainViewModel.cs
public class MainViewModel : Base, INotifyPropertyChanged
{
public MainViewModel()
{
}
private ObservableCollection<person> personValues;
public ObservableCollection<person> Persons
{
get { return personValues; }
set
{
this.SetProperty<ObservableCollection<person>>(ref this.personValues, value);
}
}
private ObservableCollection<PersonDetails> detailsValues;
public ObservableCollection<PersonDetails> Details
{
/* This is correct below ?? I have an error as
'PersonDemo.MainViewModel' does not contain a definition for 'GetDetails' and no extension method 'GetDetails' accepting a first argument of type 'PersonDemo.MainViewModel' could be found (are you missing a using directive or an assembly reference?)*/
get { return this.GetDetails(this.Selectedperson.PersonID); }
}
private person selectedValue;
public person Selectedperson
{
get { return selectedValue; }
set
{
this.SetProperty<person>(ref this.selectedValue, value);
this.RaiseNotification("Details");
}
}
}
XAML
<Grid>
<DataGrid Margin="100,20,116,211" ItemsSource="{Binding Persons}" SelectedItem="{Binding Selectedperson}" />
<DataGrid Margin="100,130,116,101" ItemsSource="{Binding Details}" />
</Grid>
任何人都可以帮忙继续编写MainViewModel吗?我好几周都被困在这里。
答案 0 :(得分:7)
首先,我建议在基类中使用INotifyPropertyChanged
接口。也许您使用它并忘记在示例代码中写入它。为了使用MVVM模式,您需要为两个数据网格实现ViewModel。我们在示例BothGridViewModel
中调用它,您可以根据需要调用它。然后在这个视图模型中你需要所有人的集合,让我们称之为AllPerson
,然后你需要有一个Person
类型的属性,当你在网格中有所选的人时,让我们打电话它SelectedPerson
。它将是这样的:
public class BothGridViewModel:INotifyPropertyChanged
{
...
public ObservableCollection<Person> AllPersons {...}
public Person SelectedPerson {...}
...
}
您只需要在View的DataContext中设置它。和绑定:
<YourView DataContext={Binding SomeBothGridViewModelClass}>
<Grid>
<DataGrid Margin="100,20,116,211" ItemsSource="{Binding AllPersons}" SelectedItem="{Binding SelectedPerson}" />
<DataGrid Margin="100,130,116,101" ItemsSource="{Binding SelectedPerson.Getpersons}" /> <!--Or some SelectedPerson.PersonDetails.GetDetails-->
</Grid>
</YourView DataContext={Binding SomeBothGridViewModelClass}>
我认为这是制作你想要的好视图模型结构。希望这有助于你...
修改
我现在看到你的观点,你有两个数据库表,一个用于主要属性,另一个用于详细信息。我看到两种很好的方法: 1)第一个问题是,我不相信第二个数据网格是必要的,因为每个人都没有收集细节。相反,您可以使用网格和其他控件来显示属性。另外我认为你必须为这个人实现一个视图模型,例如:
public class PersonViewModel:INotifyPropertyChanged
{
public string FirstName {...}
public string LastName {...}
//Other properties
public PersonDetails Details {...}
}
然后在网格中,您可以将项目源绑定到PersonViewModel
的集合,然后您可以绑定到网格的选定项目,例如:
<Grid>
<DataGrid x:Name="dataGrid" ItemsSource={Binding AllPersonViewModels}/>
...
<!--Then some combo box, text block or text box binding to a property of the selected item's details-->
...
<TextBox Text={Binding SelectedItem.Details.Address, ElementName=dataGrid}/>
...
<Grid>
2)我认为可以做的第二种方式是在同一个数据网格中显示所有数据。为此,您需要以这种方式执行PersonViewModel
课程:
public class PersonViewModel:INotifyPropertyChanged
{
public string FirstName {...}
public string LastName {...}
//Other properties
//Then the Details properties
public string Address {...}
//...
//Note this class is like a wrapper for person, and person details
}
这种方式稍微简单一些,但可能导致不需要的数据库无法访问。
编辑2
看了你的代码后,我不得不说几件事情:在PersonViewModel
和PersonDetailViewModel
中你应该使用DispatcherTimer
而不是Timer
,因为我们在Wpf。另外,也许您应该使用存储库模式来创建PersonRepository
和PersonDetailRepository
放置所有数据库通信的地方,事实上,PersonViewModel
和PersonDetailViewModel
是一个在某种程度上,存储库,但到现在你不需要改变它,它应该工作。我将在这里向您展示MainViewModel
的代码,我将其修改为具有SelectedPersonDetail属性,这样,您需要做的就是在视图中进行绑定:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using tryout13022013.PersonViewModels;
using System.ComponentModel;
using tryout13022013.DetailsViewModel;
using tryout13022013.PersonModel;
namespace tryout13022013
{
public class MainViewModel
{
private PersonViewModel _subPerson = new PersonViewModel();
public PersonViewModel SubPerson
{
get
{
return _subPerson;
}
set
{
if (_subPerson != value)
{
_subPerson = value; OnPropertyChanged("SubPerson");
}
}
}
private PersonDetailsViewModel _subDetail = new PersonDetailsViewModel();
public PersonDetailsViewModel SubDetail
{
get { return _subDetail; }
set
{
_subDetail = value; OnPropertyChanged("SubDetail");
}
}
private Person _selectedPerson;
public Person SelectedPerson
{
get { return _selectedPerson; }
set {
if (_selectedPerson != value)
{
_selectedPerson = value;
OnPropertyChanged("SelectedPerson");
OnPropertyChanged("SelectedPersonDetail"); //In this way when Change the Selected Person, the Selected Detail will be changed again...
//if (this.SelectedPerson != null && this.SubDetail != null)
//{
// I dont know how to call the PersonDetailsViewModel class like a method here in order to load its data. kindly help
//this.SubDetail.MethodforLoadingPersonDetails(this.SelectedPerson);
//}
}
}
}
public PersonDetails SelectedPersonDetail
{
get
{
if (SubDetail == null || SelectedPerson ==null)
return null;
return SubDetails.DetailsData.FirstOrDefault(detail => detail.PersonID == SelectedPerson.PersonID);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyname)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyname));
}
}
}
这是您可以在视图中进行绑定的实例,在这种情况下,用于选择第二个网格中的项目:
<Window x:Class="tryout13022013.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:person="clr-namespace:tryout13022013.PersonViewModels"
xmlns:details="clr-namespace:tryout13022013.DetailsViewModel"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding SubPerson.PersonData}" SelectedItem="{Binding SelectedPerson, Mode=TwoWay}"
AutoGenerateColumns="False" Height="77" HorizontalAlignment="Left"
Margin="101,26,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="auto" >
<DataGrid.DataContext>
<person:PersonViewModel/>
</DataGrid.DataContext>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="auto" Binding="{Binding PersonID}"/>
<DataGridTextColumn Header="First Name" Width="auto" Binding="{Binding FirstName}"/>
<DataGridTextColumn Header="Last Name" Width="auto" Binding="{Binding LastName}"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding SubDetail.DetailsData}"
AutoGenerateColumns="False" Height="77" HorizontalAlignment="Left"
Margin="101,135,0,0" Name="dataGrid2" VerticalAlignment="Top" Width="255" SelectedItem="{Binding SelectedPersonDetail, Mode=OneWayToSource}">
<DataGrid.DataContext>
<details:PersonDetailsViewModel/>
</DataGrid.DataContext>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="auto" Binding="{Binding PersonID}"/>
<DataGridTextColumn Header="Address" Width="auto" Binding="{Binding Address}"/>
<DataGridTextColumn Header="Position" Width="auto" Binding="{Binding Description}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
我忘记了,你需要绑定到SubPerson.PersonData
和SubDetail.DetailsData
,这些都是集合。
看看......
答案 1 :(得分:4)
在WPF房间与Buba1947聊天后,我得出结论,她实际上在数据库中有一个1-1关系分割表,她实际上试图在分割数据网格中显示。
我建议她在PersonViewModel
&amp;之上创建一个Person
。 PersonDetails
表格,因此她的MainViewModel
在ObservableCollection<PersonViewModel>
属性下只包含一个Persons
,我们将绑定两个DataGrids
。
所以MainViewModel.cs(DataContext)有:
private ObservableCollection<PersonViewModel> personValues;
public ObservableCollection<PersonViewModel> Persons
{
get { return personValues; }
set { this.SetProperty<ObservableCollection<PersonViewModel>>(ref this.personValues, value); }
}
更改数据适配器以使用内部联接使数据进入,相关查询与此类似(填充上面的Persons属性):
"SELECT * FROM [Person].[dbo].[persons] INNER JOIN [Person].[dbo].[personDetails] ON [Person].[dbo].[persons].[Id] = [Person].[dbo].[personDetails].[Id]"
接下来,我们将CollectionViewSource
绑定到Xaml中的人员:
<Window.Resources>
<CollectionViewSource x:Key="PersonsData" Source={Binding Persons} />
</Window.Resources>
并使用单个列绑定DataGrids
:
<DataGrid ItemsSource={Binding Source={StaticResource PersonsData}} AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Header="Person Id" Content={Binding PersonId} />
<DataGridTextColumn Header="First Name" Content={Binding FirstName} />
<DataGridTextColumn Header="Last Name" Content={Binding LastName} />
</DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource={Binding Source={StaticResource PersonsData}} AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Header="Person Id" Content={Binding PersonId} />
<DataGridTextColumn Header="Address" Content={Binding Address} />
<DataGridTextColumn Header="Position" Content={Binding Position} />
</DataGrid.Columns>
</DataGrid>
注意:这里可能有拼写错误,因为我还没有VS,请告诉我是否需要修理。我已经从记忆中写下了所有这些。
答案 2 :(得分:2)
请检查以下链接,找到解决问题的方法