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

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

【问题讨论】:

    标签: wpf datagrid combobox itemssource


    【解决方案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。
    • 受你的例子启发,我现在让它工作。由于此处的空间限制,我将发布我的代码作为答案。非常感谢您的帮助!
    猜你喜欢
    • 2012-10-06
    • 2012-02-18
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多