Единая сетка как шаблон панели для всех элементов в ItemsControl, кроме первого - PullRequest
0 голосов
/ 13 октября 2018

Я работаю над мастером прогресса.Я определил его как стиль, основанный на ItemsControl.У меня есть ItemTemplateSelector с двумя DataTemplates, один для первого элемента и один для остальных элементов.У меня это работает правильно, за исключением одной действительно маленькой проблемы, которую очень сложно исправить.Существует разрыв между первым элементом и вторым элементом.enter image description here Вот как должен выглядеть элемент управления: enter image description here Разрыв возникает из-за того, что я использую равномерную сетку и поэтому все столбцы имеют одинаковый размер, дажехотя у первого нет строки.Использование равномерной сетки очень важно, потому что я хочу, чтобы все в одной строке и чтобы элемент управления растягивался, чтобы заполнить доступное пространство по мере его роста.Я пытался не использовать равномерную сетку, но в итоге я либо столкнулся с проблемами с полями, либо не заполнил доступное пространство.как я могу исправить этот пробел?

Вот код:

<Style x:Key="WizardProgressBar" TargetType="{x:Type ItemsControl}">
        <Style.Resources>
            <DataTemplate x:Key="FirstItem">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Ellipse Name="ellipse"  HorizontalAlignment="Left" Height="32" Width="32"  />
                </Grid>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Completed}" Value="False">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource DisabledBrush}" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding InProgress}" Value="True">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>


            <DataTemplate x:Key="OtherItem">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Ellipse Name="ellipse"  Grid.Column="1" HorizontalAlignment="Left" Height="32" Width="32" />
                    <Line Name="leftPath" Grid.Column="0" X1="0" Y1="16" 
                              X2="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" Y2="16" />
                </Grid>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Completed}" Value="False">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource DisabledBrush}" />
                        <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource DisabledBrush}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding InProgress}" Value="True">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
                        <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </Style.Resources>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="1"/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemTemplateSelector">
            <Setter.Value>
                <wpf:ItemsDataTemplateSelector  FirstItem="{StaticResource FirstItem}" OtherItem="{StaticResource OtherItem}" />
            </Setter.Value>
        </Setter>
    </Style>


public class ItemsDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate FirstItem { get; set; }
        public DataTemplate OtherItem { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
            var returnTemplate = (itemsControl.ItemContainerGenerator.IndexFromContainer(container) == 0) ? FirstItem : OtherItem;
            return returnTemplate;
        }
    }

Ответы [ 3 ]

0 голосов
/ 16 октября 2018

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

. Я могу предложить вам следующее решение.

Целевая конфигурация будет выглядеть следующим образом:

|··O––|––O––|––O––|––O··|

У вас будетШирокий край полуэлемента слева и справа (представлен точками выше).При желании вы можете использовать их, указав поля своего элемента управления.

Кроме того, мы можем упростить ваши шаблоны данных.На самом деле, нам не нужны отдельные шаблоны для первого и других элементов.См. Ниже.

Настройка

Здесь мы будем использовать три уловки:

  • свойство ItemsControl.AlternationIndex для получения первого элемента в ItemsControl
  • a Canvas, позволяющий рисовать соединительные линии вне соответствующих UniforGrid ячеек
  • специального IValueConverter, который поможет нам рассчитать необходимое положение линии

Теперь, это стиль для вашего ItemsControl:

<Style x:Key="WizardProgressBar" TargetType="{x:Type ItemsControl}">
  <Style.Resources>
    <local:LinearConverter x:Key="Multiplier" Scale="-0.5" Offset="16"/>

    <DataTemplate DataType="{x:Type local:YourItemType}">
      <Grid>            
        <Canvas>
          <Rectangle x:Name="leftPath" Height="2" Stroke="Blue" Canvas.Top="16"
                     Canvas.Left="{Binding Width, RelativeSource={RelativeSource Self}, Converter={StaticResource Multiplier}}"
                     Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}"/>
        </Canvas>
        <Ellipse Name="ellipse" HorizontalAlignment="Center" Height="32" Width="32" Stroke="Blue"/>
      </Grid>
      <DataTemplate.Triggers>
        <Trigger Property="ItemsControl.AlternationIndex" Value="0">
          <Setter TargetName="leftPath" Property="Visibility" Value="Collapsed"/>
        </Trigger>
        <DataTrigger Binding="{Binding Completed}" Value="False">
          <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource DisabledBrush}" />
          <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource DisabledBrush}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding InProgress}" Value="True">
          <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
          <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}" />
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>
  </Style.Resources>
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate>
        <UniformGrid Rows="1"/>
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
  <Setter Property="AlternationCount" Value="100"/>
