2010-10-01 55 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

相关问题