【问题标题】:WPF .Net 4.0 Datagrid, cascading comboboxes not cascading on cell update, only after row updateWPF .Net 4.0 Datagrid,级联组合框在单元格更新时不级联,仅在行更新后
【发布时间】:2011-07-15 02:10:31
【问题描述】:

我是 WPF 新手,我发现 WPF .Net 4.0 Datagrid 似乎无法处理级联组合框。只有在从一行中移除焦点后,单元格才会正确填充选择框的正确数据。实际上,我可以看到在失去对行的焦点后触发的调试点,但在失去对单元格的焦点时却看不到。

这种行为似乎与以前的 WPF 工具包 Datagrid 不同,它一切都按预期进行。

这里明显的解决方案是使用 WPF 工具包,但这是 .Net 4.0 中的一个新项目,因此向后退是没有意义的(也许我会重新考虑这个问题)。我也理解 WPF 工具包有它自己的缺陷,这需要我学习和解决这些问题。

我几乎在网上搜索了许多资源,但运气不佳。一个反复出现的主题似乎是,单元格不是创建这种情况的可视化树的一部分(不确定这是否有效)。

非常感谢我可能错过的活动或工作示例的任何帮助。

提前致谢。

场景

WPF .Net 4.0 数据网格。

  1. 从第 1 行开始。
  2. 双击国家单元格,将中国更改为美国
  3. 双击 City 单元格,注意 Cities 仍为中国(非预期)
  4. 将焦点移至第 2 行。
  5. 再次双击第 1 行的 City 单元格,注意 Cities 已更新。现在可以选择纽约和华盛顿。

WPF 工具包

  1. 从第 1 行开始。
  2. 双击国家单元格,将中国更改为美国
  3. 双击 City 单元格,注意 Cities 代表美国(预期)

减去 WPF Toolkit 使用情况,两者的代码几乎相同(示例来自 Jialiang's Blog

代码

WPF .Net 4.0

<Window x:Class="CSWPFCascadeDataGridComboBoxColumns.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CSWPFCascadeDataGridComboBoxColumns"    
Title="Cascade DataGridComboBoxColumns" Height="300" Width="300" Loaded="Window_Loaded">
<DockPanel LastChildFill="True">
    <DataGrid Name="dataGrid" ItemsSource="{Binding}" 
                      AutoGenerateColumns="False" 
                      PreparingCellForEdit="datagrid_PreparingCellForEdit">
        <DataGrid.Columns>
            <DataGridComboBoxColumn x:Name="column1" Width="80"/>
            <DataGridComboBoxColumn x:Name="column2" Width="80"/>               
        </DataGrid.Columns>
    </DataGrid>
</DockPanel>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public enum Country
    {
        China,
        UnitedStates
    }
    public enum ChinaCity
    {
        Beijing,
        Shanghai
    }
    public enum UnitedStatesCity
    {
        NewYork,
        Washington
    }

    DataTable table = null;
    string[] strChinaCities, strUnitedStateCities;

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        /////////////////////////////////////////////////////////////////
        // get all enumeration values of type enum Country
        //
        Array countries = Enum.GetValues(typeof(Country));

        /////////////////////////////////////////////////////////////////
        // copy all Country enumeration values to a string array
        //
        string[] strCountries = new string[countries.Length];
        for (int i = 0; i < countries.Length; i++)
        {
            strCountries[i] = (countries as Country[])[i].ToString();
        }

        /////////////////////////////////////////////////////////////////
        // get all enumeration values of type enum ChinaCity
        //
        Array chinaCities = Enum.GetValues(typeof(ChinaCity));

        /////////////////////////////////////////////////////////////////
        // copy all ChinaCity enumeration values to a string array
        //
        strChinaCities = new string[chinaCities.Length];
        for (int i = 0; i < chinaCities.Length; i++)
        {
            strChinaCities[i] = (chinaCities as ChinaCity[])[i].ToString();
        }

        /////////////////////////////////////////////////////////////////
        // get all enumeration values of type enum UnitedStatesCity
        //
        Array unitedStateCities = Enum.GetValues(typeof(UnitedStatesCity));

        /////////////////////////////////////////////////////////////////
        //copy all UnitedStateCity enumeration values to a string array
        //
        strUnitedStateCities = new string[unitedStateCities.Length];
        for (int i = 0; i < unitedStateCities.Length; i++)
        {
            strUnitedStateCities[i] = (unitedStateCities as UnitedStatesCity[])[i].ToString();
        }

        //////////////////////////////////////////////////////////////////
        // combine both the two city enumeration value into one string array
        //
        string[] strAllCities = new string[strChinaCities.Length + strUnitedStateCities.Length];
        strChinaCities.CopyTo(strAllCities, 0);
        strUnitedStateCities.CopyTo(strAllCities, strChinaCities.Length);

        ///////////////////////////////////////////////////////////////////////////////
        // data bind the two DataGridComboBoxColumn's ItemsSource property respectively
        //
        BindingOperations.SetBinding(this.column1, DataGridComboBoxColumn.ItemsSourceProperty,
            new Binding() { Source = strCountries });
        BindingOperations.SetBinding(this.column2, DataGridComboBoxColumn.ItemsSourceProperty,
            new Binding() { Source = strAllCities });

        /////////////////////////////////////////////////////////////////
        // create a DataTable and add two DataColumn into it
        //
        table = new DataTable();
        table.Columns.Add("Country");
        table.Columns.Add("City");

        /////////////////////////////////////////////////////////////////
        // add a DataRow into this DataTable
        //
        table.Rows.Add(new object[] { "China", "Beijing" });

        /////////////////////////////////////////////////////////////////
        // set the DataContext property of the DataGrid to the DataTable
        //
        this.dataGrid.DataContext = table;

        /////////////////////////////////////////////////////////////////
        // set the Header of both DataGridComboBoxColumn and bind the
        // SelectedItemBinding property of both DataGridComboBoxColumn
        this.column1.Header = "Country";
        this.column1.SelectedItemBinding = new Binding("Country");
        this.column2.Header = "City";
        this.column2.SelectedItemBinding = new Binding("City");

    }

    /// <summary>
    /// this PreparingCellForEdit event handler gets the hosted editing ComboBox control 
    /// and bind its ItemsSource property according to the value of the Country
    /// </summary>             
    private void datagrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
    {
        if (e.Column.Header.Equals("City"))
        {
            ComboBox cboEditingElement = e.EditingElement as ComboBox;
            if ((e.Row.Item as DataRowView)["Country"].Equals("China"))
            {
                //////////////////////////////////////////////////////////////////////////
                // bind the ItemsSource property of the cmbEditingElement to China city
                // string array if the selected country is China
                //
                BindingOperations.SetBinding(cboEditingElement, ComboBox.ItemsSourceProperty,
                    new Binding() { Source = strChinaCities });
            }
            else
            {
                //////////////////////////////////////////////////////////////////////////
                // bind the ItemsSource property of the cmbEditingElement to United State
                // city string array if the selected country is United State
                //
                BindingOperations.SetBinding(cboEditingElement, ComboBox.ItemsSourceProperty,
                    new Binding() { Source = strUnitedStateCities });
            }
        }
    }
}

