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

时间:2010-10-01 23:17:54

标签: wpf datagrid combobox itemssource

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

2 个答案:

答案 0 :(得分:2)

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

偶然的机会我碰巧正在制作一个能做到这一点的屏幕。鉴于这些......

  • 网格中的每一行都绑定到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

答案 1 :(得分:1)

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

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

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

请注意,当State更改时,它将不会更新StateCandidates列表,直到选择了另一行,这是一个我将要解决的分离问题。有人知道我怎么强行提交吗?

再次感谢Jonathan的灵感。我会一直在寻找更好的解决方案。