Трудно сказать, какова лучшая практика в этом случае, но вот как я бы это сделал.
Элемент управления мастера на вашем скриншоте выглядит как комбинация ProgressBar
и ItemsControl
, и в этом случае мне кажется проще извлечь из ItemsControl
и реализовать функциональность прогресса, чем наоборот, но также зависит от того, как вы хотите, чтобы это работало (если вы хотите плавный прогресс или просто подсвечиваете точки, например, одну за другой) .
Используя UniformGrid
в качестве ItemsPanel
и ItemTemplate
ниже, мы получаем следующий вид (Steps - это List
строк)
![enter image description here](https://i.stack.imgur.com/pKbYR.png)
<ItemsControl ItemsSource="{Binding Steps}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Ellipse HorizontalAlignment="Center" Height="20" Width="20" Stroke="Transparent" Fill="Blue"/>
<TextBlock Grid.Row="1" Text="{Binding}" HorizontalAlignment="Center" Margin="0,5,0,0"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Добавление DropShadowEffect
к ItemsPanel
, два Path
элемента к ItemTemplate
и два DataTriggers
, чтобы определить, является ли текущий элемент первым или последним элементом, чтобы показать / скрыть левый / правый Path
и мы можем получить очень похожий вид на ваш скриншот
![enter image description here](https://i.stack.imgur.com/I6HgZ.png)
ItemsPanel
<UniformGrid Rows="1" SnapsToDevicePixels="True">
<UniformGrid.Effect>
<DropShadowEffect Color="Black"
BlurRadius="5"
Opacity="0.6"
ShadowDepth="0"/>
</UniformGrid.Effect>
</UniformGrid>
ItemTemplate
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="Path">
<Setter Property="Data" Value="M0.0,0.0 L0.0,0.33 L1.0,0.33 L1.0,0.66 L0.0,0.66 L0.0,1.0"/>
<Setter Property="StrokeThickness" Value="0"/>
<Setter Property="Height" Value="21"/>
<Setter Property="Stretch" Value="Fill"/>
<Setter Property="Fill" Value="{StaticResource wizardBarBrush}"/>
<Setter Property="StrokeEndLineCap" Value="Square"/>
<Setter Property="StrokeStartLineCap" Value="Square"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
</DataTemplate.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Path Name="leftPath"/>
<Path Name="rightPath" Grid.Column="1"/>
<Ellipse Grid.ColumnSpan="2" HorizontalAlignment="Center" Height="20" Width="20" Stroke="Transparent" Fill="{StaticResource wizardBarBrush}"/>
<TextBlock Grid.ColumnSpan="2" Grid.Row="1" Text="{Binding}" HorizontalAlignment="Center" Margin="0,5,0,0"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}"
Value="{x:Null}">
<Setter TargetName="leftPath" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={markup:IsLastItemConverter}}"
Value="True">
<Setter TargetName="rightPath" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Если вы решите использовать этот подход, вы, вероятно, сможете решить, как сделать все остальное, как
- Реализуйте это в пользовательском элементе управления многократного использования
- Получите штрих (
DropShadowEffect
) только для части прогресса, а не в тексте
- Реализация функциональности прогресса и т. Д.
В любом случае, я загрузил пример проекта с пользовательским элементом управления WizardProgressBar
и демонстрационный проект, использующий его здесь: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0
Это выглядит так
![enter image description here](https://i.stack.imgur.com/68oqj.gif)
Что нужно отметить по поводу образца
- Я попал в ситуацию, когда я получу
DropShadowEffect
на прогресс-части и заголовках или получу тонкую грань между каждым элементом (как видно на рисунке). Я не могу придумать простой способ избавиться от него, так что, возможно, это не самый лучший подход в конце концов:)
- Прогресс прост. Он просто имеет значение от 0 до 100, и затем конвертер решает, должен ли элемент светиться или нет
- Этот элемент управления может оказать небольшое влияние на производительность, но я не уверен, так как мой компьютер сегодня работает все медленно ...
Обновление
Внесены некоторые изменения в пример проекта, в котором я разделил презентацию на две ItemsControls
, чтобы избавиться от тонких линий между каждым элементом. Теперь это выглядит так
![enter image description here](https://i.stack.imgur.com/0eUmU.png)
Загрузил его здесь: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0
Конец обновления
А вот недостающие части из приведенного выше примера кода
<LinearGradientBrush x:Key="wizardBarBrush" StartPoint="0.5,0.0" EndPoint="0.5,1.0">
<GradientStop Color="#FFE4E4E4" Offset="0.25"/>
<GradientStop Color="#FFededed" Offset="0.50"/>
<GradientStop Color="#FFFCFCFC" Offset="0.75"/>
</LinearGradientBrush>
IsLastItemConverter
public class IsLastItemConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ContentPresenter contentPresenter = value as ContentPresenter;
ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(contentPresenter);
int index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter);
return (index == (itemsControl.Items.Count - 1));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
public IsLastItemConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}