Доступ к дочерним элементам DataGridCell из другого DataGridCell? - PullRequest
1 голос
/ 11 июля 2010

У меня есть DataGridCell, который содержит ComboBox.

Я хочу, чтобы, когда я запускаю событие 'SelectionChanged' этого, CollectionViewSource другого столбца (в конечном счете - во время выполнения, ячейка) Ресурсы CellEditingTemplate должны заполняться данными согласно выбранному значению для этой строки.

Может быть, DataTrigger, ActionTrigger, EventTrigger, может быть, по коду, XAML Мне все равно, мне просто нужно решение.

Большое спасибо!

Похожие: Доступ к управлению между DataGridCells, динамическое каскадирование ComboBoxes

1 Ответ

4 голосов
/ 11 июля 2010

Если я правильно понял ваш вопрос, вы заполните содержимое комбинированного списка в ячейке на основе выбора этого комбинированного списка в другой ячейке, которая находится в той же строке DataGrid. Если да:

Первое решение (ИМО предпочтительнее)

Создайте ViewModel, который представляет данные строк (простая оболочка вокруг вашего объекта данных). Свяжите свойство ItemsSource целевого ComboBox с IEnumerable -свойством, которое вы предоставляете из своей модели представления. Свяжите SelectedItem из исходного 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 с данными из вашего бизнес-объекта. Сделайте это также в конструкторе. В качестве ItemsSource для DataGrid вы предоставляете IEnumerable класса DataObjectVM, который оборачивает все элементы, которые вы хотите показать.


Альтернативный способ с VisualTreeHelper

Если вы хотите сделать это вручную, зарегистрируйтесь в коде за обработчиком для ComboBox.SelectionChangedEvent и измените затем ItemsSource для конечного руководства ComboBox. Бизнес-объект, который вы получите с EventArgs. ComboBox назначения вы должны искать в визуальном дереве (используйте VisualTreeHelper). События также могут быть связаны, если вы используете класс DataGridTemplateColumn и добавляете DataTemplate с соответствующими полями ComboBox.

Но я думаю, что это действительно не очень просто сделать и может быть подвержено ошибкам. Приведенное выше решение намного проще.

Вот код, который вы, вероятно, ищете:

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 это расположение в любом случае более подходящее, чем контейнер ресурсов в сетке.

Если вы не хотите этого делать, проверьте состояние следующего сообщения:

Как получить логическое дерево шаблона данных

...