从另一个DataGridCell访问DataGridCell的子节点?

时间:2010-07-10 21:31:36

标签: wpf datagrid binding datagridcell

我有一个包含ComboBox的DataGridCell。

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

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

非常感谢!

  

相关:Accessing control between DataGridCells, dynamic cascading ComboBoxes

1 个答案:

答案 0 :(得分:4)

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

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

创建一个表示行数据的ViewModel(一个围绕数据对象的简单包装器)。将目标ComboBox的ItemsSource属性绑定到您从viewmodel提供的IEnumerable - 属性。 将SelectedItem从source-ComboBox绑定到ViewModel的另一个属性。每次在ViewModel中更改此源属性时,都会更改ViewModel提供的列表内容。

用于desintation(list)属性a 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的resources部分,而不是面板,然后您可以直接访问它们。无论如何,IMO是这个位置比网格的资源容器更合适。

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

How to get logical tree of a DataTemplate