Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

Изогнутый Progress Bar c# WPF

#c_sharp #wpf #progress_bar


Ребят, подскажите как сделать полукруглый прогресс бар? С обычным вообще не возникает
проблем. Находил только больно уж сложные, круглые примеры 
    


Ответы

Ответ 1



Просто тут и не будет, придется помудрить с геометрией. Ну что ж, давайте создадим UserControl, я назвал его MyProgress. Рисовать будем с помощью Path. Контрол будет состоять из двух Path - закрашенного и незакрашенного (закрашенного другим цветом). Для упрощения расчетов я взял эллипс с радиусами 100 и центром в точке (100,100). Нам нужно нарисовать 2 луча из центра и дугу: Точку я рассчитал для варианта 75% заполненности, вот что уже получается: Теперь из этого сектора нужно вырезать круг меньшего диаметра, сделаем это с помощью CombinedGeometry в режиме Exclude: Теперь аналогично рисуем вторую часть другим цветом: С разметкой пока всё, останется только привязать координаты рассчитанной точки в ArcSegment. Займемся кодом контрола: Свойство зависимости Value: public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(MyProgress), new FrameworkPropertyMetadata(OnValueChanged)); Вспомогательное свойство зависимости с координатами точки дуги: protected static DependencyProperty AuxiliaryPointProperty = DependencyProperty.Register("AuxiliaryPoint", typeof(Point), typeof(MyProgress)); Стандартные оболочки для этих свойств: public double Value { get => (double)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } protected Point AuxiliaryPoint { get => (Point)GetValue(AuxiliaryPointProperty); set => SetValue(AuxiliaryPointProperty, value); } Ну и, наконец, метод, который будет пересчитывать координаты точки при смене Value: static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var myProgress = (MyProgress)d; var value = (double)e.NewValue; var angle = Math.PI * value / 100; var x = 100 - 100 * Math.Cos(angle); var y = 100 - 100 * Math.Sin(angle); myProgress.AuxiliaryPoint = new Point(x, y); } Теперь в разметке привяжемся к рассчитанной точке: Point="{Binding ElementName=myProgress, Path=AuxiliaryPoint}" Это нужно сделать для обоих ArcSegment В разметке я дал имя контролу для упрощения кода привязки: Всё. Это уже минимально рабочий прогрессбар, который можно добавить в окно: Выглядит так: Имейте ввиду, чтобы сделать этот контрол более-менее универсальным, вам потребуется его еще доработать, например, вынести в свойства зависимости цвета дуг и фона, минимальное и максимальное значение прогрессбара, диаметр внешнего и внутреннего круга и т.д. Привожу код контрола полностью, MyProgress.xaml: MyProgress.xaml.cs: using System; using System.Windows; using System.Windows.Controls; namespace WpfProgress { public partial class MyProgress : UserControl { public MyProgress() { InitializeComponent(); } public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(MyProgress), new FrameworkPropertyMetadata(OnValueChanged)); protected static DependencyProperty AuxiliaryPointProperty = DependencyProperty.Register("AuxiliaryPoint", typeof(Point), typeof(MyProgress)); static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var myProgress = (MyProgress)d; var value = (double)e.NewValue; var angle = Math.PI * value / 100; var x = 100 - 100 * Math.Cos(angle); var y = 100 - 100 * Math.Sin(angle); myProgress.AuxiliaryPoint = new Point(x, y); } public double Value { get => (double)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } protected Point AuxiliaryPoint { get => (Point)GetValue(AuxiliaryPointProperty); set => SetValue(AuxiliaryPointProperty, value); } } } Раз UserControl несемантично, я сделал стиль (благодаря помощи @VladD) для ProgressBar. Потом решил добавить возможность редактирования внутреннего радиуса из разметки. Но аттачед проперти, меняющее поведение контрола, тоже несемантично, поэтому в итоге остановился на Custom Control, наследованном от ProgressBar. Идея используется та же, что описана в решении выше, но теперь контрол доработан полностью (наверное), поэтому разметка усложнилась из-за кучи привязок, а также из-за реализованного режима Indeterminate. Для тех, кто хочет создать такой контрол - добавьте в проект Add - New Item - Custom Control WPF, я назвал его SemicircleProgressBar, код самого контрола предельно прост - добавлено одно свойство зависимости: public class SemicircleProgressBar : ProgressBar { static SemicircleProgressBar() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SemicircleProgressBar), new FrameworkPropertyMetadata(typeof(SemicircleProgressBar))); } public static DependencyProperty CuttingFactorProperty = DependencyProperty.Register(nameof(CuttingFactor), typeof(double), typeof(SemicircleProgressBar), new FrameworkPropertyMetadata(0.8)); public double CuttingFactor { get => (double)GetValue(CuttingFactorProperty); set => SetValue(CuttingFactorProperty, value); } } В появившемся файле Generic.xaml в папке Themes в корне проекта разметим стиль нового контрола: Конвертеры. ValueToAuxiliaryPointConverter: class ValueToAuxiliaryPointConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Contains(DependencyProperty.UnsetValue)) return DependencyProperty.UnsetValue; var v = (double)values[0]; var min = (double)values[1]; var max = (double)values[2]; var r = (double)values[3]; var ratio = (v - min) / (max - min); var angle = Math.PI * ratio; var x = 1 - r * Math.Cos(angle); var y = 1 - r * Math.Sin(angle); return new Point(x, y); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } RadiusToSizeConverter: class RadiusToSizeConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var r = (double)value; return new Size(r, r); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } Код для демонстрации: Использована информация из этого ответа

Комментариев нет:

Отправить комментарий