2013-02-11 10 views
5

Mam dwa datagrid wyświetlane w moim interfejsie użytkownika. Kiedy wybiorę konkretny wiersz na datagrid 1, chciałbym wyświetlić szczegóły datagridu 1 na datagrid 2. Wprowadzam dane datagrid z bazy danych. Oto dwie struktury tabeli bazy danych.Wybrane datagridItem i databinding

Uwaga: zarówno w tabeli są odwzorowywane przez PersonID w bazie

Person Table

PersonDetails Table

Oto kod do tej pory próbowałem

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)); 
     } 
    } 
} 

pe rson.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; 
     } 
    } 

} 

klasa 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> 

ktoś może pomóc, aby przejść na piśmie MainViewModel? Utknąłem tu od tygodni.

+0

czym problem z kodem? to nie działa, działa z pewnym błędem? To dobrze, że masz dużo kodu, ale daj nam znać, jaki jest problem. – iltzortz

+1

W której linii znalazłeś błąd? Moje pytanie brzmi: znaleźć właściwy sposób kodowania ViewModel. Widzisz tylko Widok i Model. – user1221765

Odpowiedz

7

Najpierw polecam używanie interfejsu INotifyPropertyChanged w klasie bazowej. Może go użyjesz i zapomnisz zapisać go w przykładowym kodzie. Aby to zrobić przy pomocy MVVM, musisz zaimplementować ViewModel dla obu siatek danych. Nazwijmy to w naszym przykładzie BothGridViewModel, możesz go nazwać tak, jak chcesz. Następnie w tym modelu widoku potrzebujesz kolekcji wszystkich osób, nazwijmy ją AllPerson, wtedy musisz mieć właściwość o typie Person, kiedy będziesz mieć wybraną osobę w siatce, nazwijmy ją SelectedPerson. To będzie coś takiego:

public class BothGridViewModel:INotifyPropertyChanged 
{ 
    ... 
    public ObservableCollection<Person> AllPersons {...} 
    public Person SelectedPerson {...} 
    ... 
} 

Wszystko, czego potrzebujesz, to ustawić w DataContext widoku. I wiązań:

<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}> 

Uważam, że jest to dobra struktura modelu widoku do robienia tego, co chcesz. Nadzieję, że to pomaga ...


EDIT


widzę swój punkt teraz, masz dwie tabele bazy danych, jeden dla głównych właściwości oraz inne dla szczegółów. Widzę dwa dobre sposoby na zrobienie tego: 1) Pierwszym z nich jest to, że nie wierzę, że drugi datagrid jest konieczny z powodu każdej osoby, której nie posiadasz zbioru szczegółów. Zamiast tego możesz użyć siatki i innych elementów sterujących do pokazywania właściwości.Także myślę, że należy wdrożyć model widok dla osoby, na przykład:

public class PersonViewModel:INotifyPropertyChanged 
{ 
    public string FirstName {...} 
    public string LastName {...} 
    //Other properties 

    public PersonDetails Details {...} 
} 

Następnie w siatce, można powiązać źródło elementów do kolekcji PersonViewModel następnie można dokonać wiązania do wybranej pozycji siatka, na przykład:

<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) Drugi sposób, jaki wydaje mi się możliwy do zrobienia, pokazuje wszystkie dane z tego samego datagridu. Do tego trzeba zrobić klasę PersonViewModel w ten sposób:

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 
} 

ten sposób jest nieco prostsze, ale może spowodować niechciane bazę nad dostępem.


EDIT 2


Po rzucić okiem swojego kodu, muszę powiedzieć kilka rzeczy o nim: na PersonViewModel i PersonDetailViewModel należy użyć DispatcherTimer zamian Timer, bo są w Wpf. Inne, być może powinieneś użyć wzorca repozytorium, aby utworzyć PersonRepository i PersonDetailRepository, gdzie wszystkie komunikaty DB, w rzeczywistości, PersonViewModel i PersonDetailViewModel są w pewien sposób repozytoriami, ale do tej pory nie trzeba zmieniać to powinno działać. Mam zamiar pokazać wam tutaj kod MainViewModel, ja nodified go mieć właściwość SelectedPersonDetail, w ten sposób, wszystko co musisz zrobić, to wiąże w Widoku:

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)); 
     } 
    } 
} 

I to jest instace wiążącej można dokonać w widoku, w tym przypadku do wyboru pozycji w drugiej sieci:

<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> 

zapomniałem go, trzeba wykonać wiązanie SubPerson.PersonData i SubDetail.DetailsData, są to kolekcja. Sprawdź to ...

+0

Dzięki Raulu, zredagowałem moje pytanie powyżej .. proszę to sprawdzić .. – user1221765

+0

Buba, proszę sprawdzić moją odpowiedź, zmodyfikowałem to. Jeśli jakakolwiek wątpliwość sprawi, że będę komentować ... –

+0

Dziękuję za twoją obserwację .. pierwsza brzmi podobnie, ale niezupełnie, a druga opcja jest czymś, czego nie potrzebuję. Mam dwie różne tabele w bazie danych. Muszę wyświetlać jako dwa różne datagridy, gdy kliknę ID osoby = 1 w datagrid1, datagrid 2 powinien wyświetlać tylko szczegóły należące do personID = 1. Dodam mój kompletny kod i opublikuję go tutaj, proszę spojrzeć, dostaniesz pełny przegląd mojego problemu. Wielkie dzięki – user1221765

4

Po rozmowie z Buba1947 w pokoju WPF, doszedłem do wniosku, że ona ma tabelę podziału relacji 1-1 w bazie danych, którą próbuje pokazać jako taką w podzielonych danych siatki.

ja proponuje ją stworzyć PersonViewModel na szczycie Person & PersonDetails tabelach więc jej MainViewModel zawiera tylko jeden ObservableCollection<PersonViewModel> pod mienia Persons, do którego będziemy łączyć obie DataGrids.

tak MainViewModel.cs (DataContext) posiada:

private ObservableCollection<PersonViewModel> personValues; 
public ObservableCollection<PersonViewModel> Persons 
{ 
    get { return personValues; } 
    set { this.SetProperty<ObservableCollection<PersonViewModel>>(ref this.personValues, value); } 
} 

Adapter dane są zmieniane w celu dostosowania danych w użyciu sprzężenie wewnętrzne z odpowiednim zapytaniem będąc coś na wzór tego (który wypełnia własność osób powyżej):

"SELECT * FROM [Person].[dbo].[persons] INNER JOIN [Person].[dbo].[personDetails] ON [Person].[dbo].[persons].[Id] = [Person].[dbo].[personDetails].[Id]" 

Następny będziemy wiązać CollectionViewSource do osób w XAML:

<Window.Resources> 
    <CollectionViewSource x:Key="PersonsData" Source={Binding Persons} /> 
</Window.Resources> 

i wiążą DataGrids do tego z poszczególnych kolumn:

<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> 

Uwaga: może występować literówka, ponieważ nie mam pod ręką instrukcji VS, proszę dać mi znać, jeśli muszę coś naprawić. Napisałem to wszystko z pamięci.