【问题标题】:Access DataGridCell's children from another DataGridCell?从另一个 DataGridCell 访问 DataGridCell 的子级?
【发布时间】:2010-07-10 21:31:35
【问题描述】:

我有一个包含 ComboBox 的 DataGridCell。

我希望,当我触发它的“SelectionChanged”事件时,不同列的 CollectionViewSource(最终 - 在运行时,单元格)CellEditingTemplate 的资源应该根据该行的选定值填充数据。

也许是DataTrigger、ActionTrigger、EventTrigger,也许是代码、XAML 我不在乎,我只需要一个解决方案。

非常感谢!

相关:Accessing control between DataGridCells, dynamic cascading ComboBoxes

【问题讨论】:

    标签: wpf datagrid binding datagridcell


    【解决方案1】:

    如果我正确理解您的问题,您将根据在 DataGrid 同一行中的另一个单元格中选择组合框来填充单元格中组合框的内容。 如果是:

    第一个解决方案(IMO 更可取)

    创建一个表示行数据的 ViewModel(数据对象的简单包装器)。将目标 ComboBox 的 ItemsSource-property 绑定到您从视图模型中提供的 IEnumerable-property。 将 Source-ComboBox 中的 SelectedItem 绑定到 ViewModel 的另一个属性。每次 ViewModel 中的源属性发生更改时,都会更改 ViewModel 提供的列表的内容。

    ObservableCollection<T> 用于目标(列表)属性。源属性由您决定。

    这是一个大概的例子。我将类称为 VM(用于 ViewModel),但这对您当前的解决方案没有任何改变。 MVVM 也可以部分使用。

    public class DataObjectVM : DependencyObject {
    
        public static readonly DependencyProperty SelectedCategoryProperty =
            DependencyProperty.Register("SelectedCategory", typeof(CategoryClass), typeof(DataObjectVM), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,delegate (DependencyObject d,DependencyPropertyChangedEventArgs e){
                ((DataObjectVM)d).SelectedCategoryChanged(e);
            }));
    
        ObservableCollection<ItemClass> _items=new ObservableCollection<ItemClass>();
    
    
        void SelectedCategoryChanged(DependencyPropertyChangedEventArgs e) {
    
            // Change here the contents of the _items collection. 
            // The destination ComboBox will update as you desire
            // Do not change the _items reference. Only clear, add, remove or
            //  rearange the collection-items
    
        }
    
        // Bind the destination ComboxBox.ItemsSource to this property
        public IEnumerable<ItemClass> DestinationItems {
            get {
                return _items;
            }
        }
        // Bind to this property with the source ComboBox.SelectedItem
        public CategoryClass SelectedCategory {
            get { return (CategoryClass)GetValue(SelectedCategoryProperty); }
            set { SetValue(SelectedCategoryProperty, value); }
        }
    
    }
    

    向此类添加一个构造函数,该构造函数获取您的数据对象,并将一些包装器属性设置为您需要在 DataGrid 中提供的其余属性。如果它们很多,您还可以创建一个属性来提供您的数据对象并直接绑定到它。不好,但它会完成这项工作。 您还可以(必须)使用业务对象中的数据预初始化 SelectedCategory。在构造函数中也这样做。 作为 DataGrid 的 ItemsSource,您可以提供 DataObjectVM 类的 IEnumerable,它包含您要显示的所有项目。


    VisualTreeHelper 的替代方法

    如果您想手动操作,请在 ComboBox.SelectionChangedEvent 处理程序后面的代码中注册,然后更改目标 ComboBox 手册的 ItemsSource。您将通过 EventArgs 获得的业务对象。您必须在可视化树中搜索的目标 ComboBox(使用 VisualTreeHelper)。如果您使用DataGridTemplateColumn 类并使用相应的组合框添加DataTemplate,也可以连接事件。

    但我认为这真的不是很简单,而且很容易出错。上面的解决方案要容易得多。

    这是您可能正在寻找的代码:

    private void CboSource_SelectionChanged(object sender, SelectionChangedEventArgs e) {
        ComboBox cbo = (ComboBox)sender;
        FrameworkElement currentFe = VisualTreeHelper.GetParent(cbo) as FrameworkElement;
        while (null != currentFe && !(currentFe is DataGridRow)) {
            currentFe = VisualTreeHelper.GetParent(currentFe) as FrameworkElement;
        }
        if (null != currentFe) {
            List<ComboBox> list = new List<ComboBox>();
            FindChildFrameworkElementsOfType<ComboBox>(currentFe,list);
            // Requirement 1: Find ComboBox
            foreach (ComboBox cboFound in list) {
                if (cboFound.Name == "PART_CboDestination") {
                    // This is the desired ComboBox
                    // Your BO is available through cbo.Found.DataContext property
                    // If don't like to check the name, you can also depend on the
                    // sequence of the cbo's because I search them in a deep search
                    // operation. The sequence will be fix.
                }
            }
    
            List<DataGridCell> cells = new List<DataGridCell>();
            FindChildFrameworkElementsOfType<DataGridCell>(currentFe,cells);
            // Requirement 2: Find Sibling Cell
            foreach (DataGridCell cell in cells) {
                // Here you have the desired cell of the other post
                // Take the sibling you are interested in
                // The sequence is as you expect it
    
                DataGridTemplateColumn col=cell.Column as DataGridTemplateColumn;
                DataTemplate template = col.CellTemplate;
    
                // Through template.Resources you can access the CollectionViewSources
                // if they are placed in the CellTemplate.
                // Change this code if you will have an edit cell template or another
                // another construction
    
            }
        }            
    }
    
    void FindChildFrameworkElementsOfType<T>(DependencyObject parent,IList<T> list) where T: FrameworkElement{            
        DependencyObject child;
        for(int i=0;i< VisualTreeHelper.GetChildrenCount(parent);i++){            
            child = VisualTreeHelper.GetChild(parent, i);
            if (child is T) {
                list.Add((T)child);
            }
            FindChildFrameworkElementsOfType<T>(child,list);
        }
    }
    

    这是我使用的标记:

    <DataGrid.Columns>                        
        <DataGridTemplateColumn Header="Source" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox Name="PART_CboSource" SelectionChanged="CboSource_SelectionChanged" ItemsSource="!!YOUR ITEMS SOURCE!!" SelectedItem="{Binding Category}">                            
                    </ComboBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Destination">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox Name="PART_CboDestination"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
    

    访问 CollectionViewSource

    要访问 CollectionViewSource,请将其放入相应 DataTemplate 的资源部分,而不是面板,然后您将可以直接访问它们。无论如何,IMO 这个位置比网格的资源容器更合适。

    如果您不想这样做,请检查以下帖子的状态:

    How to get logical tree of a DataTemplate

    【讨论】:

    • 很遗憾我没有在这个项目中使用 MVVM,也没有使用 MVVM 的经验。可以用普通的WPF来做吗?因为我的时间限制不允许我将整个项目重新创建为 MVVM 或学习 MVVM。但是,是的,你理解得很好,这是我的愿望。
    • 我的问题是关于第二个选项。我的问题只是查找部分,我不知道如何找到表亲组合框,我不想有一个硬编码的地址表,我仍然想以某种方式保持动态......
    • +1,听起来不错,看起来您正在接近我需要的东西,感谢您更新您的答案。仍然没有解决的是我想在(第二个)DataTemplate.Resources 中有 CollectionViewSource,而不是 Control。将帮助我搜索 CollectionViewSource 资源。
    • 另外,我说的是 CellEditingTemplate。
    • 好的,所需的兄弟姐妹也在其中。如果不可用,则从单元格模板中获取编辑模板。其余的应该很容易
    猜你喜欢
    • 1970-01-01
    • 2012-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多