在WPF中实现这样的控件有最佳的方法吗?
我可以轻松复制文本标签和进度条(没有每个标签上方的圆形“隆起”),但我想知道是否已经有控件或最佳实践可用于创建这种类型的控件在WPF中。
在这种情况下很难说什么是最佳实践,但以下是我会做的方式。
你截图中的向导控件看起来像是 ProgressBar
和 ItemsControl
的组合,在这种情况下,对我来说从 ItemsControl
派生并实现进度功能似乎更容易,但这也取决于你希望它如何工作(例如,如果你想要平滑的进度条还是只逐个点亮)。
使用下面的 ItemTemplate
和 UniformGrid
作为 ItemsPanel
,我们得到以下外观(Steps 是字符串列表)
<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>
在ItemsPanel
中添加DropShadowEffect
,在ItemTemplate
中添加两个Path
元素,并使用两个DataTriggers
来确定当前项目是否是要显示/隐藏左/右Path
中的第一个或最后一个项目,这样我们可以获得与您的屏幕截图相似的外观。
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
它看起来像这样
有关示例的注意事项
DropShadowEffect
或在每个项目之间获得细线(如图片中所示)。 我想不出轻松摆脱它的方法,所以也许这并不是最好的方法 :)更新
对示例项目进行了一些更改,将演示分成了两个ItemsControls
,以消除每个项目之间的细线。 现在它看起来是这样的:
在此处上传: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;
}
}
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="2">
<Canvas x:Name="canvasMain" Height="80" Width="330" VerticalAlignment="Top" Background="White" SnapsToDevicePixels="True">
<Rectangle x:Name="recMainBar" Height="30" Canvas.Left="0" Canvas.Top="30" Stroke="Black" Width="300">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="1,1" MappingMode="RelativeToBoundingBox" StartPoint="0,0" SpreadMethod="Reflect">
<GradientStop Color="#FFF5400A"/>
<GradientStop Color="#FF54C816" Offset="1"/>
<GradientStop Color="#FF31C614" Offset="0.996"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- Cover of the bar -->
<Rectangle x:Name="recMainBarCover" Height="30" Canvas.Top="30" Canvas.Left="0" Stroke="Black" Width="300" Fill="#FFEBEBEB"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="2.0" TextWrapping="Wrap" Text="0%" Canvas.Top="66.95" Width="16" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="30" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="30" Text="10%" Canvas.Top="66.95" Width="21" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="60" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="60" Text="20%" Canvas.Top="66.95" Width="21" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="90" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="90" Text="30%" Canvas.Top="66.95" Width="21" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="120" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="120" Text="40%" Canvas.Top="66.95" Width="21" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="150" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="145" FontWeight="Bold" Text="50%" Canvas.Top="66.95" Width="31" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="180" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="180" Text="60%" Canvas.Top="66.95" Width="27" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="210" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="210" Text="70%" Canvas.Top="66.95" Width="27" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="240" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="240" Text="80%" Canvas.Top="66.95" Width="27" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="270" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="270" Text="90%" Canvas.Top="66.95" Width="27" RenderTransformOrigin="-0.051,-0.9"/>
<Path Data="M8,34 L24.5,48.95" Fill="Green" Height="6.95" Stretch="Fill" Stroke="Black" Canvas.Left="300" Canvas.Top="60" Width="5"/>
<TextBlock FontSize="10" Height="15" Canvas.Left="300" Text="100%" Canvas.Top="66.95" Width="27" RenderTransformOrigin="-0.051,-0.9"/>
<TextBlock Name="txtTitle" FontSize="16" FontWeight="Bold" Background="Black" Foreground="White" Height="30" Canvas.Left="0" Text="Confidence Factor" Canvas.Top="0" Width="330" HorizontalAlignment="Center" TextAlignment="Center"/>
</Canvas>
</Border>
ItemsControl
(或其他列表)并选择当前步骤或索引,但我也愿意考虑其他选项。 - Adam Maras