Код факторинга триггеров данных, связанных с различными свойствами - PullRequest
1 голос
/ 19 сентября 2011

У меня есть сетка данных, привязанная к списку пользовательских классов "модели". В одной из ячеек у меня есть 4 прямоугольника, представляющих 4 разных строковых свойства моего модельного класса.

Прямоугольники должны быть черными, если соответствующее свойство пусто, иначе красным. Я добиваюсь этого, используя DataTrigger для каждого прямоугольника, и он работает нормально, но мне нужно написать четыре раза один и тот же источник данных с изменением только связанного пути. Кажется, что это может быть более эффективным.

Есть ли умный способ определить DataTrigger один раз и использовать разные связанные пути для каждого элемента (в моем случае прямоугольники), к которому он применяется?

Спасибо.

Это мой модельный класс:

class myModel
{
    [...]
    public string pr1 { get; set; }
    public string pr2 { get; set; }
    public string pr3 { get; set; }
    public string pr4 { get; set; }
    [...]
}

Это сетка данных, привязанная к List<myModel>:

<DataGrid AutoGenerateColumns="False" Height="200" Name="myDg" ItemsSource="{Binding}">
    <DataGrid.Columns>
        [...]
        <DataGridTemplateColumn Header="prs">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel  Name="spRow" Orientation="Horizontal">
                        <Rectangle Height="15" Name="rPr1" Width="10">
                            <Rectangle.Style>
                                <Style TargetType="{x:Type Rectangle}">
                                    <Setter Property="Rectangle.Stroke" Value="Red"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=pr1}" Value="">
                                            <Setter Property="Rectangle.Stroke" Value="Black"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Rectangle.Style>
                        </Rectangle>
                        <Rectangle Height="15" Name="rPr2" Width="10">
                            <Rectangle.Style>
                                <Style TargetType="{x:Type Rectangle}">
                                    <Setter Property="Rectangle.Stroke" Value="Red"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=pr2}" Value="">
                                            <Setter Property="Rectangle.Stroke" Value="Black"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Rectangle.Style>
                        </Rectangle>
                        <Rectangle Height="15" Name="rPr3" Width="10">
                            <Rectangle.Style>
                                <Style TargetType="{x:Type Rectangle}">
                                    <Setter Property="Rectangle.Stroke" Value="Red"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=pr3}" Value="">
                                            <Setter Property="Rectangle.Stroke" Value="Black"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Rectangle.Style>
                        </Rectangle>
                        <Rectangle Height="15" Name="rPr4" Width="10">
                            <Rectangle.Style>
                                <Style TargetType="{x:Type Rectangle}">
                                    <Setter Property="Rectangle.Stroke" Value="Red"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Path=pr4}" Value="">
                                            <Setter Property="Rectangle.Stroke" Value="Black"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Rectangle.Style>
                        </Rectangle>
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

Ответы [ 2 ]

1 голос
/ 19 сентября 2011

Существует два способа ...

Решение 1:

Вы можете обобщить стиль на уровне предка и затем специализировать свойства модели в каждом прямоугольнике.

Вы можете сделать это, привязав конкретное свойство pr к Tag каждого прямоугольника, а затем используйте Tag в качестве источника триггера общих данных.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <TextBox Margin="5" x:Name="MyTextBox1" Text="pr1" />
        <TextBox Margin="5" x:Name="MyTextBox2" Text="pr2" />
        <TextBox Margin="5" x:Name="MyTextBox3" Text="pr3" />
        <TextBox Margin="5" x:Name="MyTextBox4" Text="pr4" />
    </StackPanel>
    <StackPanel Grid.Row="1" Orientation="Horizontal">
        <StackPanel.Resources>
            <Style TargetType="{x:Type Rectangle}">
                <Setter Property="Height" Value="20"/>
                <Setter Property="Width" Value="20"/>
                <Setter Property="Stroke" Value="Black"/>
                <Setter Property="StrokeThickness" Value="1"/>
                <Setter Property="Margin" Value="5"/>
                <Style.Triggers>
                    <DataTrigger
                          Binding="{Binding Tag,
                              RelativeSource={RelativeSource Self}}"
                          Value="">
                        <Setter Property="Fill" Value="Red"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Resources>
        <Rectangle Tag="{Binding Text, ElementName=MyTextBox1, Mode=OneWay}" />
        <Rectangle Tag="{Binding Text, ElementName=MyTextBox2, Mode=OneWay}" />
        <Rectangle Tag="{Binding Text, ElementName=MyTextBox3, Mode=OneWay}" />
        <Rectangle Tag="{Binding Text, ElementName=MyTextBox4, Mode=OneWay}" />
    </StackPanel>
