2016-01-13 3 views
0

Мне нужно добавить различные элементы управления (TextBox/CheckBox/ComboBox и т. Д.) В ItemsControl на основе определенных условий. Каждый элемент в ItemsControl - это пара Name-Value. Имя всегда представлено TextBlock, но Value может быть любым элементом управления пользовательским интерфейсом. Я использую горизонтально выровненную StackPanel для представления каждого элемента. Первый элемент управления в StackPanel остается TextBlock, но второй элемент управления зависит от свойства ItemDataType, установленного в ViewModel во время выполнения.Добавить элементы управления в ItemsControl

Проблема заключается в том, что я не могу назначить разные элементы управления в 2-м элементе StackPanel, используя триггер стиля с свойством ItemDataType.

Код сниппета:

<UserControl.Resources> 

    <DataTemplate x:Key="TextBoxTemplate"> 
     <TextBox Text="{Binding Path=DataValue}"/> 
    </DataTemplate> 

    <DataTemplate x:Key="ComboBoxTemplate"> 
     <ComboBox ItemsSource="{Binding Path=SelectionList}" SelectedValue="{Binding Path=DataValue,Mode=TwoWay}"/> 
    </DataTemplate> 

    <DataTemplate x:Key="CheckBoxTemplate"> 
     <CheckBox IsChecked="{Binding Path=DataValue,Mode=TwoWay}" /> 
    </DataTemplate> 

    <DataTemplate x:Key="ButtonTemplate"> 
     <Button Content="{Binding Path=DataValue}"/> 
    </DataTemplate> 

    <DataTemplate x:Key="dynamicTemplate"> 
     <StackPanel Orientation="Horizontal" Tag="{Binding ItemDataType}"> 
      <TextBlock Text="{Binding Path=DataName,Mode=TwoWay}"/> 
      <ContentControl> 
       <ContentControl.Style> 
        <Style TargetType="{x:Type ContentControl}"> 
         <Style.Triggers> 
          <DataTrigger Binding="{Binding ItemDataType}" Value="TextBox"> 
           <Setter Property="Template" Value="{StaticResource TextBoxTemplate}"/> 
          </DataTrigger> 
         </Style.Triggers> 
        </Style> 
       </ContentControl.Style> 
      </ContentControl> 
     </StackPanel> 
    </DataTemplate> 

</UserControl.Resources> 

<Grid> 
    <!-- CONTROL LAYOUT --> 
    <ItemsControl ItemsSource="{Binding Path=DataList,Mode=TwoWay}" ItemTemplate="{StaticResource dynamicTemplate}"> 

     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <StackPanel></StackPanel> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
    </ItemsControl> 
</Grid> 

Ошибка я получаю DataTemplate недопустим для ContentControl.Template собственности. Я понимаю, что то, что я делаю, ошибочно, но я хочу помочь сделать это правильно.

Спасибо,

RDV

ответ

0

Ответ (или ответ) является написание DataTemplateSelector, который возвращает правильный шаблон на основе некоторого произвольного параметра. Что-то вроде следующего (извините весь нулевой контрольный шум, который скопирован непосредственно из производственного кода).

/// <summary> 
/// Selects template based on the value of a named property of the data item. 
/// Property name is specified by ResourceKeyPropertyName. 
/// </summary> 
public class PropertyValueTemplateSelector : DataTemplateSelector 
{ 
    /// <summary> 
    /// Gets or sets a path to a value on the source object to serve as a resource key for 
    /// the DataTemplate used to display the source object. 
    /// </summary> 
    public string ResourceKeyPropertyName { get; set; } 

    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     var context = container as FrameworkElement; 
     DataTemplate template = null; 

     if (null == container) 
     { 
      throw new NullReferenceException("container"); 
     } 
     else if (null == context) 
     { 
      throw new Exception("container must be FramekworkElement"); 
     } 
     else if (String.IsNullOrEmpty(ResourceKeyPropertyName)) 
     { 
      throw new NullReferenceException("ResourceKeyPropertyName"); 
     } 
     else if (null == item) 
     { 
      return null; 
     } 

     var prop = item.GetType().GetProperty(ResourceKeyPropertyName); 

     if (null == prop) 
     { 
      throw new Exception("Undefined property " + ResourceKeyPropertyName); 
     } 

     var resourceKey = prop.GetValue(item, null); 

     if (null != resourceKey) 
     { 
      try 
      { 
       template = context.FindResource(resourceKey) as DataTemplate; 
      } 
      catch (Exception ex) 
      { 
       Ability.CAPS.WPF.Utilities.ErrorHandler.HandleException(ex, Ability.Logging.AbilityExceptionPolicy.GeneralExceptionPolicy); 
       template = null; 
      } 
     } 

     return template ?? base.SelectTemplate(item, container); 
    } 
} 

Использование как и в XAML:

<ItemsControl 
    > 
    <ItemsControl.ItemTemplateSelector> 
     <!-- Tell it use the value of the "DataName" property as the 
      resource key for the template it uses. 
     --> 
     <local:PropertyValueTemplateSelector 
      ResourceKeyPropertyName="DataName" /> 
    </ItemsControl.ItemTemplateSelector> 
    <!-- etc. 
     etc. 
     etc. --> 
</ItemsControl> 

Если DataName свойство элемента равна «Foo», это будет выглядеть в местном контексте для DataTemplate, чей ресурс ключ «Foo» и использование, которое - так назовите свой DataTemplates и идите в город. Нет более простого или более обобщенного способа сделать это. Кстати, в нашем коде это используется только в одном месте на данный момент (я только что написал его в прошлом месяце), и он использует значения enum для ключей ресурсов, а не строк. Любой объект будет делать. Клавиши ресурсов не обязательно должны быть строками.

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

Другим вариантом было бы, имеющий один шаблон элемента с набором триггеров, который устанавливает шаблон на с внутренней ContentControl в зависимости от значения DataName. Here's an answer that does something like that.

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