Как по-разному стилизовать разные элементы при использовании шаблона данных в xaml? - PullRequest
3 голосов
/ 20 февраля 2010

Я бы хотел, чтобы мои элементы, сгенерированные из таблицы данных, выглядели по-разному в зависимости от некоторых свойств в модели. Например, я хочу отобразить текст в разных цветах или представить разные изображения или чертеж пути для каждого сгенерированного элемента. Я знаю, как это сделать в целом, но я ищу решение, которое позволило бы редактору редактировать визуальные детали в Expression Blend, не касаясь кода. Например самое простое решение:

   <DataTemplate>
        <StackPanel Orientation="Horizontal"> 
            <Image Source="{Binding MyImageSource}"/>
            <TextBlock Width="200"  Text="{Binding MyText}" Forecolor="{Binding MyColor}"></TextBox> 
        </StackPanel> 
    </DataTemplate> 

Где «MyImageSource» и «MyColor» являются свойствами модели элемента (типа ImageSource и Brush) не удовлетворяет моим потребностям, потому что я не хочу назначать эти значения. Я хочу, чтобы дизайнер сделал это. Вместо свойств «MyImageSource» и «MyColor» моя модель будет иметь свойство типа «ItemType» или «ItemStyle» типа enum или string (или другого типа). Я не ищу "религиозного" строгого решения MVVM. Мое единственное требование - избегать необходимости, чтобы дизайнер ждал, пока я исправлю код, следуя его инструкциям, таким как «изменить цвет элемента типа X в списке Y на #FFAACC», потому что это похоже на нарушение правила SoC.

РЕДАКТИРОВАТЬ (на основе ответов):

Я нашел решение, похожее на описанное bendewey здесь - оно требует получения пользовательского элемента управления для элемента управления с помощью атрибута ItemsSource. Идея использования разных шаблонов данных для каждого типа элемента изящна, но, на мой взгляд, она охватывает ситуацию, когда мы хотим создать совершенно разные визуальные элементы для каждого элемента. Когда элементы отличаются только по некоторым цветам и изображению (и кроме них содержат много общих элементов), создание отдельной таблицы данных для каждого типа элемента приведет к ненужному повторению кода (xaml). В этой ситуации решение Влада подходит лучше. Есть ли какая-либо другая техника, кроме этих двух?

Ответы [ 3 ]

6 голосов
/ 21 февраля 2010

Вы можете украсть концепцию ItemTemplateSelector из путеводителя WPF. Вот как это будет выглядеть:

App.xaml

<DataTemplate x:Key="MaleTemplate">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="M - " />
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</DataTemplate>

<DataTemplate x:Key="FemaleTemplate">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="F - " />
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</DataTemplate>

MainPage.xaml

<UserControl.Resources>
    <local:PersonTemplateSelector x:Key="PersonTemplateSelector" />
</UserControl.Resources>

<Grid x:Name="LayoutRoot">
    <local:CustomItemsControl ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource PersonTemplateSelector}" />
</Grid>

MainPage.xaml.cs

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        this.DataContext = Person.GetSampleData();
    }
}

PersonTemplateSelector.cs

public class PersonTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var person = item as Person;
        if (person != null)
        {
            switch (person.Gender)
            {
                case Gender.Male:
                    return Application.Current.Resources["MaleTemplate"] as DataTemplate;
                case Gender.Female:
                    return Application.Current.Resources["FemaleTemplate"] as DataTemplate;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
        return null;
    }
}

Если это то, что делает с тех пор, и вы заинтересованы в использовании, вам нужно будет использовать этот CustomItemsControl, или некоторые варианты:

CustomItemsControl.cs

public class CustomItemsControl : ItemsControl
{
    public DataTemplateSelector ItemTemplateSelector
    {
        get { return (DataTemplateSelector)GetValue(ItemTemplateSelectorProperty); }
        set { SetValue(ItemTemplateSelectorProperty, value); }
    }

    public static readonly DependencyProperty ItemTemplateSelectorProperty =
        DependencyProperty.Register("ItemTemplateSelector", typeof(DataTemplateSelector), typeof(CustomItemsControl), new PropertyMetadata(null));

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        ContentPresenter presenter = element as ContentPresenter;

        if (presenter != null)
        {
            DataTemplate itemTemplate = null;
            if (base.ItemTemplate != null)
            {
                itemTemplate = base.ItemTemplate;
            }
            else if (this.ItemTemplateSelector != null)
            {
                itemTemplate = this.ItemTemplateSelector.SelectTemplate(item, element);
            }

            presenter.Content = item;
            presenter.ContentTemplate = itemTemplate;
        }
    }
}

public class DataTemplateSelector
{
    public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        return null;
    }
}
0 голосов
/ 10 июля 2012

Afaik, DataTemplateSelector не совместим с Blend. По крайней мере, я не мог заставить это работать.

Полностью смешиваемое решение включает в себя один пользовательский элемент управления, унаследованный от ListBox и предоставляющий свойство (одно или несколько, но одно проще), которое будет регулировать, какие шаблоны данных применять. Затем вы редактируете стиль для этого ListBox и добавляете триггеры. Внутри триггера (Property Trigger) вы меняете шаблон данных на тот, который соответствует значению триггера.

Пример:

<Style x:Key="OrientedListStyle" TargetType="{x:Type local:OrientedListBox}">
<Setter Property="ItemTemplate" Value="{DynamicResource TemplateVertical}"/>
<Setter Property="ItemsPanel" Value="{DynamicResource PanelVertical}"/>

<Style.Triggers>
    <Trigger Property="Orientation" Value="Horizontal">
        <Setter Property="ItemTemplate" Value="{DynamicResource TemplateHorizontal}"/>
        <Setter Property="ItemsPanel" Value="{DynamicResource PanelHorizontal}"/>
    </Trigger>
</Style.Triggers>

0 голосов
/ 20 февраля 2010

Вы можете использовать конвертеры. Например, Forecolor="{Binding MyText, Converter=ColorFromText}. Вам нужно будет сохранить логику выбора цвета в конвертере.

...