Ребят, подскажите как сделать полукруглый прогресс бар? С обычным вообще не возникает проблем. Находил только больно уж сложные, круглые примеры
Ответ
Просто тут и не будет, придется помудрить с геометрией.
Ну что ж, давайте создадим 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();
}
}
Код для демонстрации:
Использована информация из этого ответа
Комментариев нет:
Отправить комментарий