Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WPF, Silverlight Новый топик    Ответить
 ComboBox в DataGrid  [new]
Eld Hasp
Member

Откуда:
Сообщений: 225
Попробовал создать колонку в DataGrid с ComboBox... не тут-то было.
Вроде всё просто...., но никак! Чё-то упускаю, а что не доходит.
ComboBox список выводит, но не меняется привязанный элемент и не дополняется коллекция DataGrid.
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding SelectedItem, ElementName=dataGrid}"/>
        <DataGrid x:Name="dataGrid" ItemsSource="{Binding Path=ListPropDG}" Grid.Row="1" ColumnWidth="*">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding PropInt}" IsReadOnly="True"/>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding PropString}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding Path=ListProp, ElementName=window}" 
                                      SelectedItem="{Binding Path, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  
                                      DisplayMemberPath="PropString"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
    public partial class Example : Window
    {
        public Example()
        {
            InitializeComponent();
            DataContext = this;
        }

        /// <summary>Коллекция ComboBox</summary>
        public ObservableCollection <ExampClass> ListProp { get; set; } = new ObservableCollection<ExampClass>()
                 {
                     new ExampClass("Один",1),
                     new ExampClass("Два",2),
                     new ExampClass("Три",3),
                     new ExampClass("Четыре",4),
                     new ExampClass("Пять",5)
                 };

        /// <summary>Коллекция DataGrid</summary>
        public ObservableCollection<ExampClass> ListPropDG { get; set; } = new ObservableCollection<ExampClass>()
                 {
                     new ExampClass("Три",3),
                     new ExampClass("Пять",5)
                 };

        /// <summary>Класс для примера с двумя свойствами</summary>
        public class ExampClass : OnPropertyChangedClass
        {
            private string _propString;
            private int _propInt;

            public string PropString { get => _propString; set { _propString = value; OnPropertyChanged(); } }
            public int PropInt { get => _propInt; set { _propInt = value; OnPropertyChanged(); } }
            public ExampClass() { }
            public ExampClass(string PropString, int PropInt) { this.PropString = PropString; this.PropInt = PropInt; }

            public override string ToString() => $"Int = {PropInt}, String = {PropString}";
        }
    }

И второй вопрос. Я правильно понял, что использовать колонку DataGridComboBoxColumn можно только со статическим списком?
15 дек 18, 23:59    [21765064]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4710
Eld Hasp
ComboBox список выводит, но не меняется привязанный элемент и не дополняется коллекция DataGrid

Потому что здесь вообще непонятно что творится.
Вот это
Eld Hasp
SelectedItem="{Binding Path, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  


- что? Вы биндите SelectedItem комбобокса на свойство типа, соответствующего типу генерик-параметра ItemsSource самого DataGrid - т.е. на свойство типа ExampClass. Но у класса ExampClass нет свойства Path, это во-первых. Во-вторых, свойство типа генерик-параметра исходной коллекции (ItemsSource для DataGrid), которое вы собираетесь менять, должно быть того же типа, что и SelectedItem комбобокса (а здесь явно чувствуется попытка прикрутить SelectedItem c типом ExampClass к свойству с типом int). В противном случае для биндинга нужно задавать конвертер - в данном случае конвертер между ExampClass и int.
Eld Hasp
И второй вопрос. Я правильно понял, что использовать колонку DataGridComboBoxColumn можно только со статическим списком?

Что подразумевается под "статическим списком"?
16 дек 18, 06:35    [21765156]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Eld Hasp
Member

Откуда:
Сообщений: 225
Сон Веры Павловны
- что? Вы биндите SelectedItem комбобокса на свойство типа, соответствующего типу генерик-параметра ItemsSource самого DataGrid - т.е. на свойство типа ExampClass. Но у класса ExampClass нет свойства Path, это во-первых.
Делал без Path, но выходила ошибка с указанием поставить Path.... Ну, и поставил, но работать всё равно не стало.
Сон Веры Павловны
Во-вторых, свойство типа генерик-параметра исходной коллекции (ItemsSource для DataGrid), которое вы собираетесь менять, должно быть того же типа, что и SelectedItem комбобокса (а здесь явно чувствуется попытка прикрутить SelectedItem c типом ExampClass к свойству с типом int). В противном случае для биндинга нужно задавать конвертер - в данном случае конвертер между ExampClass и int.
Может я не правильно понимаю - поправьте.
В биндинг передаётся элемент коллекции ItemsSource DataGrid. В данном случае тип этого элемента ExampClass. В режиме отображения TextBlock я привязал к свойству PropString этого класса. В режиме редактирования идея была в том, чтобы ComboBox заменял сам элемент элементом из своего списка. Не одно из свойств, а весь элемент. Свойство PropInt элемента отображается в соседней колонке. Эта колонка недоступна для редактирования.
Я понимаю, что что-то неправильно здесь делаю. Но конвертер здесь для чего? По замыслу ComboBox.SelectedItem и элемент DataGrid.ItemsSource одного типа.
Сон Веры Павловны
Eld Hasp
И второй вопрос. Я правильно понял, что использовать колонку DataGridComboBoxColumn можно только со статическим списком?

