Как показать PART_Indicator, когда значение равно 0 в ProgressBar?WPF - PullRequest
0 голосов
/ 31 мая 2018

У меня есть ProgressBar с пользовательским стилем, я пытаюсь показать эллипс в начале бара, но когда ProgressBar.Value = 0 эллипс не отображается, только когда ProgressBar.Value > 5, естьспособ заставить этот PART_Indicator показывать эллипс?

Вот полный и воспроизводимый пример, просто нажмите на кнопку запуска, и он запустит таймер:

Любые идеи приветствуются, спасибо ваванс!

Кодовый код:

namespace ProgressBar
{
    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Threading;

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private TimeSpan _elapsedTime;

        private TimeSpan _estimatedTotalTime;

        private bool _isIndeterminate;

        private DispatcherTimer _progressBarTimer;

        private double _timeProgress;

        public MainWindow()
        {
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public TimeSpan ElapsedTime
        {
            get => _elapsedTime;
            set
            {
                _elapsedTime = value;
                NotifyPropertyChanged();
            }
        }

        public TimeSpan EstimatedTotalTime
        {
            get => _estimatedTotalTime;
            set
            {
                _estimatedTotalTime = value;
                NotifyPropertyChanged();
            }
        }

        public bool IsIndeterminate
        {
            get => _isIndeterminate;
            set
            {
                _isIndeterminate = value;
                NotifyPropertyChanged();
            }
        }

        public DispatcherTimer ProgressBarTimer
        {
            get => _progressBarTimer;
            set
            {
                _progressBarTimer = value;
                NotifyPropertyChanged();
            }
        }

        public double TimeProgress
        {
            get => _timeProgress;
            set
            {
                _timeProgress = value;
                NotifyPropertyChanged();
            }
        }

        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        private void OnStart(object sender, RoutedEventArgs routedEventArgs)
        {
            EstimatedTotalTime = new TimeSpan(0, 0, 60);
            ElapsedTime = new TimeSpan();

            ProgressBarTimer = new DispatcherTimer(
                                   new TimeSpan(0, 0, 1),
                                   DispatcherPriority.Normal,
                                   OnTick,
                                   Dispatcher.CurrentDispatcher) {
                                                                    IsEnabled = false 
                                                                 };
            ProgressBarTimer.Start();
        }

        private void OnTick(object sender, EventArgs e)
        {
            ElapsedTime = ElapsedTime.Add(new TimeSpan(0, 0, 1));

            if (ElapsedTime.TotalSeconds.Equals(EstimatedTotalTime.TotalSeconds))
            {
                IsIndeterminate = true;

                return;
            }

            TimeProgress = ElapsedTime.TotalSeconds * 100 / EstimatedTotalTime.TotalSeconds;
        }
    }
}

XAML:

<Window x:Class="ProgressBar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:ProgressBar"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        mc:Ignorable="d">
    <Window.Resources>
        <Style x:Key="LevelMeterProgressBarStyle"
               TargetType="{x:Type ProgressBar}">
            <Setter Property="Background" Value="#FFBDBDBD" />
            <Setter Property="Foreground" Value="#FF348781" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ProgressBar}">
                        <Grid Name="TemplateRoot">

                            <Rectangle Name="PART_Track"
                                       Height="5.5"
                                       HorizontalAlignment="Stretch"
                                       VerticalAlignment="Center"
                                       Fill="{TemplateBinding Background}"
                                       SnapsToDevicePixels="True" />

                            <Decorator x:Name="PART_Indicator"
                                       HorizontalAlignment="Left"
                                       VerticalAlignment="Center">

                                <Grid HorizontalAlignment="Stretch"
                                      VerticalAlignment="Stretch">

                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>

                                    <Rectangle Grid.Column="0"
                                               Height="5.5"
                                               VerticalAlignment="Center"
                                               Fill="{TemplateBinding Foreground}" />

                                    <Ellipse Grid.Column="1"
                                             Width="33"
                                             Height="33"
                                             VerticalAlignment="Center"
                                             Fill="{TemplateBinding Foreground}"
                                             Stretch="Fill" />
                                </Grid>
                            </Decorator>

                            <Border BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    CornerRadius="2" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <Button Margin="20"
                HorizontalAlignment="Center"
                Click="OnStart"
                Content="Start" />
        <TextBlock Margin="20">
            <TextBlock.Text>
                <MultiBinding StringFormat="{}{0:00}:{1:00}:{2:00} / {3:00}:{4:00}:{5:00}">
                    <Binding Path="EstimatedTotalTime.Hours" />
                    <Binding Path="EstimatedTotalTime.Minutes" />
                    <Binding Path="EstimatedTotalTime.Seconds" />
                    <Binding Path="ElapsedTime.Hours" />
                    <Binding Path="ElapsedTime.Minutes" />
                    <Binding Path="ElapsedTime.Seconds" />
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
        <ProgressBar Margin="20"
                     IsIndeterminate="{Binding IsIndeterminate}"
                     Maximum="100"
                     Minimum="0"
                     Style="{DynamicResource LevelMeterProgressBarStyle}"
                     Value="{Binding TimeProgress}" />
    </StackPanel>
