0

我是 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 的博客

代码

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>

主窗口.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 工具包代码

主窗口.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>

主窗口.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 });
            }
        }
    }
}
4

1 回答 1

3

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

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

来自微软:

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

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

于 2011-03-14T15:32:44.287 回答