Что подразумевается под "статическим списком"?
Статический, то есть неизменяемый. В инете примеры которые видел все с привязкой ItemsSource к StaticResource или прямой инициализацией списка в XAML. Попробовал привязать к списочному свойству - ничего не показывает.
16 дек 18, 11:25    [21765188]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Eld Hasp
Member

Откуда:
Сообщений: 225
Чуть более развёрнутый пример.
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding SelectedItem, ElementName=dataGrid}"/>
        <DataGrid x:Name="dataGrid" ItemsSource="{Binding Path=ListPropDG}" Grid.Row="1" ColumnWidth="*">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding PropValue}"/>
                <DataGridTextColumn Binding="{Binding PropCombo.PropInt}" IsReadOnly="True"/>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding PropString}" FontSize="{Binding PropCombo.PropString}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding ListProp, ElementName=window}" 
                                      SelectedItem="{Binding PropCombo}"  
                                      DisplayMemberPath="PropString"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
    public partial class Example : Window
    {
        public Example()
        {
            InitializeComponent();
            DataContext = this;
        }

        /// <summary>Коллекция ComboBox</summary>
        public ObservableCollection <ExampClass> ListProp { get; set; } = new ObservableCollection<ExampClass>()
                 {
                     new ExampClass("Один",1),
                     new ExampClass("Два",2),
                     new ExampClass("Три",3),
                     new ExampClass("Четыре",4),
                     new ExampClass("Пять",5)
                 };

        /// <summary>Коллекция DataGrid</summary>
        public ObservableCollection<ExampTwoClass> ListPropDG { get; set; } = new ObservableCollection<ExampTwoClass>()
                 {
                    new ExampTwoClass("One", new ExampClass("Три",3)),
                    new ExampTwoClass("Two", new ExampClass("Пять",5))
                 };

        /// <summary>Класс для примера с двумя свойствами</summary>
        public class ExampTwoClass : OnPropertyChangedClass
        {
            private string _propValue;
            private ExampClass _propCombo;

            public string PropValue { get => _propValue; set { _propValue = value; OnPropertyChanged(); } }
            public ExampClass PropCombo { get => _propCombo; set { _propCombo = value; OnPropertyChanged(); } }
            public ExampTwoClass() { }
            public ExampTwoClass(string PropString, ExampClass PropCombo) { this.PropValue = PropString; this.PropCombo = PropCombo; }

            public override string ToString() => $"String = {PropValue}, PropCombo = {{{PropCombo.ToString()}}}";
        }

        /// <summary>Класс для примера с двумя свойствами</summary>
        public class ExampClass : OnPropertyChangedClass
        {
            private string _propString;
            private int _propInt;

            public string PropString { get => _propString; set { _propString = value; OnPropertyChanged(); } }
            public int PropInt { get => _propInt; set { _propInt = value; OnPropertyChanged(); } }
            public ExampClass() { }
            public ExampClass(string PropString, int PropInt) { this.PropString = PropString; this.PropInt = PropInt; }

            public override string ToString() => $"Int = {PropInt}, String = {PropString}";
        }
    }
16 дек 18, 11:29    [21765189]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Eld Hasp
Member

Откуда:
Сообщений: 225
Ох! Пропустил одну строчку - не исправил.
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding PropCombo.PropString}" FontSize="{Binding PropCombo.PropString}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
16 дек 18, 11:32    [21765190]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Eld Hasp
Member

Откуда:
Сообщений: 225
Во втором примере, работает как задумывалось. А первый, вроде тоже самое - не работает. Как-то не так привязки указываю. На мой взгляд, оба примера одно и тоже.
16 дек 18, 11:37    [21765192]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Eld Hasp
Member

Откуда:
Сообщений: 225
Если из первого примера убрать Path, то при попытке редактирования строки выходит такая ошибка (на скриншоте)
        <DataGrid x:Name="dataGrid" ItemsSource="{Binding Path=ListPropDG}" Grid.Row="1" ColumnWidth="*">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding PropInt}" IsReadOnly="True"/>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding PropString}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding ListProp, ElementName=window}" 
                                      SelectedItem="{Binding}"  
                                      DisplayMemberPath="PropString"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>


К сообщению приложен файл. Размер - 10Kb
16 дек 18, 11:49    [21765198]     Ответить | Цитировать Сообщить модератору
 Re: ComboBox в DataGrid  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 4710
Eld Hasp,

Вот вполне работающий пример - разбирайтесь, там всё вполне очевидно.
+ Код
public partial class MainWindow
{
  public MainWindow()
  {
    InitializeComponent();
    DataContext = this;
  }

  public ObservableCollection<DiaryRecord> Diary { get; } = new ObservableCollection<DiaryRecord>{ new DiaryRecord() };
  public ObservableCollection<DiaryRecord2> Diary2 { get; } = new ObservableCollection<DiaryRecord2> { new DiaryRecord2() };
}

public class DayOfWeekX
{
  public DayOfWeekX():this(0) { }