</Window>

Ответы [ 2 ]

0 голосов
/ 04 июня 2018

Самое быстрое решение - установить MinWidth="33" на "PART_Indicator" - MinWidth, который имеет приоритет над Width, поэтому эллипс всегда будет полностью виден.Недостатком этого подхода является то, что эллипс будет оставаться «неподвижным» до тех пор, пока значение не станет достаточно высоким, чтобы общая ширина индикатора превышала ширину эллипса (я думаю, что это было 5 в вашем случае), что я считаю нежелательным.

Ключом к решению является перемещение эллипса за пределы "PART_Indicator", как упоминалось @GenericTeaCup.Идея состоит в том, чтобы установить горизонтальное поле для линейки и индикатора, равное половине ширины эллипса, так, чтобы его левая и правая стороны находились точно под центральной точкой эллипса, когда значение соответственно минимальное и максимальное.Таким образом, мы можем легко перемещать эллипс по фактической ширине индикатора и всегда располагать его в том месте, где мы хотим его видеть (по центру непосредственно над правой стороной индикатора).Теперь я не уверен, каковы ваши точные ожидания относительно начала и конца трек-бара, поэтому я дам вам два варианта, которые я считаю наиболее распространенными.

Первый из них выглядит следующим образом (я добавилнекоторая прозрачность эллипса, чтобы разница была отчетливо видна):

Start

End

Этот шаблон должен давать изображенный результат:

<ControlTemplate TargetType="{x:Type ProgressBar}">
    <Grid x:Name="TemplateRoot">
        <Rectangle x:Name="LeftFill" Height="5.5" Width="17" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" />
        <Rectangle x:Name="RightFill" Height="5.5" Width="17" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="{TemplateBinding Background}" SnapsToDevicePixels="True" />
        <Rectangle x:Name="PART_Track" Height="5.5" Margin="16.5,0" HorizontalAlignment="Stretch" VerticalAlignment="Center" Fill="{TemplateBinding Background}" SnapsToDevicePixels="True" />
        <Rectangle x:Name="PART_Indicator" Height="5.5" Margin="16.5,0" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" />
        <Ellipse Width="33" Height="33" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Stretch="Fill" SnapsToDevicePixels="True">
            <Ellipse.RenderTransform>
                <TranslateTransform X="{Binding Source={x:Reference PART_Indicator}, Path=ActualWidth}" />
            </Ellipse.RenderTransform>
        </Ellipse>
        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" SnapsToDevicePixels="True" />
    </Grid>
</ControlTemplate>

Второй вариант:

Start

End

Чтобы получить его, вам просто нужно удалить "LeftFill" и "RightFill" из шаблона.Обратите внимание, что трекбар будет иметь отступ по отношению к самому ProgressBar.

Если вам неудобно использовать RenderTransform на эллипсе, вы можете поместить его в Canvas и связать Canvas.Left свойство вместо.

0 голосов
/ 01 июня 2018

Я думаю, что это не так просто.Ширина индикатора выполнения устанавливается в соответствии с этой формулой в частном методе:

 this._indicator.Width = (this.IsIndeterminate || maximum <= minimum ? 1.0 : (num - minimum) / (maximum - minimum)) * this._track.ActualWidth;

Вам нужно будет написать свой собственный индикатор выполнения на основе RangeBase

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