2010-10-01 4 views
6

Il semble WFP DataGridComboBoxColumn utilise un seul ItemsSource pour toutes les cellules de cette colonne. J'ai un cas où les éléments ComboBox dépendent de l'autre cellule dans la même rangée. J'ai réussi à remplir l'événement ItemsSource dans PreparingCellForEdit. Cependant, cela ne fonctionne pas comme souhaité. Initialement, toutes les cellules de cette colonne sont vides. Une fois que j'ai peuplé ItemsSource pour le ComboBox de cette colonne, toutes les cellules liées (avec la même source d'éléments) affichent des valeurs. Toutefois, si je clique sur un autre type de cellule (une source d'éléments différents est remplie), toutes les valeurs disparaissent et les nouvelles cellules de type affichent des valeurs. Vous ne pouvez utiliser qu'un seul ensemble de sources d'articles pour une colonne? Je ne peux pas croire que c'est vrai. Ai-je manqué quelque chose? Une solution de contournement?Comment obtenir le niveau de la cellule ComboBox pour WPG DataGrid?

Répondre

1

Merci à l'exemple de Jonathan, je résolus mon problème comme suit. J'ai modifié le code de Jonathan pour mettre en évidence ma solution. J'ai supprimé la propriété Territory de son exemple car je n'en ai pas besoin pour mon problème.

Il existe deux colonnes. La première colonne est Etat. La deuxième colonne est StateCandidate. La colonne Etat est liée à une liste States et la colonne StateCandidate est liée à une liste StateCandidates. Le point clé est que la liste StateCandidates est recréée lorsque l'état est modifié. Ainsi, il pourrait y avoir une liste différente de StateCandidates dans chaque rangée (basée sur l'état sélectionné).

MainWindow.xaml

<Window x:Class="WpfTest1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <DataGrid Name="Zoom" AutoGenerateColumns="False" Background="DarkGray" RowHeaderWidth="50" HeadersVisibility="All"> 
      <DataGrid.Columns> 
       <DataGridTemplateColumn x:Name="colState" Header="State" Width="120"> 
        <DataGridTemplateColumn.CellTemplate> 
         <DataTemplate> 
          <TextBlock Text="{Binding State}" /> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellTemplate> 
        <DataGridTemplateColumn.CellEditingTemplate> 
         <DataTemplate> 
          <ComboBox SelectedItem="{Binding State}" ItemsSource="{Binding States}" /> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellEditingTemplate> 
       </DataGridTemplateColumn> 
       <DataGridTemplateColumn x:Name="colStateCandiate" Header="State Candidate" Width="200"> 
        <DataGridTemplateColumn.CellTemplate> 
         <DataTemplate> 
          <TextBlock Text="{Binding StateCandidate}" /> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellTemplate> 
        <DataGridTemplateColumn.CellEditingTemplate> 
         <DataTemplate> 
          <ComboBox SelectedItem="{Binding StateCandidate}" ItemsSource="{Binding StateCandidates}" /> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellEditingTemplate> 
       </DataGridTemplateColumn> 
      </DataGrid.Columns> 
     </DataGrid> 
    </Grid> 
</Window> 

MainWindow.xaml.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
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 System.ComponentModel; 

namespace WpfTest1 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      List<Model> list = new List<Model>(); 
      list.Add(new Model() { State = "TX", StateCandidate = "TX2" }); 
      list.Add(new Model() { State = "CA" }); 
      list.Add(new Model() { State = "NY", StateCandidate = "NY1" }); 
      list.Add(new Model() { State = "TX" }); 
      list.Add(new Model() { State = "AK" }); 
      list.Add(new Model() { State = "MN" }); 

      Zoom.ItemsSource = list; 
      Zoom.PreparingCellForEdit += new EventHandler<DataGridPreparingCellForEditEventArgs>(Zoom_PreparingCellForEdit); 
     } 

     void Zoom_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e) 
     { 
      if (e.Column == colStateCandiate) 
      {     
       DataGridCell cell = e.Column.GetCellContent(e.Row).Parent as DataGridCell; 
       cell.IsEnabled = (e.Row.Item as Model).StateCandidates != null; 
      } 
     } 
    } 
    public class Model : INotifyPropertyChanged 
    { 
     public event PropertyChangedEventHandler PropertyChanged; 

     private string _state; 
     private List<string> _states = new List<string>() { "CA", "TX", "NY", "IL", "MN", "AK" }; 
     private string _stateCandidate; 
     private List<string> _stateCandidates; 

     public string State 
     { 
      get { return _state; } 
      set 
      { 
       if (_state != value) 
       { 
        _state = value; 
        _stateCandidate = null; 
        if (_state == "CA" || _state == "TX" || _state == "NY") 
         _stateCandidates = new List<string> { _state + "1", _state + "2" }; 
        else 
         _stateCandidates = null; 
        OnPropertyChanged("State"); 
       } 
      } 
     } 
     public List<string> States 
     { 
      get { return _states; } 
     } 
     public string StateCandidate 
     { 
      get { return _stateCandidate; } 
      set 
      { 
       if (_stateCandidate != value) 
       { 
        _stateCandidate = value; 
        OnPropertyChanged("StateCandidate"); 
       } 
      } 
     } 
     public List<string> StateCandidates 
     { 
      get { return _stateCandidates; } 
     } 
     public void OnPropertyChanged(string name) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(name)); 
     } 
    } 
} 

