如何获取 WPF DataGrid 的单元格级别组合框?

Posted

技术标签:

【中文标题】如何获取 WPF DataGrid 的单元格级别组合框?【英文标题】:How to get cell level ComboBox for WPF DataGrid? 【发布时间】:2011-04-20 02:34:15 【问题描述】:

看起来 WFP DataGridComboBoxColumn 正在为此列中的所有单元格使用单个 ItemsSource。我有一个案例,其中 ComboBox 项目依赖于同一行中的另一个单元格。我设法在 PreparingCellForEdit 事件中填充 ItemsSource。但是,它不能按预期工作。最初,此列中的所有单元格都是空的。一旦我为该列的 ComboBox 填充了 ItemsSource,所有相关的单元格(具有相同的项目源)都将显示值。但是,如果我单击另一种类型的单元格(填充了不同的项目源),所有值都会消失,新类型的单元格会显示值。您只能为一列使用一组 Items Source?我不敢相信这是真的。我错过了什么吗?有什么解决方法吗?

【问题讨论】:

【参考方案1】:

感谢乔纳森的例子,我解决了我的问题如下。我修改了乔纳森的代码以突出显示我的解决方案。我从他的示例中删除了 Territory 属性,因为我的问题不需要它。

有两列。第一列是状态。第二列是 StateCandidate。 State 列绑定到 States 列表,StateCandidate 列绑定到 StateCandidates 列表。关键是StateCandidates 列表在State 更改时重新创建。因此,每行中可能会有不同的 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 列表,直到选择了不同的行,这是我将要解决的一个单独问题。有人知道我如何强制提交吗?

再次感谢 Jonathan 的启发。我会继续寻找更好的解决方案。

【讨论】:

你正走在与我完全一样的道路上。但既然你坚持,这里的代码:scottlogic.co.uk/blog/colin/tag/ieditableobject @Jonathan:非常感谢您提供的链接。但是,我无法为这个示例工作,可能是因为我不使用 DataTable。在为此苦苦挣扎的同时,我在该博客中发现了一条指向 codefluff.blogspot.com/2010/05/… 的评论。这个解决方案效果很好。 我的道歉,我发布了错误的链接。我也没有使用 DataTable,您的链接实际上是我最终使用的链接。【参考方案2】:

您可能无法可靠地做到这一点。网格可以重复使用组合框或随机创建/销毁它。

碰巧我正好在做这件事的屏幕上工作。鉴于这些...

网格中的每一行都绑定到 Trade 类型的对象。 每个行业都有一个国家财产 每个 Trade 都有一个 TerritoryCanidates 属性 更改 State 属性将导致 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

【讨论】:

我很困惑...它对你有用吗?我只是试了一下,无法让它工作。我尝试的是这样的:我为 ItemsSource 绑定了一个新集合。当另一个属性更改时,将重新创建此集合。我在组合框下拉列表中看不到任何内容。 奇怪,内置组合框列似乎不像我预期的那样工作。我使用模板列重新完成了我的示例,就像我的真实程序使用的那样。 P.S.我现在正式讨厌 DataGrid 用于编辑数据。相反,我将只使用 ItemsControl。 受你的例子启发,我现在让它工作。由于此处的空间限制,我将发布我的代码作为答案。非常感谢您的帮助!

以上是关于如何获取 WPF DataGrid 的单元格级别组合框?的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的 DataGrid 中获取所选单元格的当前列(C# WPF 应用程序)

C# WPF DataGrid获取单元格并改变背景色

WPF 获取DataGrid 控件选中的单元格信息

获取wpf datagrid单元格编辑事件

wpf datagrid 单元格如何默认单击一次点中

WPF的DataGrid中如何获取当前被选定的行的第一个单元格的值?