如何获取 WPF DataGrid 的单元格级别 ComboBox?

发布于 2024-09-25 18:33:50 字数 306 浏览 7 评论 0原文

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

It looks WFP DataGridComboBoxColumn is using a single ItemsSource for all cells in this column. I have a case where the ComboBox items are dependent on the other cell in the same row. I managed to populate the ItemsSource in PreparingCellForEdit event. However, it doesn't work as desired. Initially, all cells in this column is empty. Once I populate the ItemsSource for this column's ComboBox, all related cells (with the same items source) are showing values. However, if I click another type of cell (a different items source is populated), all values disappear and the new type cells show values. You can only use one set of Items Source for a column? I can't believe it is true. Did I miss anything? Any workaround?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

短暂陪伴 2024-10-02 18:33:50

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

一次偶然的机会,我正好在一个可以实现这一功能的屏幕上工作。鉴于这些...

  • 网格中的每一行都绑定到 Trade 类型的对象。
  • 每个 Trade 都有一个 State 属性
  • 每个 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

You probably can't do it reliably. The grid may reuse the combo box or randomly create/destroy it.

By chance I just happen to be working on a screen that does just that. Given these...

  • Each row in the grid is bound to an object of type Trade.
  • Each Trade has a State property
  • Each Trade has a TerritoryCanidates property
  • Changing the State property will cause the TerritoryCanidates property to change

This gives me the ability to bind the ItemsSource to the TerritoryCanidates property. Which in turn the DataGrid will honor under all circumstances.


<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
若无相欠,怎会相见 2024-10-02 18:33:50

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

有两列。第一列是状态。第二列是 StateCandidate。 State 列绑定到 States 列表,StateCandidate 列绑定到 StateCandidates 列表。关键点是,当 State 更改时,会重新创建 StateCandidates 列表。因此,每行中可能有不同的 StateCandidates 列表(基于所选的 State)。

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 列表,直到选择不同的行,这是我将要解决的一个单独问题。有人知道我如何强制提交吗?

再次感谢乔纳森的灵感。我会继续寻找更好的解决方案。

Thanks to Jonathan's example, I resolved my problem as follows. I modifiedy Jonathan's code to highlight my solution. I removed the Territory property from his example because I don't need it for my problem.

There are two columns. First column is State. Second column is StateCandidate. State column is bind to a States list, and StateCandidate column is bind to a StateCandidates list. The key point is that the StateCandidates list is recreated when State is changed. So, there could be a different list of StateCandidates in each row (based on the selected State).

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

Note that, when State is changed, it won't update StateCandidates list until a different row is selected, which is a separated issue I'll be fighting. Do anybody know how I can force commit?

Thanks to Jonathan again for his inspiration. I'll keep looking for a better solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文