</Style>

Обратите внимание на следующие изменения:

  • есть только один DataTemplate нет необходимости в двух разных
  • больше не нужен селектор шаблонов
  • нам нужен специальный конвертер в нашем стиле (код ниже)
  • Ellipse илиния (как Rectangle) помещается в 1x1 Grid в ту же ячейку
  • сама линия находится в Canvas
  • * Ellipse центрирована горизонтально
  • есть дополнительный Trigger, который ловит значение AlternationIndex 0 и скрывает строку - это для первого элемента
  • стиль устанавливаетAlternationCount до 100, если у вас будет максимум 100 страниц мастера

Вот конвертер:

class LinearConverter : IValueConverter
{
    public double Scale { get; set; }

    public double Offset { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // TODO: exception handling
        return System.Convert.ToDouble(value) * Scale + Offset;
    }

    // ConvertBack just throws a NotImplementedException
}

Пояснение

Каждый Ellipse представляетСтраница мастера и находится в центре соответствующей ячейки UniformGrid.Линии расположены слева от эллипсов.Ширина линий устанавливается равной ширине отдельной ячейки UnifiormGrid, а их горизонтальное положение в Canvas устанавливается по формуле: WidthOfEllipse / 2 - WidthOfCell / 2.Это обеспечивает правильное размещение.

Для первой страницы мастера линия будет скрыта.

Обратите внимание, что вы можете использовать Fill для эллипсов, чтобы скрыть нижележащие линии.

0 голосов
/ 16 октября 2018

Цитирую Стива Джобса: «Это не просто то, как он выглядит и как он выглядит. Дизайн - это то, как он работает». .

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

 ___________________________________________________________
|           |           |           |           |           |
O - - - - - O - - - - - O - - - - - O - - - - - O - - - - - O
|___________|___________|___________|___________|___________|

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

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

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

С точки зрения кистей, на которые вы ссылались:

                    Default                 In progress             Completed

Left node           DisabledBrush           PrimaryTextBrush        PrimaryTextBrush

Intermediate path   DisabledBrush           DisabledBrush           PrimaryTextBrush

Right node          DisabledBrush           DisabledBrush           PrimaryTextBrush

Единственный ничья назад - вырисовать каждый узел дважды, кроме первого и последнего.

0 голосов
/ 13 октября 2018

Быстрое решение - удалить первое из ItemsControl.

    <Grid VerticalAlignment="Top">
    <Grid.Resources>
        <Style x:Key="WizardProgressBar" TargetType="{x:Type ItemsControl}">
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <Ellipse Name="ellipse"  Grid.Column="1" HorizontalAlignment="Left" Height="32" Width="32" />
                            <Line Name="leftPath" Grid.Column="0" X1="0" Y1="16" 
                          X2="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" Y2="16" />
                        </Grid>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding Completed}" Value="False">
                                <Setter TargetName="ellipse" Property="Stroke" Value="Gray" />
                                <Setter TargetName="leftPath" Property="Stroke" Value="Gray"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding InProgress}" Value="True">
                                <Setter TargetName="ellipse" Property="Stroke" Value="Black"/>
                                <Setter TargetName="leftPath" Property="Stroke" Value="Black"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <UniformGrid Rows="1"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Ellipse Name="ellipse"  HorizontalAlignment="Left" Height="32" Width="32" Stroke="Black"/>
    <ItemsControl Grid.Column="1" Style="{StaticResource WizardProgressBar}"
                  x:Name="otherItemsGrid">
    </ItemsControl>
</Grid>
...