I have two datagrid displayed on my UI. When I select a particular row on datagrid 1, I would like to display the details of the datagrid 1 on datagrid 2. I am populating the datagrid data from a database. here is the two database table structure.
Note: both the table are mapped by the personid in the database
here is the code so far I have tried
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 class
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>
can anybody help to proceed in writing the MainViewModel? I am stuck here since weeks.
First, I recomend to use the
INotifyPropertyChanged
interface in the base class. Maybe you use it and forget write it in the sample code. For make this with MVVM patter, you need to implement a ViewModel for both data grids. Let's call it in our exampleBothGridViewModel
, you may call it as you want. Then in this view model you need the collection of all person, let's call itAllPerson
, then you need to have a property ofPerson
type, when you will have the selected person in the grid, let's call itSelectedPerson
. It will be something like this:All you need, is to set it in the DataContext of the View. And bindings:
I think this is the good view model structure for make what you want. Hope this helps you...
EDIT
I see your point now, you have two database tables, one for the main properties and other for the details. I see two good ways for doing this: 1) The first one, is that I don't belive that the second datagrid is necesary due for each person you don't have a collection of details. Instead you may use a grid, and other controls for showing the properties. Also I think you must to implement a view model for the person, for instance:
Then in the grid, you may bind the items source to a collection of
PersonViewModel
then you can make bindings to the selected item of the grid, for instance:2) The second way I think could be done, is showing all data in the same datagrid. For this you need to do the
PersonViewModel
class in this way:This way is a bit simpler, but maybe cause an unwanted database over access.
EDIT 2
After have a look of your code, I have to say a few things about it: In the
PersonViewModel
andPersonDetailViewModel
you should use aDispatcherTimer
instead aTimer
, because we are in Wpf. Other thing, maybe you should use a Repository pattern, to create aPersonRepository
and aPersonDetailRepository
where to put all the DB comunication, in fact,PersonViewModel
andPersonDetailViewModel
are a in some way, repositories, but by now you don't need to change it, it should work. I'm going to show you here the code of theMainViewModel
, I nodified it to have the SelectedPersonDetail property, in this way, all you need to do is make a binding in the View:And this is an instace of a binding you can make in your View, in this case for selecting the item in the second grid:
I forgot it, you need to make the binding to
SubPerson.PersonData
andSubDetail.DetailsData
, these are the collection. Check it out...Kindly check the below link to find the solution for my problem
thread link
Having had a chat with Buba1947 in WPF room, I've concluded that she's actually got a 1-1 relation split table in database that she's actually trying to show as such in the split data grids.
I've proposed her to create a
PersonViewModel
on top ofPerson
&PersonDetails
tables so herMainViewModel
contains only oneObservableCollection<PersonViewModel>
under thePersons
property to which we shall bind the twoDataGrids
.so MainViewModel.cs (the DataContext) has:
The data adaptor is changed to bring the data in using an inner join with relevant query being something along the lines of this (which fills the Persons property above):
Next we shall bind a
CollectionViewSource
to Persons in Xaml:and bind
DataGrids
to this with individual columns:Note: there may be typo in here as I haven't got VS to hand, please let me know if I need to fix something. I've written all of this from memory.