2011-10-07 8 views
24

¿Existe alguna forma de implementar un control como este en WPF?Implementación de un control de progreso del asistente en WPF

Wizard Progress Control

puedo replicar fácilmente las etiquetas de texto y la barra de progreso (sin los "baches" circulares sobre cada etiqueta) pero me gustaría saber si hay un control ya está ahí, o una mejor práctica , para crear este tipo de control en WPF.

+0

No creo que haya nada exactamente así ahora. Supongo que quieres que las etiquetas y los porcentajes sean dinámicos, ¿verdad? – Stimul8d

+0

Preferiblemente. Me gustaría poder agregar un 'ItemsControl' (u otra lista) de etiquetas y seleccionar un paso o índice actual, pero estoy abierto a otras opciones. –

Respuesta

63

Es difícil decir que mejor práctica es en este caso, pero aquí es cómo lo haría.

El control asistente en la pantalla se ve como una combinación de un ProgressBar y un ItemsControl y en este caso parece más fácil para mí para derivar de ItemsControl e implementar la funcionalidad progreso luego al revés, pero también depende de cómo se quiero que funcione (si quieres un progreso sin problemas o simplemente iluminas los puntos uno a uno, por ejemplo).

utilizando un UniformGrid como ItemsPanel y la ItemTemplate continuación, obtenemos el siguiente aspecto (Steps es un List de cadenas)
enter image description here

<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> 

Adición de un DropShadowEffect a los ItemsPanel, dos Path elementos a ItemTemplate y dos DataTriggers para determinar si el artículo actual es el primero o el último elemento para mostrar/ocultar la izquierda/derecha Path y podemos conseguir un aspecto bastante similar a la captura de pantalla
enter image description here

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> 

Si decide utilizar este enfoque probablemente pueda entrenamiento cómo hacer funcionar el resto, como

  • implementar esto en un control personalizado resuable
  • Sólo conseguir el accidente cerebrovascular (DropShadowEffect) en la parte progreso y no en el texto
  • implementar la funcionalidad progreso etc.

De todos modos, subido un proyecto de ejemplo con un control personalizado llamado WizardProgressBar y un proyecto de demostración de usarlo aquí: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0

parece que este
enter image description here

cosas a tener en cuenta sobre la muestra

  • terminé en una situación en la que tendría la DropShadowEffect en la parte progreso y los encabezados o de obtener una línea delgada entre cada elemento (como se ve en la imagen). No puedo pensar en una manera fácil de deshacerse de él, así que tal vez este no sea el mejor enfoque después de todo :)
  • La parte de progreso es simple. Simplemente tiene un valor entre 0-100 y luego un convertidor decide si el elemento debe estar encendido o no
  • Este control puede tener un pequeño impacto en el rendimiento pero no puedo estar seguro ya que mi computadora parece estar funcionando todo lento hoy ..

actualización

hizo algunos cambios en el proyecto de ejemplo en el que todo dividido la presentación en dos ItemsControls para deshacerse de las líneas finas entre cada elemento.Ahora parece que esta
enter image description here
subido aquí: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0

Fin de actualización

Y aquí son las partes que faltan en el código de ejemplo anterior

<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; 
    } 
} 
+5

Respuesta increíble, completamente completa. Mereces el doble de la recompensa que recibiste por esta publicación. ¡Gracias! –

+0

Gracias Adam :) Hice algunos cambios en el proyecto de muestra que elimina la delgada línea entre cada elemento. –

+1

+1, bien hecho !! –

2

Hice algo similar a esto también. En realidad es bastante fácil en WPF. Básicamente creé 2 rectángulos y los superpuse. La rejilla en el fondo tiene los colores de degradado y el rectángulo en primer plano es el área gris utilizada para cubrir el rectángulo de degradado.

Simplemente ajuste el ancho del rectángulo gris para dar la ilusión de que la barra se mueve hacia la derecha o hacia la izquierda.

A continuación se muestra una imagen de lo que hice, junto con el XAML.

enter image description here

<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> 
+1

Como dije, es fácil recrear las etiquetas de texto y el progreso (sin un bache sobre cada etiqueta). Estoy buscando exactamente replicar el diseño que publiqué como un control reutilizable. –

0

Se puede dibujar el indicador de progreso completo, configure una máscara de recorte para el indicador de progreso, y, o bien alterar o intercambio que enmascarar con otra máscara en los puntos apropiados durante la ejecución de su programa. Si quieres ser realmente creativo, puedes hacer un control que pueda definir cualquier cantidad arbitraria de puntos.

Este artículo se habla de las máscaras de recorte genéricos en la expresión: http://expression.microsoft.com/en-us/cc197119

este artículo se muestra un código que puede ser un poco más relevante: http://blog.pixelingene.com/2009/02/animating-graphs-in-wpf-using-clipping-masks/ y en este código, aquí se puede ajustar el RectangleGeometry en tiempo de ejecución .

Así que lo que obtengo de toda esta lectura y reflexión es que posiblemente pruebe la propiedad Clip en su indicador de progreso azul, y deje el fondo tal como está.

Esa es la ruta que probablemente tomaría. ¡Espero que esto ayude!

Cuestiones relacionadas