  public DayOfWeekX(int dayOfWeek)
  {
    N = dayOfWeek;
    Name = DayName(dayOfWeek);
  }

  public int N { get; }
  public string Name { get; }

  public override bool Equals(object obj) => obj is DayOfWeekX dx && dx.N==N;
  public override int GetHashCode() => N.GetHashCode();
  public override string ToString() => $"[{GetType().Name}] {Name} ({N})";

  static readonly Dictionary<int, string> days = new Dictionary<int, string>
  {
    { 0, "Понедельник" },
    { 1, "Вторник" },
    { 2, "Среда" },
    { 3, "Четверг" },
    { 4, "Пятница" },
    { 5, "Суббота" },
    { 6, "Воскресенье" }
  };

  public static IReadOnlyCollection<DayOfWeekX> DaysOfWeek { get; }
  public static string DayName(int dayOfWeek) => days[dayOfWeek];

  static DayOfWeekX()
    => DaysOfWeek = days.Select(kvp => new DayOfWeekX(kvp.Key))
      .OrderBy(d => d.N).ToList();
}

public class DiaryRecordBase : INotifyPropertyChanged
{
  protected DiaryRecordBase() => Description = string.Empty;

  string _description;
  public string Description
  {
    get => _description;
    set
    {
      _description = value;
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public class DiaryRecord: DiaryRecordBase
{
  public DiaryRecord() => Day = new DayOfWeekX();

  DayOfWeekX _day;
  public DayOfWeekX Day
  {
    get => _day;
    set
    {
      _day = value;
      OnPropertyChanged();
    }
  }
}

public class DiaryRecord2 : DiaryRecordBase
{
  public IReadOnlyCollection<DayOfWeekX> Days => 
    Day%2==0
      ? DayOfWeekX.DaysOfWeek
      : DayOfWeekX.DaysOfWeek.OrderByDescending(d=>d.N).ToList();

  int _day;
  public int Day
  {
    get => _day;
    set
    {
      _day = value;
      OnPropertyChanged();
      OnPropertyChanged(nameof(DayName));
      Application.Current.Dispatcher.
        BeginInvoke(DispatcherPriority.ContextIdle,
        (Action)(()=> OnPropertyChanged(nameof(Days))));
    }
  }

  public string DayName => DayOfWeekX.DayName(Day);
}

public class DayOfWeekConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (!(value is int n))
      throw new ArgumentException();
    var dx = DayOfWeekX.DaysOfWeek.FirstOrDefault(dw => dw.N == n);
    return dx ?? throw new ArgumentException();
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    => value is DayOfWeekX dx ? (object)dx.N : null;
}

+ Разметка
<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <DataGrid
    CanUserAddRows="True"
    ItemsSource="{Binding Diary}"
    AutoGenerateColumns="False">
    <DataGrid.Columns>
      <DataGridTemplateColumn Header="День недели">
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding Day.Name}"/>
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
        <DataGridTemplateColumn.CellEditingTemplate>
          <DataTemplate>
            <ComboBox
              ItemsSource="{Binding Source={x:Static local:DayOfWeekX.DaysOfWeek}}" 
              SelectedItem="{Binding Day, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  
              DisplayMemberPath="Name"/>
          </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>
      </DataGridTemplateColumn>
      <DataGridTextColumn
        Header="Описание"
        Width="*"
        Binding="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </DataGrid.Columns>
  </DataGrid>
  <DataGrid
    Grid.Row="1"
    Margin="0,10,0,0"
    CanUserAddRows="True"
    ItemsSource="{Binding Diary2}"
    AutoGenerateColumns="False">
    <DataGrid.Columns>
      <DataGridTemplateColumn Header="День недели">
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <TextBlock Text="{Binding DayName}"/>
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
        <DataGridTemplateColumn.CellEditingTemplate>
          <DataTemplate>
            <DataTemplate.Resources>
              <local:DayOfWeekConverter x:Key="DayOfWeekConverter" />
            </DataTemplate.Resources>
            <ComboBox
              ItemsSource="{Binding Days}" 
              SelectedItem="{Binding Day, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource DayOfWeekConverter}}"
              DisplayMemberPath="Name"/>
          </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>
      </DataGridTemplateColumn>
      <DataGridTextColumn
        Header="Описание"
        Width="*"
        Binding="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </DataGrid.Columns>
  </DataGrid>
</Grid>

В верхнем датагриде SelectedItem комбобокса связан со свойством такого же типа из элемента ItemsSource датагрида, ItemsSource комбобокса статический. В нижнем датагриде SelectedItem комбобокса связан со свойством другого типа из элемента ItemsSource датагрида, и поэтому используется конвертер, ItemsSource комбобокса динамический (привязан к свойству элемента из ItemsSource датагрида). Визуально это выражается в том, что если выбрать нечетный день недели, список в комбобоксе будет отсортирован по возрастанию номера дня недели, если выбрать нечетный - по убыванию.
16 дек 18, 14:46    [21765299]     Ответить | Цитировать Сообщить модератору
Все форумы / WPF, Silverlight Ответить