</Grid>

Итак, в приведенном выше примере, когда вы очищаете отдельное текстовое поле, вы получаете соответствующий красный прямоугольник.Обратите внимание, что мы использовали тег через DataTrigger, но мы также можем использовать обычный триггер ...

<Trigger Property="Tag" Value="">
    <Setter Property="Fill" Value="Red"/>
</Trigger>

Решение 2:

Использование ItemsControlа затем 4 (или n) элемента в вашей модели для хранения значений pr1..pr4.

РЕДАКТИРОВАТЬ

....

Измените модель, включив в нее список pr объектов.Я использую SourceFilter класс просто для хранения двухстороннего редактируемого строкового значения, которое содержалось в pr'n' свойствах ... Вы можете использовать любой класс, который содержит строковое значение черезсвойство.

    public List<SourceFilter> pr
    {
        get;
        set;
    }

Затем я загружаю четыре свойства как четыре SourceFilter объекта ...

    pr = new List<SourceFilter>();
    pr.Add(new SourceFilter("pr1"));
    pr.Add(new SourceFilter("pr2"));
    pr.Add(new SourceFilter("pr3"));
    pr.Add(new SourceFilter("pr4"));

И затем я использую ItemsControl's ItemPanel, ItemsTemplate, ItemsSource для достижения точно такого же эффекта из шага 1 ...

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ItemsControl Grid.Row="0" ItemsSource="{Binding pr, ElementName=MyWindow2}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Margin="5"
                         Text="{Binding Path=Source,
                                        Mode=TwoWay,
                                        UpdateSourceTrigger=PropertyChanged}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ItemsControl Grid.Row="1" ItemsSource="{Binding pr, ElementName=MyWindow2}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <DataTemplate.Resources>
                    <Style TargetType="{x:Type Rectangle}">
                        <Setter Property="Height" Value="20"/>
                        <Setter Property="Width" Value="20"/>
                        <Setter Property="Stroke" Value="Black"/>
                        <Setter Property="StrokeThickness" Value="1"/>
                        <Setter Property="Margin" Value="5"/>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Source}" Value="">
                                <Setter Property="Fill" Value="Red"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </DataTemplate.Resources>
                <Rectangle />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Преимущество здесь в том, что вам не нужно нигде указывать привязки pr1..pr4.Плюс его расширяемость (так как pr может содержать любые значения n и автоматически генерирует такое же количество прямоугольников).

1 голос
/ 19 сентября 2011

Да, вы можете создать класс, производный от DataTrigger и с предварительно установленными неизменными деталями:

  public class RedBlackDataTrigger : DataTrigger
  {
    public RedBlackDataTrigger()
    {
      Value = string.Empty;
      Setters.Add(new Setter(Rectangle.StrokeProperty, new SolidColorBrush(Colors.Black)));
    }
  }

Вы можете использовать его в XAML и просто добавить привязку:

    <Rectangle Height="15" Width="10">
    <Rectangle.Style>
        <Style TargetType ="Rectangle">
            <Setter Property="Rectangle.Stroke" Value="Red"/>
            <Style.Triggers>
                <Your-Namespace:RedBlackDataTrigger Binding="{Binding Path=pr4}" />
            </Style.Triggers>
        </Style>
    </Rectangle.Style>
    </Rectangle>

Поскольку вы также дублируете свой Style, вы можете сделать еще один шаг и создать производный класс стилей:

  public class RectangleStyle : Style
  {
    public RectangleStyle()
    {
      TargetType = typeof (Rectangle);
      Setters.Add(new Setter(Rectangle.StrokeProperty, new SolidColorBrush(Colors.Red)));
    }

    public string ColorTrigger
    {
      set
      {
        Triggers.Add(new RedBlackDataTrigger {Binding = new Binding(value) });
      }
    }
  }

Теперь вы сократили количество элементов до:

  <Rectangle Height="15" Width="10">
      <Rectangle.Style>
           <Your-Namespace:RectangleStyle ColorTrigger="pr4" />
      </Rectangle.Style>
  </Rectangle>

Вы даже можете сделать еще один шаг вперед и создать производный класс Rectangle, чтобы сделать все это.

...