Подкласс DataGridBoundColumn в Silverlight

Я пытаюсь создать подкласс DataGridBoundColumn, чтобы создать тот, который отображает ComboBox. API отражает ComboBox:

<sdk:DataGrid x:Name="dataGrid1" 
              AutoGenerateColumns="False" 
              ItemsSource="{Binding Items}">
    <sdk:DataGrid.Columns>
        <controls:DataGridComboBoxColumn 
            Binding="{Binding SalutationId}" 
            SelectedValuePath="SalutationId"
            DisplayMemberPath="SalutationName"
            ItemsSource="{Binding Salutations, ElementName=UserControl}"
            Header="Salutation"
            />
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

Необъяснимо, что привязка ItemsSource с использованием XAML не работает, но делает это программно - т.е.

    private void BindSalutations()
    {
        SalutationColumn = (DataGridComboBoxColumn) this.dataGrid1.Columns[1];
        BindingOperations.SetBinding(
            SalutationColumn, 
            DataGridComboBoxColumn.ItemsSourceProperty, 
            new Binding("Salutations") { Source = this });
    }

Я убедился, что ItemsSource определен как DependencyProperty, но безрезультатно. Вот моя реализация:

using System;
using System.Collections;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SlDataGridColumn.Controls
{
    public class DataGridComboBoxColumn: DataGridBoundColumn
    {
        public DataGridComboBoxColumn()
            : base()
        {

        }

        #region ItemsSource Dependency Property
#if false
        private IEnumerable _itemsSource;

        public IEnumerable ItemsSource
        {
            get { return _itemsSource; }
            set { _itemsSource = value; base.NotifyPropertyChanged("ItemsSource"); }
        }

#else
        /// <summary> 
        /// Get or Sets the ItemsSource dependency property.  
        /// </summary> 
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); base.NotifyPropertyChanged("ItemsSource"); }
        }

        /// <summary> 
        /// Identifies the ItemsSource dependency property. This enables animation, styling, binding, etc...
        /// </summary> 
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource",
                                        typeof(IEnumerable),
                                        typeof(DataGridComboBoxColumn),
                                        new PropertyMetadata(null, OnItemsSourcePropertyChanged));

        /// <summary>
        /// ItemsSource changed handler. 
        /// </summary>
        /// <param name="d">DataGridComboBoxColumn that changed its ItemsSource.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param> 
        private static void OnItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var source = d as DataGridComboBoxColumn;
            if (source != null)
            {
                var value = (IEnumerable)e.NewValue;
                source.NotifyPropertyChanged("ItemsSource");
            }
        }
#endif
        #endregion ItemsSource Dependency Property

        #region SelectedValuePath Dependency Property

        /// <summary> 
        /// Get or Sets the SelectedValuePath dependency property.  
        /// </summary> 
        public string SelectedValuePath
        {
            get { return (string)GetValue(SelectedValuePathProperty); }
            set { SetValue(SelectedValuePathProperty, value); }
        }

        /// <summary> 
        /// Identifies the SelectedValuePath dependency property. This enables animation, styling, binding, etc...
        /// </summary> 
        public static readonly DependencyProperty SelectedValuePathProperty =
            DependencyProperty.Register("SelectedValuePath",
                                        typeof(string),
                                        typeof(DataGridComboBoxColumn),
                                        new PropertyMetadata("", OnSelectedValuePathPropertyChanged));

        /// <summary>
        /// SelectedValuePath changed handler. 
        /// </summary>
        /// <param name="d">DataGridComboBoxColumn that changed its SelectedValuePath.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param> 
        private static void OnSelectedValuePathPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var source = d as DataGridComboBoxColumn;
            if (source != null)
            {
                var value = (string)e.NewValue;
                source.NotifyPropertyChanged("SelectedValuePath");
            }
        }

        #endregion SelectedValuePath Dependency Property

        #region DisplayMemberPath Dependency Property

        /// <summary> 
        /// Get or Sets the DisplayMemberPath dependency property.  
        /// </summary> 
        public string DisplayMemberPath
        {
            get { return (string)GetValue(DisplayMemberPathProperty); }
            set { SetValue(DisplayMemberPathProperty, value); }
        }

        /// <summary> 
        /// Identifies the DisplayMemberPath dependency property. This enables animation, styling, binding, etc...
        /// </summary> 
        public static readonly DependencyProperty DisplayMemberPathProperty =
            DependencyProperty.Register("DisplayMemberPath",
                                        typeof(string),
                                        typeof(DataGridComboBoxColumn),
                                        new PropertyMetadata("", OnDisplayMemberPathPropertyChanged));

        /// <summary>
        /// DisplayMemberPath changed handler. 
        /// </summary>
        /// <param name="d">DataGridComboBoxColumn that changed its DisplayMemberPath.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param> 
        private static void OnDisplayMemberPathPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var source = d as DataGridComboBoxColumn;
            if (source != null)
            {
                var value = (string)e.NewValue;
                source.NotifyPropertyChanged("DisplayMemberPath");
            }
        }

        #endregion DisplayMemberPath Dependency Property

        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
        {
            var cb = new ComboBox();
            BindingOperations.SetBinding(cb, ComboBox.SelectedValueProperty, new Binding(this.Binding) { Source = dataItem });
            BindingOperations.SetBinding(cb, ComboBox.ItemsSourceProperty, new Binding("ItemsSource") { Source = this });
            BindingOperations.SetBinding(cb, ComboBox.DisplayMemberPathProperty, new Binding("DisplayMemberPath") { Source = this });
            BindingOperations.SetBinding(cb, ComboBox.SelectedValuePathProperty, new Binding("SelectedValuePath") { Source = this });
            return cb;
        }

        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            var cb = new ComboBox();
            cb.DataContext = dataItem;
            BindingOperations.SetBinding(cb, ComboBox.ItemsSourceProperty, new Binding("ItemsSource") { Source = this });
            BindingOperations.SetBinding(cb, ComboBox.DisplayMemberPathProperty, new Binding("DisplayMemberPath") { Source = this });
            BindingOperations.SetBinding(cb, ComboBox.SelectedValuePathProperty, new Binding("SelectedValuePath") { Source = this });
            BindingOperations.SetBinding(cb, ComboBox.SelectedValueProperty, new Binding(this.Binding.Path.Path));
            return cb;
        }

        protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs args)
        {
            return args.OriginalSource;
        }
    }
}

Пример исходного кода проекта находится здесь

1 ответ

DataGridColumn является объектом DependencyObject, а не FrameworkElement. Это различие важно для понимания проблемы. Смотрите здесь и здесь.

Следующая привязка не работает, поскольку DependencyObject не может выполнить привязку через ElementName.

<controls:DataGridComboBoxColumn 
        ItemsSource="{Binding Salutations, ElementName=UserControl}"
        />
Другие вопросы по тегам