在 WPF 中实现向导进度控制 [英] Implementing a wizard progress control in WPF
问题描述
在 WPF 中是否有最好的方法来实现这样的控件?
我可以轻松复制文本标签和进度条(每个标签上方没有圆形凸起"),但我想知道是否已经有控件或最佳实践来创建此类WPF 中的控件.
很难说在这种情况下最佳实践是什么,但我会这样做.
屏幕截图中的向导控件看起来像是 ProgressBar
和 ItemsControl
的组合,在这种情况下,从 ItemsControl<派生似乎更容易/code> 并实现进度功能而不是其他方式,但这也取决于您希望它如何工作(例如,如果您想要平稳的进度或只是一个一个地点亮点).
使用 UniformGrid
作为 ItemsPanel
和下面的 ItemTemplate
,我们得到以下外观(Steps is a List
字符串)
<ItemsControl.ItemsPanel><ItemsPanelTemplate><UniformGrid Rows="1"/></ItemsPanelTemplate></ItemsControl.ItemsPanel><ItemsControl.ItemTemplate><数据模板><网格><Grid.RowDefinitions><RowDefinition Height="自动"/><RowDefinition Height="自动"/></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"/></网格></数据模板></ItemsControl.ItemTemplate></ItemsControl>
将 DropShadowEffect
添加到 ItemsPanel
、两个 Path
元素到 ItemTemplate
和两个 DataTriggers
确定当前项目是显示/隐藏左/右 Path
的第一个还是最后一个项目,我们可以获得与您的屏幕截图非常相似的外观
ItemsPanel
<UniformGrid.Effect><DropShadowEffect 颜色="黑色"模糊半径=5"不透明度=0.6"ShadowDepth="0"/></UniformGrid.Effect></UniformGrid>
ItemTemplate
<DataTemplate.Resources><Style TargetType="路径"><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="透明"/></风格></DataTemplate.Resources><网格><Grid.RowDefinitions><RowDefinition Height="自动"/><RowDefinition Height="自动"/></Grid.RowDefinitions><Grid.ColumnDefinitions><列定义/><列定义/></Grid.ColumnDefinitions><路径名称="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"/></网格><DataTemplate.Triggers><DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}"值="{x:Null}"><Setter TargetName="leftPath" Property="Visibility" Value="Collapsed"/></数据触发器><DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={markup:IsLastItemConverter}}"值="真"><Setter TargetName="rightPath" Property="Visibility" Value="Collapsed"/></数据触发器></DataTemplate.Triggers></数据模板>
如果你决定使用这种方法,你可能会想出如何让剩下的事情继续下去,比如
- 在可重用的自定义控件中实现这一点
- 只在进度部分而不是在文本中获取笔画(
DropShadowEffect
) - 实现进度功能等
无论如何,我上传了一个带有名为 WizardProgressBar
的自定义控件的示例项目和一个使用它的演示项目:
更新结束
这是上面示例代码中缺失的部分
<GradientStop Color="#FFE4E4E4" Offset="0.25"/><GradientStop Color="#FFededed" Offset="0.50"/><GradientStop Color="#FFFCFCFC" Offset="0.75"/></LinearGradientBrush>
IsLastItemConverter
公共类 IsLastItemConverter : MarkupExtension, IValueConverter{公共对象转换(对象值,类型目标类型,对象参数,System.Globalization.CultureInfo 文化){ContentPresenter contentPresenter = 作为 ContentPresenter 的值;ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(contentPresenter);int index = itemsControl.ItemContainerGenerator.IndexFromContainer(contentPresenter);返回(索引==(itemsControl.Items.Count - 1));}公共对象 ConvertBack(对象值,类型目标类型,对象参数,System.Globalization.CultureInfo 文化){抛出新的 NotSupportedException();}公共 IsLastItemConverter() { }公共覆盖对象 ProvideValue(IServiceProvider serviceProvider){返回这个;}}
Is there a best way to implement a control like this in WPF?
I can easily replicate the text labels and the progress bar (without the circular "bumps" above each label) but I'd like to know if there's a control already out there, or a best practice, for creating this sort of control in WPF.
It's hard to say what the best practice is in this case but here is how I would do it.
The wizard control in your screenshot looks like a combination of a ProgressBar
and an ItemsControl
and in this case it seems easier to me to derive from ItemsControl
and implement the progress functionality than the other way around but it also depends on how you want it to work (if you want a smooth progress or just light up the dots one-by-one for example).
Using a UniformGrid
as ItemsPanel
and the ItemTemplate
below, we get the following look (Steps is a List
of strings)
<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>
Adding a DropShadowEffect
to the ItemsPanel
, two Path
elements to the ItemTemplate
and two DataTriggers
to determine if the current item is the first or last item to show/hide the left/right Path
and we can get a pretty similar look to your screenshot
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>
If you decide to use this approach you can probably work out how to get the rest of it going, like
- Implement this in a reusable custom control
- Only get the stroke (
DropShadowEffect
) on the progress-part and not in the text - Implement the progress functionality etc.
Anyway, I uploaded a sample project with a custom control called WizardProgressBar
and a demo project using it here: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0
It looks like this
Things to note about the sample
- I ended up in a situation where I would get the
DropShadowEffect
on the progress-part and the headers or get a thin line between each item (as seen in the picture). I can't think of an easy way to get rid of it so maybe this isn't the best approach after all :) - The progress-part is simple. It just has a value between 0-100 and then a converter decides if the item should be lit or not
- This control might have a small performance impact but I can't be sure since my computer seems to be running everything slow today...
Update
Made a few changes to the sample project where I split the presentation into two ItemsControls
to get rid of the thin lines between each item. It now looks like this
Uploaded it here: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0
End of Update
And here are the missing parts from the sample code above
<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;
}
}
这篇关于在 WPF 中实现向导进度控制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!