Notez que, lorsque l'État est modifié, il ne sera pas mise à jour la liste StateCandidates jusqu'à ce qu'une autre ligne est sélectionnée, ce qui est une question séparée, je vais me battre. Est-ce que quelqu'un sait comment je peux forcer?

Merci encore à Jonathan pour son inspiration. Je continuerai à chercher une meilleure solution.

+0

Vous descendez exactement la même route vers nulle part où j'étais. Mais puisque vous insistez, voici le code: http://www.scottlogic.co.uk/blog/colin/tag/ieditableobject/ –

+2

@Jonathan: Merci beaucoup pour le lien. Cependant, je ne pouvais pas faire fonctionner cela pour cet exemple, peut-être parce que je n'utilise pas DataTable. Tout en se torturant avec cela, j'ai trouvé un commentaire dans ce blog pointant vers http://codefluff.blogspot.com/2010/05/commiting-bound-cell-changes.html. Cette solution fonctionne très bien. – newman

+0

Mes excuses, j'ai posté le mauvais lien. Je n'utilise pas non plus de DataTable, votre lien est en fait celui que j'ai fini par utiliser. –

2

Vous ne pouvez probablement pas le faire de manière fiable. La grille peut réutiliser la zone de liste déroulante ou la créer/la détruire au hasard.

Par chance, il m'arrive de travailler sur un écran qui fait exactement cela. Compte tenu de ces ...

  • Chaque ligne de la grille est liée à un objet de type Trade.
  • Chaque commerce possède une propriété de l'Etat
  • Chaque commerce a une propriété TerritoryCanidates
  • modification de la propriété de l'État entraînera la propriété TerritoryCanidates changer

Cela me donne la possibilité de lier le ItemsSource aux TerritoryCanidates propriété. Ce qui à son tour le DataGrid honorera en toutes circonstances.


<Window x:Class="MainWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="MainWindow" Height="350" Width="525"> 
<Grid> 
    <DataGrid Name="Zoom" AutoGenerateColumns="False"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn Header="State"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <TextBlock Text="{Binding State}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
       <DataGridTemplateColumn.CellEditingTemplate> 
        <DataTemplate> 
         <ComboBox SelectedItem="{Binding State}" ItemsSource="{Binding StateCanidates}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellEditingTemplate> 
      </DataGridTemplateColumn> 

      <DataGridTemplateColumn Header="Territory"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <TextBlock Text="{Binding Territory}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
       <DataGridTemplateColumn.CellEditingTemplate> 
        <DataTemplate> 
         <ComboBox SelectedItem="{Binding Territory}" ItemsSource="{Binding TerritoryCanidates}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellEditingTemplate> 
      </DataGridTemplateColumn> 

     </DataGrid.Columns> 

    </DataGrid> 
</Grid> 
</Window> 


Imports System.ComponentModel 

Class MainWindow 
Sub New() 

    ' This call is required by the designer. 
    InitializeComponent() 

    ' Add any initialization after the InitializeComponent() call. 
    Dim x As New List(Of Model) 
    x.Add(New Model) 
    x.Add(New Model) 
    x.Add(New Model) 

    Zoom.ItemsSource = x 
End Sub 
End Class 

Class Model 
Implements INotifyPropertyChanged 

Public ReadOnly Property StateCanidates As List(Of String) 
    Get 
     Return New List(Of String) From {"CA", "TX", "NY"} 
    End Get 
End Property 

Public ReadOnly Property TerritoryCanidates As List(Of String) 
    Get 
     If State = "" Then Return Nothing 
     Return New List(Of String) From {State & "1", State & "2"} 
    End Get 
End Property 

Private m_State As String 
Public Property State() As String 
    Get 
     Return m_State 
    End Get 
    Set(ByVal value As String) 
     m_State = value 
     OnPropertyChanged("State") 
     OnPropertyChanged("TerritoryCanidates") 
    End Set 
End Property 

Private m_Territory As String 
Public Property Territory() As String 
    Get 
     Return m_Territory 
    End Get 
    Set(ByVal value As String) 
     m_Territory = value 
     OnPropertyChanged("Territory") 
    End Set 
End Property 




Public Sub OnPropertyChanged(ByVal propertyName As String) 
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) 
End Sub 

Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged 
End Class 
+0

Je suis confus ... ça marche pour vous? Je l'ai juste essayé et je ne pouvais pas le faire fonctionner. Ce que j'ai essayé est comme ceci: j'ai fait une nouvelle collection pour l'ItemsSource à lier. Cette collection sera recréée lorsqu'une autre propriété sera modifiée. Je ne vois rien dans la liste déroulante de la liste déroulante. – newman

+0

Bizarre, il semble que la colonne de liste déroulante intégrée ne fonctionne pas comme prévu. J'ai refait mon exemple en utilisant des colonnes modèles comme mon vrai programme utilise. –

+1

P.S. Je déteste maintenant officiellement le DataGrid pour éditer les données. Je vais plutôt utiliser simplement un ItemsControl. –

Questions connexes