2010-10-01 88 views
6

看起來WFP DataGridComboBoxColumn對此列中的所有單元格使用單個ItemsSource。我有一種情況,ComboBox項目依賴於同一行中的其他單元格。我設法在PreparingCellForEdit事件中填充ItemsSource。但是,它不能按需要工作。最初,該列中的所有單元都是空的。一旦我爲此列的ComboBox填充ItemsSource,所有相關的單元格(具有相同的項源)將顯示值。但是,如果我單擊另一種類型的單元格(填充了不同的項目源),則所有值都將消失,並且新類型單元格顯示值。你只能使用一組Items Source作爲列嗎?我不敢相信這是真的。我錯過了什麼嗎?任何解決方法?如何爲WPF DataGrid獲取單元格級別的ComboBox?

回答

1

感謝喬納森的例子,我解決我的問題如下。我修改了喬納森的代碼來突出我的解決方案。我從他的例子中刪除了Territory屬性,因爲我不需要它來解決我的問題。

有兩列。第一列是國家。第二列是StateCandidate。 State列綁定到狀態列表,StateCandidate列綁定到StateCandidates列表。關鍵在於StateCandidates列表在狀態改變時重新創建。因此,每行中可能有不同的狀態候選列表(基於所選狀態)。

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

需要注意的是,當狀態改變時,它不會,直到選擇不同的行更新StateCandidates名單,這是一個分離的問題,我會戰鬥。有誰知道我可以如何強制執行?

再次感謝喬納森的靈感。我會繼續尋找更好的解決方案。

+0

你正在走向完全相同的道路,我無處可去。但既然你堅持,這是代碼:http://www.scottlogic.co.uk/blog/colin/tag/ieditableobject/ – 2010-10-03 12:42:24

+2

@Jonathan:非常感謝你的鏈接。但是,我無法爲這個示例做這項工作,也許是因爲我沒有使用DataTable。儘管如此,我在博客中發現了一條評論,指向http://codefluff.blogspot.com/2010/05/commiting-bound-cell-changes.html。這個解決方案很好。 – newman 2010-10-03 14:09:10

+0

我的appologies,我發佈了錯誤的鏈接。我也沒有使用DataTable,你的鏈接實際上是我最終使用的。 – 2010-10-04 05:14:41

2

你可能無法可靠地做到這一點。網格可以重用組合框或隨機創建/銷燬它。

偶然我碰巧正在做一個這樣的屏幕。鑑於這些...

  • 網格中的每一行都綁定到Trade類型的對象。
  • 每個行業都有一個國家財產
  • 每個行業都有一個TerritoryCanidates財產
  • 更改狀態屬性將導致TerritoryCanidates屬性來更改

這讓我到的ItemsSource綁定到TerritoryCanidates的能力屬性。 DataGrid在任何情況下都會遵守。


<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

我很困惑...它對你有用嗎?我只是試了一下,不能使它工作。我試過的是這樣的:我爲ItemsSource綁定了一個新的集合。此集合將在另一個屬性更改時重新創建。我沒有看到任何組合框下拉菜單。 – newman 2010-10-02 02:42:01

+0

奇怪的是,它看起來像內置的組合框列沒有像我期望的那樣工作。我重新做了我的例子,使用像我真正的程序使用的模板列。 – 2010-10-02 09:43:40

+1

P.S.我現在正式討厭編輯數據的DataGrid。相反,我只是使用ItemsControl。 – 2010-10-02 09:48:24