【问题标题】:Add different controls in ItemsControl在 ItemsControl 中添加不同的控件
【发布时间】:2016-01-13 16:31:05
【问题描述】:

我需要根据特定条件在 ItemsControl 中添加不同的控件(TextBox/CheckBox/ComboBox 等)。 ItemsControl 中的每个 Item 都是一个名称-值对。 Name 始终由 TextBlock 表示,但 Value 可以是任何 UI 控件。 我使用水平对齐的 StackPanel 来表示每个项目。 StackPanel 中的第一个控件仍然是 TextBlock,但第二个控件取决于运行时在 ViewModel 中设置的“ItemDataType”属性。

我遇到的问题是我无法使用带有 ItemDataType 属性的 Style 触发器在 StackPanel 的第二个元素中分配不同的控件。

代码片段:

<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

【问题讨论】:

    标签: wpf datatemplate itemscontrol contentcontrol


    【解决方案1】:

    答案(或一个答案)是写一个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”,这将在本地上下文中查找资源键为“Foo”的DataTemplate 并使用它——因此相应地命名您的DataTemplates 并前往城镇.没有更简单或更通用的方法可以做到这一点。顺便说一句,在我们的代码中,它目前仅在一个地方使用(我上个月刚写的),它使用枚举值作为资源键而不是字符串。任何物体都可以。资源键不必是字符串。

    或者,您可能希望进行查找或将DataName 值转换为一组不同的键。您的电话,如果这更适合您的需求。

    另一种选择是拥有一个带有一组触发器的项目模板,该触发器在 inner ContentControl 上设置模板,具体取决于DataName 的值。 Here's an answer that does something like that.

    【讨论】:

      【解决方案2】:

      我想要一个 XAML 解决方案 - 花了我一段时间 :-)。 以下是工作代码:

          <Style x:Key="nvpTextBlockStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
              <Setter Property="HorizontalAlignment" Value="Left"/>
              <Setter Property="Width" Value="{Binding Path=LabelWidthStr, FallbackValue=50}"/>
              <Setter Property="Margin" Value="0,5,0,5"/>
              <Setter Property="Text" Value="{Binding Path=NameData,Mode=TwoWay}"/>
              <Setter Property="FontSize" Value="16"/>
          </Style>
      
          <DataTemplate x:Key="textBoxTemplate">
              <TextBox Margin="1,1" Text="{Binding Path=ValueData,UpdateSourceTrigger=PropertyChanged,
                          ValidatesOnExceptions=True,NotifyOnValidationError=True,ValidatesOnDataErrors=True}"/>
          </DataTemplate>
      
          <DataTemplate x:Key="comboBoxTemplate">
              <ComboBox HorizontalAlignment="Left" ItemsSource="{Binding Path=SelectionList}" 
                            SelectedValue="{Binding Path=ValueData,Mode=TwoWay}"
                            IsEnabled="{Binding IsDataItemEnabled}"/>
          </DataTemplate>
      
          <DataTemplate x:Key="checkBoxTemplate">
              <CheckBox HorizontalAlignment="Left" VerticalAlignment="Center"  
                            IsChecked="{Binding Path=ValueData,Mode=TwoWay}"/>
          </DataTemplate>
      
          <DataTemplate x:Key="buttonTemplate">
              <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.AddCommand}" 
                          CommandParameter="{Binding}" Width="30" Height="25">
                  <TextBlock Text="&#x1F511;" FontFamily="Segoe UI Symbol" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Gray" />
              </Button>
          </DataTemplate>
      
          <DataTemplate x:Key="dynamicTemplate">
              <StackPanel Orientation="Horizontal" Margin ="5">
                  <TextBlock Style="{StaticResource nvpTextBlockStyle}"/>
                  <ContentPresenter Content="{Binding}" 
                      Tag="{Binding Path=CustomDataType, FallbackValue={x:Static local:CustomViewModel.TEXTBOX_TEMPLATE}}">
                      <ContentPresenter.Resources>
                          <Style TargetType="{x:Type ContentPresenter}">
                              <Style.Triggers>
                                  <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.TEXTBOX_TEMPLATE}">
                                      <Setter Property="ContentTemplate" Value="{StaticResource textBoxTemplate}"/>
                                  </Trigger>
                                  <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.COMBOBOX_TEMPLATE}">
                                      <Setter Property="ContentTemplate" Value="{StaticResource comboBoxTemplate}"/>
                                  </Trigger>
                                  <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.CHECKBOX_TEMPLATE}">
                                      <Setter Property="ContentTemplate" Value="{StaticResource checkBoxTemplate}"/>
                                  </Trigger>
                                  <Trigger Property="Tag" Value="{x:Static local:CustomViewModel.BUTTON_TEMPLATE}">
                                      <Setter Property="ContentTemplate" Value="{StaticResource buttonTemplate}"/>
                                  </Trigger>
                              </Style.Triggers>
                          </Style>
                      </ContentPresenter.Resources>
                  </ContentPresenter>
              </StackPanel>
          </DataTemplate>
      
          <Grid>       
          <ScrollViewer VerticalScrollBarVisibility="Auto">
              <ItemsControl  ItemsSource="{Binding Path=CustomDataList,Mode=TwoWay}" 
                             ItemTemplate="{StaticResource dynamicTemplate}";
                             KeyboardNavigation.IsTabStop="False">
                  <ItemsPanelTemplate>
                      <StackPanel></StackPanel>
                  </ItemsPanelTemplate>
              </ItemsControl>
          </ScrollViewer>
      
      </Grid>
      

      谢谢,

      RDV

      【讨论】:

        猜你喜欢
        • 2017-01-01
        • 1970-01-01
        • 2013-03-01
        • 2010-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多