WPF 工具包代码

MainWindow.xaml

<Window x:Class="CSWPFCascadeDataGridComboBoxColumns.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CSWPFCascadeDataGridComboBoxColumns"
xmlns:toolkit ="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="Cascade DataGridComboBoxColumns" Height="300" Width="300" Loaded="Window_Loaded">
<DockPanel LastChildFill="True">
    <toolkit:DataGrid Name="dataGrid" ItemsSource="{Binding}" 
                      AutoGenerateColumns="False" 
                      PreparingCellForEdit="datagrid_PreparingCellForEdit">
        <toolkit:DataGrid.Columns>
            <toolkit:DataGridComboBoxColumn x:Name="column1" Width="80"/>
            <toolkit:DataGridComboBoxColumn x:Name="column2" Width="80"/>               
        </toolkit:DataGrid.Columns>
    </toolkit:DataGrid>
</DockPanel>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public enum Country
    {
        China,
        UnitedStates
    }
    public enum ChinaCity
    {
        Beijing,
        Shanghai
    }
    public enum UnitedStatesCity
    {
        NewYork,
        Washington
    }

    DataTable table = null;
    string[] strChinaCities, strUnitedStateCities;

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        /////////////////////////////////////////////////////////////////
        // get all enumeration values of type enum Country
        //
        Array countries = Enum.GetValues(typeof(Country));

        /////////////////////////////////////////////////////////////////
        // copy all Country enumeration values to a string array
        //
        string[] strCountries = new string[countries.Length];
        for (int i = 0; i < countries.Length; i++)
        {
            strCountries[i] = (countries as Country[])[i].ToString();
        }

        /////////////////////////////////////////////////////////////////
        // get all enumeration values of type enum ChinaCity
        //
        Array chinaCities = Enum.GetValues(typeof(ChinaCity));

        /////////////////////////////////////////////////////////////////
        // copy all ChinaCity enumeration values to a string array
        //
        strChinaCities = new string[chinaCities.Length];
        for (int i = 0; i < chinaCities.Length; i++)
        {
            strChinaCities[i] = (chinaCities as ChinaCity[])[i].ToString();
        }

        /////////////////////////////////////////////////////////////////
        // get all enumeration values of type enum UnitedStatesCity
        //
        Array unitedStateCities = Enum.GetValues(typeof(UnitedStatesCity));

        /////////////////////////////////////////////////////////////////
        //copy all UnitedStateCity enumeration values to a string array
        //
        strUnitedStateCities = new string[unitedStateCities.Length];
        for (int i = 0; i < unitedStateCities.Length; i++)
        {
            strUnitedStateCities[i] = (unitedStateCities as UnitedStatesCity[])[i].ToString();
        }

        //////////////////////////////////////////////////////////////////
        // combine both the two city enumeration value into one string array
        //
        string[] strAllCities = new string[strChinaCities.Length + strUnitedStateCities.Length];
        strChinaCities.CopyTo(strAllCities, 0);
        strUnitedStateCities.CopyTo(strAllCities, strChinaCities.Length);

        ///////////////////////////////////////////////////////////////////////////////
        // data bind the two DataGridComboBoxColumn's ItemsSource property respectively
        //
        BindingOperations.SetBinding(this.column1, DataGridComboBoxColumn.ItemsSourceProperty,
            new Binding() { Source = strCountries });
        BindingOperations.SetBinding(this.column2, DataGridComboBoxColumn.ItemsSourceProperty,
            new Binding() { Source = strAllCities });

        /////////////////////////////////////////////////////////////////
        // create a DataTable and add two DataColumn into it
        //
        table = new DataTable();
        table.Columns.Add("Country");
        table.Columns.Add("City");

        /////////////////////////////////////////////////////////////////
        // add a DataRow into this DataTable
        //
        table.Rows.Add(new object[] { "China", "Beijing" });

        /////////////////////////////////////////////////////////////////
        // set the DataContext property of the DataGrid to the DataTable
        //
        this.dataGrid.DataContext = table;

        /////////////////////////////////////////////////////////////////
        // set the Header of both DataGridComboBoxColumn and bind the
        // SelectedItemBinding property of both DataGridComboBoxColumn
        this.column1.Header = "Country";
        this.column1.SelectedItemBinding = new Binding("Country");
        this.column2.Header = "City";
        this.column2.SelectedItemBinding = new Binding("City");

    }

    /// <summary>
    /// this PreparingCellForEdit event handler gets the hosted editing ComboBox control 
    /// and bind its ItemsSource property according to the value of the Country
    /// </summary>             
    private void datagrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
    {
        if (e.Column.Header.Equals("City"))
        {
            ComboBox cboEditingElement = e.EditingElement as ComboBox;
            if ((e.Row.Item as DataRowView)["Country"].Equals("China"))
            {
                //////////////////////////////////////////////////////////////////////////
                // bind the ItemsSource property of the cmbEditingElement to China city
                // string array if the selected country is China
                //
                BindingOperations.SetBinding(cboEditingElement, ComboBox.ItemsSourceProperty,
                    new Binding() { Source = strChinaCities });
            }
            else
            {
                //////////////////////////////////////////////////////////////////////////
                // bind the ItemsSource property of the cmbEditingElement to United State
                // city string array if the selected country is United State
                //
                BindingOperations.SetBinding(cboEditingElement, ComboBox.ItemsSourceProperty,
                    new Binding() { Source = strUnitedStateCities });
            }
        }
    }
}

【问题讨论】:

    标签: wpf datagrid combobox wpftoolkit cascadingdropdown


    【解决方案1】:

    我总是很惊讶,一个人花这么多时间研究一个问题,却只向社区提出问题,然后在 60 分钟内找到答案。因此,作为 WPF 的新手,肯定会遇到很多困难。

    显然它就像在所选项目的绑定上设置UpdateSourceTrigger=PropertyChanged一样简单。

    来自微软:

    DataGrid 的编辑模板中的绑定通常会将其 UpdateSourceTrigger 设置为 Explicit,以便在用户提交行之前不会更新源属性。您可以通过在 ComboBox.SelectedItem 的绑定上设置 UpdateSourceTrigger=PropertyChanged 来覆盖它。"

    希望我以外的其他人会发现这很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-31
      • 2014-10-09
      • 2021-06-19
      • 2017-10-24
      • 2011-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多