2012-06-06 6 views
3

У меня есть googled вокруг, но с очень ограниченной удачей. У меня вопрос относительно редактируемого WPF DataGrid; в CellEditingTemplate показан ComboBox, но в CellTemplate показан TextBox с соответствующим значением ComboBox. Мой код выглядит примерно так:WPF DataGrid: CellEditingTemplate ComboBox несколько данных для CellTemplate TextBox

<DataGridTemplateColumn Header="Unit"> 
    <DataGridTemplateColumn.CellEditingTemplate> 
    <DataTemplate> 
     <ComboBox Name="comboBoxUnit" ItemsSource="{Binding ...}" SelectedValue="{Binding UnitId, ValidatesOnDataErrors=True}" SelectedValuePath="Id"> 
     <ComboBox.ItemTemplate> 
      <DataTemplate> 
      <StackPanel Orientation="Horizontal"> 
       <TextBlock Text="{Binding Id}" /> 
       <TextBlock Text=" " /> 
       <TextBlock Text="{Binding Name}" /> 
      </StackPanel> 
      </DataTemplate> 
     </ComboBox.ItemTemplate> 
     </ComboBox> 
    </DataTemplate> 
    </DataGridTemplateColumn.CellEditingTemplate> 
    <DataGridTemplateColumn.CellTemplate> 
    <DataTemplate> 
     <TextBlock Text="<would like to have selected Unit's Id and Name here>" /> 
    </DataTemplate>          
    </DataGridTemplateColumn.CellTemplate> 
</DataGridTemplateColumn> 

Как я могу это достичь? Отдельное свойство в классе (иметь свойства UnitId и UnitName) не является проблемой, я могу добавить его, но как связать оба с ComboBox? Могу ли я получить доступ к CellEditingTemplate ComboBox в CellTemplate? Похоже, что они находятся в «разных пространствах имен», так как я могу назвать элементы управления как с одинаковыми именами ...

Любые идеи, указатели? Заранее благодарен, DB

ответ

3

Самый простой способ добиться этого же - использовать DataGridComboBoxColumn.

Однако в моем текущем проекте у нас было так много проблем с DataGridComboBoxColumn, что мы его больше не используем. Вместо этого мы используем DataGridTemplateColumn с ComboBox в CellEditingTemplate и TextBlock в CellTemplate(как и вы).

Чтобы иметь возможность отображать данные на основе идентификатора (для получения той же функциональности в TextBlock, как в ComboBox), мы используем конвертер под названием CodeToDescriptionConverter. Его также можно использовать как этот

<TextBlock> 
    <TextBlock.Text> 
     <MultiBinding> 
      <MultiBinding.Converter> 
       <con:CodeToDescriptionConverter CodeAttribute="Id" 
               StringFormat="{}{0} - {1}"> 
        <con:CodeToDescriptionConverter.DescriptionAttributes> 
         <sys:String>Id</sys:String> 
         <sys:String>Name</sys:String> 
        </con:CodeToDescriptionConverter.DescriptionAttributes> 
       </con:CodeToDescriptionConverter> 
      </MultiBinding.Converter> 
      <Binding Path="UnitId"/> 
      <Binding Path="Units"/> 
     </MultiBinding> 
    </TextBlock.Text> 
</TextBlock> 
  • Первый Binding это значение, которое мы ищем (Id)
  • Второй Binding является IList мы смотрим в
  • CodeAttribute это имя свойства мы хотим по сравнению с id (first Binding)
  • DescriptionAttributes - это те объекты, которые мы хотим вернуть в формате StringFormat

А в вашем случае: Найти экземпляр в Units, где свойство Id имеет такое же значение, как UnitId и для этого экземпляра возвращать значения из Id и Name отформатированного {0} - {1}

CodeToDescriptionConverter использует рефлексию для достижения этой

public class CodeToDescriptionConverter : IMultiValueConverter 
{ 
    public string CodeAttribute { get; set; } 
    public string StringFormat { get; set; } 
    public List<string> DescriptionAttributes { get; set; } 

    public CodeToDescriptionConverter() 
    { 
     StringFormat = "{0}"; 
     DescriptionAttributes = new List<string>(); 
    } 

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values.Length != 2 || 
      values[0] == DependencyProperty.UnsetValue || 
      values[1] == DependencyProperty.UnsetValue || 
      values[0] == null || 
      values[1] == null) 
     { 
      return null; 
     } 

     string code = values[0].ToString(); 
     IList sourceCollection = values[values.Length - 1] as IList; 
     object[] returnDescriptions = new object[DescriptionAttributes.Count]; 
     foreach (object obj in sourceCollection) 
     { 
      PropertyInfo codePropertyInfo = obj.GetType().GetProperty(CodeAttribute); 
      if (codePropertyInfo == null) 
      { 
       throw new ArgumentException("Code Property " + CodeAttribute + " not found"); 
      } 
      string codeValue = codePropertyInfo.GetValue(obj, null).ToString(); 
      if (code == codeValue) 
      { 
       for (int i = 0; i < DescriptionAttributes.Count; i++) 
       { 
        string descriptionAttribute = DescriptionAttributes[i]; 
        PropertyInfo descriptionPropertyInfo = obj.GetType().GetProperty(descriptionAttribute); 
        if (descriptionPropertyInfo == null) 
        { 
         throw new ArgumentException("Description Property " + descriptionAttribute + " not found"); 
        } 
        object descriptionObject = descriptionPropertyInfo.GetValue(obj, null); 
        string description = ""; 
        if (descriptionObject != null) 
        { 
         description = descriptionPropertyInfo.GetValue(obj, null).ToString(); 
        } 
        returnDescriptions[i] = description; 
       } 
       break; 
      } 
     } 

     // Ex. string.Format("{0} - {1} - {2}", arg1, arg2, arg3); 
     return string.Format(StringFormat, returnDescriptions); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
} 

Я загрузил образец приложения здесь: CodeToDescriptionSample.zip.
В комплект поставки входит DataGridTemplateColumn с CodeToDescriptionConverter и DataGridComboBoxColumn, который делает то же самое. Надеюсь, что это поможет

+0

Благодарим за всесторонний ответ. Входящий образец, похоже, делает именно то, что я хочу. Я попытаюсь включить ваш код в свой проект завтра и отправить обратную связь. – dabor

+2

Работает как и ожидалось, очень красивое и универсальное решение! Это показало мне многое о конвертерах цен, которые я не знал. Я попытался отправить коллекцию Units через ConverterParameter, но привязка не работает с параметрами конвертера, поэтому я перестал искать там ... – dabor

+1

Рад, что это вам помогло :) Да, вы не можете привязать «Конвертер», потому что это не 'DependencyPropety'.И если вы сделаете конвертер наследованным от 'DependencyObject', чтобы он мог иметь« DependencyProperties », вы столкнулись с следующей проблемой. Это не в дереве, поэтому 'DataContext',' RelativeSource' или 'ElementName'' Bindings' не работает, а только 'Source'. Его проще всего отправить в качестве второго «Binding» :) –

Смежные вопросы