Реализация мастера управления прогрессом в WPF - PullRequest
25 голосов
/ 07 октября 2011

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

Ответы [ 3 ]

71 голосов
/ 16 октября 2011

Трудно сказать, какова лучшая практика в этом случае, но вот как я бы это сделал.

Элемент управления мастера на вашем скриншоте выглядит как комбинация ProgressBar и ItemsControl, и в этом случае мне кажется проще извлечь из ItemsControl и реализовать функциональность прогресса, чем наоборот, но также зависит от того, как вы хотите, чтобы это работало (если вы хотите плавный прогресс или просто подсвечиваете точки, например, одну за другой) .

Используя UniformGrid в качестве ItemsPanel и ItemTemplate ниже, мы получаем следующий вид (Steps - это List строк)
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>

Добавление DropShadowEffect к ItemsPanel, два Path элемента к ItemTemplate и два DataTriggers, чтобы определить, является ли текущий элемент первым или последним элементом, чтобы показать / скрыть левый / правый Path и мы можем получить очень похожий вид на ваш скриншот
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>

Если вы решите использовать этот подход, вы, вероятно, сможете решить, как сделать все остальное, как

  • Реализуйте это в пользовательском элементе управления многократного использования
  • Получите штрих (DropShadowEffect) только для части прогресса, а не в тексте
  • Реализация функциональности прогресса и т. Д.

В любом случае, я загрузил пример проекта с пользовательским элементом управления WizardProgressBar и демонстрационный проект, использующий его здесь: https://www.dropbox.com/s/ng9vfi6uwn1peot/WizardProgressBarDemo2.zip?dl=0

Это выглядит так
enter image description here

Что нужно отметить по поводу образца

  • Я попал в ситуацию, когда я получу DropShadowEffect на прогресс-части и заголовках или получу тонкую грань между каждым элементом (как видно на рисунке). Я не могу придумать простой способ избавиться от него, так что, возможно, это не самый лучший подход в конце концов:)
  • Прогресс прост. Он просто имеет значение от 0 до 100, и затем конвертер решает, должен ли элемент светиться или нет
  • Этот элемент управления может оказать небольшое влияние на производительность, но я не уверен, так как мой компьютер сегодня работает все медленно ...

Обновление

Внесены некоторые изменения в пример проекта, в котором я разделил презентацию на две ItemsControls, чтобы избавиться от тонких линий между каждым элементом. Теперь это выглядит так
enter image description here
Загрузил его здесь: 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;
    }
}
2 голосов
/ 07 октября 2011

Я тоже сделал нечто подобное.На самом деле это довольно просто в WPF.В основном я создал 2 прямоугольника и перекрыл их.Прямоугольник на заднем плане имеет цвета градиента, а прямоугольник на переднем плане - это серая область, используемая для покрытия прямоугольника градиента.

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

Ниже приведено изображение того, что я сделал, вместе с 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>
0 голосов
/ 13 октября 2011

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

В этой статье рассказывается об общих обтравочных масках в выражении: http://expression.microsoft.com/en-us/cc197119

В этой статье показан код, который может быть немного более актуальным: http://blog.pixelingene.com/2009/02/animating-graphs-in-wpf-using-clipping-masks/ и в этом коде вы можете легко настроить RectangleGeometry во время выполнения.

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

Это маршрут, который я бы, вероятно, выбрал. Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...