How to use calculated and customized DependecyProperty in UserControl? - wpf

I want to make an EllipticArc for a project and I created a UserControl to make it. In my code I need to calculate the EndPoint of the Arc according to the Angle , Radius and IsLargeArc. I wrote the code below but it didn't work, so nothing show up in the designer. I can't understand why this is happening. How can I solve it?
EllipticArc.cs
public partial class EllipticArc: UserControl {
public EllipticArc() {
InitializeComponent();
}
public double Radius {
get {
return (int) GetValue(RadiusProperty);
}
set {
SetValue(RadiusProperty, value);
}
}
// Using a DependencyProperty as the backing store for Yaricap. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(EllipticArc), new PropertyMetadata(20 d));
public Point BeginPoint {
get {
return (Point) GetValue(BeginPointProperty);
}
set {
SetValue(BeginPointProperty, value);
}
}
// Using a DependencyProperty as the backing store for BeginPoint. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BeginPointProperty = DependencyProperty.Register("BeginPoint", typeof(Point), typeof(EllipticArc), new PropertyMetadata(new Point(0 d, 0 d)));
public double Angle {
get {
return (int) GetValue(AngleProperty);
}
set {
SetValue(AngleProperty, value);
}
}
// Using a DependencyProperty as the backing store for Angle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AngleProperty = DependencyProperty.Register("Angle", typeof(double), typeof(EllipticArc), new PropertyMetadata(90 d));
public bool IsLarge {
get {
SetValue(IsLargeProperty, Angle >= 180 ? true: false);
return (bool) GetValue(IsLargeProperty);
}
set {
SetValue(IsLargeProperty, value);
}
}
// Using a DependencyProperty as the backing store for IsLarge. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLargeProperty = DependencyProperty.Register("IsLarge", typeof(bool), typeof(EllipticArc), new PropertyMetadata(false));
Point CalculateEndPoint(Point beginPoint, double radius, double angle) {
beginPoint.X = beginPoint.X + radius;
beginPoint.Y = beginPoint.Y + radius;
double xplus = Math.Sin(angle) * radius + beginPoint.X;
double yplus = Math.Cos(angle) * radius + beginPoint.Y;
return new Point(xplus, yplus);
}
public Point EndPoint {
get {
SetValue(EndPointProperty, CalculateEndPoint(BeginPoint, Radius, Angle));
return (Point) GetValue(EndPointProperty);
}
set {
SetValue(EndPointProperty, value);
}
}
// Using a DependencyProperty as the backing store for EndPoint. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EndPointProperty = DependencyProperty.Register("EndPoint", typeof(Point), typeof(EllipticArc), new PropertyMetadata(0));
public Size BarSize {
get {
SetValue(BarSizeProperty, new Size(Radius * 2, Radius * 2));
return (Size) GetValue(BarSizeProperty);
}
set {
SetValue(BarSizeProperty, value);
}
}
// Using a DependencyProperty as the backing store for BarSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BarSizeProperty = DependencyProperty.Register("BarSize", typeof(Size), typeof(EllipticArc), new PropertyMetadata(new Size(40, 40)));
}
EllipticArc.xaml
<UserControl x:Class="WpfApplication2.EllipticArc"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Path>
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="{Binding BeginPoint}">
<ArcSegment Size="{Binding BarSize}" Point="{Binding EndPoint}"
SweepDirection="Clockwise" IsLargeArc="{Binding IsLarge}"
RotationAngle="0"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
</UserControl>

Related

Why ActualWidth is always 0 after DrawingContext drew?

Recently I have been trying to rewrite TextBlock for letterspacing. Now I almost achieved this by the DrawingContext.
Here is my code:
public class STextBlock : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(STextBlock), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender));
public double LetterSpacing
{
get { return (double)GetValue(LetterSpacingProperty); }
set { SetValue(LetterSpacingProperty, value); }
}
// Using a DependencyProperty as the backing store for LetterSpacing. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LetterSpacingProperty =
DependencyProperty.Register("LetterSpacing", typeof(double), typeof(STextBlock), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
// Using a DependencyProperty as the backing store for FontStyle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontStyleProperty =
DependencyProperty.Register("FontStyle", typeof(FontStyle), typeof(STextBlock), new FrameworkPropertyMetadata(FontStyles.Normal, FrameworkPropertyMetadataOptions.AffectsRender));
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
// Using a DependencyProperty as the backing store for Foreground. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ForegroundProperty =
DependencyProperty.Register("Foreground", typeof(Brush), typeof(STextBlock), new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Black), FrameworkPropertyMetadataOptions.AffectsRender));
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
// Using a DependencyProperty as the backing store for FontStretch. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontStretchProperty =
DependencyProperty.Register("FontStretch", typeof(FontStretch), typeof(STextBlock), new FrameworkPropertyMetadata(FontStretches.Normal, FrameworkPropertyMetadataOptions.AffectsRender));
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
// Using a DependencyProperty as the backing store for FontFamily. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontFamilyProperty =
DependencyProperty.Register("FontFamily", typeof(FontFamily), typeof(STextBlock), new FrameworkPropertyMetadata(new FontFamily(), FrameworkPropertyMetadataOptions.AffectsRender));
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
// Using a DependencyProperty as the backing store for FontSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontSizeProperty =
DependencyProperty.Register("FontSize", typeof(double), typeof(STextBlock), new FrameworkPropertyMetadata(12.0, FrameworkPropertyMetadataOptions.AffectsRender));
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
// Using a DependencyProperty as the backing store for FontWeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontWeightProperty =
DependencyProperty.Register("FontWeight", typeof(FontWeight), typeof(STextBlock), new FrameworkPropertyMetadata(FontWeights.Normal, FrameworkPropertyMetadataOptions.AffectsRender));
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
List<FormattedTextToDrawModel> FormattedTextToDrawList = new List<FormattedTextToDrawModel>();
double PositionX = Padding.Left;
double PositionY = Padding.Top;
if (!string.IsNullOrEmpty(Text))
{
foreach (var i in Text)
{
var FT = new FormattedText(i.ToString(), CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Foreground);
FormattedTextToDrawList.Add(new FormattedTextToDrawModel() { FormattedText = FT, DrawPoint = new Point(PositionX, PositionY) });
PositionX += FT.Width + LetterSpacing;
if (PositionX > ActualSize.Width)
{
ActualSize.Width = PositionX;
}
if (PositionY + FT.Height > ActualSize.Height)
{
ActualSize.Height = PositionY + FT.Height;
}
}
}
ActualSize.Width += Padding.Right;
ActualSize.Height += Padding.Top;
drawingContext.DrawRectangle(Background, new Pen(), new Rect(ActualSize));
foreach (var i in FormattedTextToDrawList)
{
drawingContext.DrawText(i.FormattedText, i.DrawPoint);
}
}
public struct FormattedTextToDrawModel
{
public FormattedText FormattedText { get;init;}
public Point DrawPoint { get; init; }
}
Size ActualSize = new Size(0, 0);
}
I test the code above like this:
<Border VerticalAlignment="Center" HorizontalAlignment="Center">
<Test:STextBlock Margin="10" x:Name="TB" Text="123123" Padding="20" LetterSpacing="4" FontFamily="Comic Sans MS" FontSize="20" Background="Red"></Seal:STextBlock>
</Border>
Now I have a problem. After the DrawingContext drew the background and text. The ActualWidth and ActualHeight not only of the STextBlock self but also of its parent is still being 0. What's wrong with it?
Is there any way I can set the ActualWidth and ActualHeight by myself?
Actual width and height are calculated internally as part of the measure arrange layout pass.
Each control being laid out is asked for it's desired size.
How much space would it like?
That's then compared to what it can have from parent containers etc and it may not get what it desires.
As an example.
A textblock which cannot have the width it would like will have it's text truncated or it will overflow onto the next line.
FrameworkElement.ActualWidth is read only
Note only {get;}
https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.actualwidth?view=windowsdesktop-7.0
You don't set that directly yourself.
You should instead override measure. I think you've sort of done those calculations.
protected override Size MeasureOverride(Size constraint)
{
Size desiredSize = new Size();
// Calculate how big your text wants to be
return desiredSize;
}

Binding ElementName return an instance from a previously instancied DataTemplate

I have the following DataTemplate
<DataTemplate x:Key="ArchiveModeContentTemplate">
<Button Style="{x:Static ui:ButtonStyles.DrawingButtonLabel}" Grid.Row="1" Grid.Column="0" Foreground="{x:Static ui:UbiBrushes.UbiDarkBlue}"
Content="{StaticResource ValidateIcon48}" ui:StyleProperties.Label="{DynamicResource Archive}"
Command="{Binding ElementName=factory,Path=BuildPopup}">
<i:Interaction.Behaviors>
<pop:PopupFactory x:Name="factory" Factory="{Binding ConfirmArchivingFactory}" />
</i:Interaction.Behaviors>
</Button>
</DataTemplate>
PopupFactory has a Command BuildPopup. this Command is given to the button with a binding with ElementName.
The first time this dataTemplate is displayed, it work fine. The button get the command. But if this dataTemplate is unloaded then displayed again, the binding give to the button the command of the previous instance of PopupFactory and not the newly created instance.
I pass in the constructor of PopupFactory and it is attached to the new button. So it is not a problem of PopupFactory being shared between templates.
Why this is happening? is it a bug with a the xaml cache?
Edit
I have an even stranger bug now.
I changed the syntax to the following to have the binding elementName after the name declaration in the Xaml. Now the command is working correctly but the the second button which is using a binding RelativeSource to find a command named GoBack don't work anymore. I used snoop to check the binding and it complain that it can't find the command BuildPopup. WPF is getting crazy!
<Button Style="{x:Static ui:ButtonStyles.DrawingButtonLabel}" Grid.Row="1" Grid.Column="0" Foreground="{x:Static ui:UbiBrushes.UbiDarkBlue}"
Content="{StaticResource ValidateIcon48}" ui:StyleProperties.Label="{DynamicResource Archive}">
<i:Interaction.Behaviors>
<pop:PopupFactory x:Name="Archivefactory" Factory="{Binding ConfirmArchivingFactory}" IsSingleInstance="False" />
</i:Interaction.Behaviors>
<Button.Command>
<Binding ElementName="Archivefactory" Path="BuildPopup" />
</Button.Command>
</Button>
<Button Grid.Row="1" Grid.Column="1"
Style="{x:Static ui:ButtonStyles.DrawingButtonLabel}"
Content="{StaticResource CrossIcon48}"
Foreground="Green"
ui:StyleProperties.Label="{DynamicResource Cancel}"
Command="{Binding Path=GoBack, RelativeSource={RelativeSource AncestorType={x:Type ui:DrillDown}}}" />
Edit
Here the code of PopupFactory
public class PopupFactory : Behavior<UIElement>
{
public ICommand BuildPopup { get; private set; }
private bool _canExecute;
private IDisposable _canexecuteSubscription = null;
public IObservable<bool> CanExecuteSource
{
get { return (IObservable<bool>)GetValue(CanExecuteSourceProperty); }
set { SetValue(CanExecuteSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for CanExecute. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CanExecuteSourceProperty =
DependencyProperty.Register("CanExecute", typeof(IObservable<bool>), typeof(PopupFactory), new PropertyMetadata(null));
private static void OnCanExecuteSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)
{
var factory = obj as PopupFactory;
factory._canexecuteSubscription?.Dispose();
if (arg.NewValue != null)
{
factory._canexecuteSubscription = ((IObservable<bool>)arg.NewValue)
.ObserveOnDispatcher()
.Subscribe(factory.UpdateCanExecute);
}
}
private void UpdateCanExecute(bool value)
{
_canExecute = value;
((RelayCommand<object>)BuildPopup).RaiseCanExecuteChanged();
}
public IFactory Factory
{
get { return (IFactory)GetValue(FactoryProperty); }
set { SetValue(FactoryProperty, value); }
}
// Using a DependencyProperty as the backing store for Factory. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FactoryProperty =
DependencyProperty.Register("Factory", typeof(IFactory), typeof(PopupFactory), new PropertyMetadata(null, OnFactoryChanged));
private static void OnFactoryChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)
{
var factory = obj as PopupFactory;
((RelayCommand<object>)factory.BuildPopup).RaiseCanExecuteChanged();
}
public UIElement PlacementTarget
{
get { return (UIElement)GetValue(PlacementTargetProperty); }
set { SetValue(PlacementTargetProperty, value); }
}
// Using a DependencyProperty as the backing store for PlacementTarget. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PlacementTargetProperty =
DependencyProperty.Register("PlacementTarget", typeof(UIElement), typeof(PopupFactory), new PropertyMetadata(null));
public PlacementMode Placement
{
get { return (PlacementMode)GetValue(PlacementProperty); }
set { SetValue(PlacementProperty, value); }
}
// Using a DependencyProperty as the backing store for Placement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PlacementProperty =
DependencyProperty.Register("Placement", typeof(PlacementMode), typeof(PopupFactory), new PropertyMetadata(PlacementMode.Center));
public bool IsSingleInstance
{
get { return (bool)GetValue(IsSingleInstanceProperty); }
set { SetValue(IsSingleInstanceProperty, value); }
}
// Using a DependencyProperty as the backing store for IsSingleInsance. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSingleInstanceProperty =
DependencyProperty.Register("IsSingleInstance", typeof(bool), typeof(PopupFactory), new PropertyMetadata(false));
private bool _singleInstanceShowed = false;
public PopupFactory()
{
BuildPopup = new RelayCommand<object>((f) =>
{
ShowPopup(f);
}, (p) =>
{
return _canExecute && Factory != null && !_singleInstanceShowed;
});
UpdateCanExecute(true);
}
public IOverlayContainer ShowPopup(object parameter)
{
var param = new PopupParameter() { Owner = AssociatedObject };
UIElement target = PlacementTarget != null ? PlacementTarget : AssociatedObject;
var item = Factory.Build(parameter);
param.Content = item.Item;
param.Owner = AssociatedObject;
param.RemoveCondition = item.DisposeStream;
var container = OverlayManager.ShowPopup(param);
var placement = new PopupRelativePlacement(container as FrameworkElement, target,
Placement, false);
item.PostFactory?.Invoke();
if (IsSingleInstance)
{
_singleInstanceShowed = true;
OverlayManager.PopupOperations.Where((op) => op.Id == container.Id && op.Operationtype == OverlayOperation.OpType.PopupRemoved)
.Once((_) =>
{
_singleInstanceShowed = false;
((RelayCommand<object>)BuildPopup).RaiseCanExecuteChanged();
});
}
return container;
}
}
Problem solved.
I moved the PopupFactory Behavior to a visual parent of the button. This way, the behavior is created before the button and WPF don't mess up the name resolution during the binding.

Binding two dependency properties from two different usercontrols

I have created two separate usercontrols, they are meant to work together.
The first one is a simple usercontrol with a thumb attached to it, the thumb makes the control move around by dragging, this is simple and working.
XAML:
<Canvas>
<Thumb x:Name="Thumb" Width="15" Height="15" DragDelta="Thumb_DragDelta"/>
</Canvas>
Code-Behind: A dependency property called Position, when Setter is called it updates the usercontrol's margin.
public partial class ThumbPoint : UserControl
{
public Point Position
{
get { return (Point)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); this.Margin = new Thickness(value.X, value.Y, 0, 0); }
}
// Using a DependencyProperty as the backing store for Position. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register("Position", typeof(Point), typeof(ThumbPoint), new PropertyMetadata(new Point()));
public ThumbPoint()
{
InitializeComponent();
Position = new Point(0, 0);
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Position = new Point(Position.X + e.HorizontalChange, Position.Y + e.VerticalChange);
}
}
The second UserControl is called StraightLine, its composed of a Line control
XAML:
<Canvas>
<Line x:Name="Line" Stroke="Gray" StrokeThickness="1"/>
</Canvas>
Code-Behind: A dependency property called StartPosition, when Setter is called it updates the Line X1 and Y1 (starting position of the line).
public partial class StraightLine : UserControl
{
public Point StartPosition
{
get { return (Point)GetValue(StartPositionProperty); }
set { SetValue(StartPositionProperty, value); Line.X1 = value.X; Line.Y1 = value.Y; }
}
// Using a DependencyProperty as the backing store for StartPosition. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StartPositionProperty =
DependencyProperty.Register("StartPosition", typeof(Point), typeof(StraightLine), new PropertyMetadata(new Point()));
public StraightLine()
{
InitializeComponent();
Line.X1 = 0;
Line.Y1 = 0;
Line.X2 = 300;
Line.Y2 = 200;
}
}
Here I am trying to bind them together on the mainwindow.xaml:
<Canvas>
<local:ThumbPoint x:Name="ThumbPoint"/>
<local:StraightLine StartPosition="{Binding Position, ElementName=ThumbPoint}"/>
</Canvas>
Desired effect: DependencyProperty StartPosition of the StraightLine should be updated.
Whats happening: It's not being updated so only the ThumbPoint is moving.
binding doesn't use common property wrappers for DP (public Point StartPosition), it uses SetValue() directly, so code in setter isn't invoked.
What is needed is propertyChangedCallback:
public Point StartPosition
{
get { return (Point)GetValue(StartPositionProperty); }
set { SetValue(StartPositionProperty, value); }
}
public static readonly DependencyProperty StartPositionProperty =
DependencyProperty.Register("StartPosition", typeof(Point), typeof(StraightLine), new PropertyMetadata(new Point(), OnStartPositionChanged));
private static void OnStartPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
StraightLine c = (StraightLine) d;
c.Line.X1 = c.StartPosition.X;
c.Line.Y1 = c.StartPosition.Y;
}
in ThumbPoint public Point Position property has the same issue but it works there because you use setter directly: Position = new Point()
Alternatively bind X1 and Y1 value in xaml:
<Canvas>
<Line x:Name="Line" Stroke="Gray" StrokeThickness="1"
X1="{Binding StartPosition.X, RelativeSource={RelativeSource AncestorType=StraightLine}}"
Y1="{Binding StartPosition.Y, RelativeSource={RelativeSource AncestorType=StraightLine}}"/>
</Canvas>
(or use ElementName instead of RelativeSource)

WPF circular progress bar: how to move hard coded property

In my application i am using Circular progress-bar.
So in case i want to use this controller in several places how can i set the Radius property in my XAML instead of using the current value which is all the time 100 ? (in class CircularProgressBar)
This is my Circular progress bar:
<UserControl x:Class="myApplication.CircularProgressBar"
x:Name="userControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid>
<Path x:Name="pathRoot" Stroke="{Binding SegmentColor, ElementName=userControl}"
StrokeThickness="{Binding StrokeThickness, ElementName=userControl}"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="100" Width="100">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure x:Name="pathFigure">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment x:Name="arcSegment" SweepDirection="Clockwise" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
</Grid>
</UserControl>
public partial class CircularProgressBar : UserControl
{
public CircularProgressBar()
{
InitializeComponent();
Angle = (Percentage * 360) / 100;
RenderArc();
}
public int Radius
{
get { return (int)GetValue(RadiusProperty); }
set { SetValue(RadiusProperty, value); }
}
public Brush SegmentColor
{
get { return (Brush)GetValue(SegmentColorProperty); }
set { SetValue(SegmentColorProperty, value); }
}
public int StrokeThickness
{
get { return (int)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public double Percentage
{
get { return (double)GetValue(PercentageProperty); }
set { SetValue(PercentageProperty, value); }
}
public double Angle
{
get { return (double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
public enum Modes
{
Full = 360,
Half = 180,
Intermediate = 250
}
public Modes CircularMode
{
get { return (Modes)GetValue(CircularModeProperty); }
set { SetValue(CircularModeProperty, value); }
}
public static readonly DependencyProperty CircularModeProperty =
DependencyProperty.Register("CircularMode", typeof(Modes), typeof(CircularProgressBar), new PropertyMetadata(Modes.Full));
// Using a DependencyProperty as the backing store for Percentage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PercentageProperty =
DependencyProperty.Register("Percentage", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(65d, new PropertyChangedCallback(OnPercentageChanged)));
// Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StrokeThicknessProperty =
DependencyProperty.Register("StrokeThickness", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(1));
// Using a DependencyProperty as the backing store for SegmentColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SegmentColorProperty =
DependencyProperty.Register("SegmentColor", typeof(Brush), typeof(CircularProgressBar), new PropertyMetadata(new SolidColorBrush(Colors.Red)));
// Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RadiusProperty =
DependencyProperty.Register("Radius", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(100, new PropertyChangedCallback(OnPropertyChanged)));
// Using a DependencyProperty as the backing store for Angle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(120d, new PropertyChangedCallback(OnPropertyChanged)));
private static void OnPercentageChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
CircularProgressBar circle = sender as CircularProgressBar;
circle.Angle = (circle.Percentage * (int)circle.CircularMode) / 100;
}
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
CircularProgressBar circle = sender as CircularProgressBar;
circle.RenderArc();
}
public void RenderArc()
{
Point startPoint = new Point(Radius, 0);
Point endPoint = ComputeCartesianCoordinate(Angle, Radius);
endPoint.X += Radius;
endPoint.Y += Radius;
pathRoot.Width = Radius * 2 + StrokeThickness;
pathRoot.Height = Radius * 2 + StrokeThickness;
pathRoot.Margin = new Thickness(StrokeThickness, StrokeThickness, 0, 0);
bool largeArc = Angle > 180.0;
Size outerArcSize = new Size(Radius, Radius);
pathFigure.StartPoint = startPoint;
if (startPoint.X == Math.Round(endPoint.X) && startPoint.Y == Math.Round(endPoint.Y))
endPoint.X -= 0.01;
arcSegment.Point = endPoint;
arcSegment.Size = outerArcSize;
arcSegment.IsLargeArc = largeArc;
}
private Point ComputeCartesianCoordinate(double angle, double radius)
{
// convert to radians
double angleRad = (Math.PI / 180.0) * (angle - 90);
double x = radius * Math.Cos(angleRad);
double y = radius * Math.Sin(angleRad);
return new Point(x, y);
}
}
This is the hard code value:
// Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RadiusProperty =
DependencyProperty.Register("Radius", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(100, new PropertyChangedCallback(OnPropertyChanged)));
100 is just the default value of the Radius property. You should be able to set it to any int value you want just like you set any other dependency property:
<local:CircularProgressBar Radius="200" ... />

Change my Circular Progress-Bar into half circle

I have Circular Progress-Bar:
<Grid>
<Path x:Name="pathRoot" Stroke="{Binding SegmentColor, ElementName=userControl}"
StrokeThickness="{Binding StrokeThickness, ElementName=userControl}"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="100" Width="100">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure x:Name="pathFigure">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment x:Name="arcSegment" SweepDirection="Clockwise" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
</Grid>
/// <summary>
/// Interaction logic for CircularProgressBar.xaml
/// </summary>
public partial class CircularProgressBar : UserControl
{
public CircularProgressBar()
{
InitializeComponent();
Angle = (Percentage * 360) / 100;
RenderArc();
}
public int Radius
{
get { return (int)GetValue(RadiusProperty); }
set { SetValue(RadiusProperty, value); }
}
public Brush SegmentColor
{
get { return (Brush)GetValue(SegmentColorProperty); }
set { SetValue(SegmentColorProperty, value); }
}
public int StrokeThickness
{
get { return (int)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public double Percentage
{
get { return (double)GetValue(PercentageProperty); }
set { SetValue(PercentageProperty, value); }
}
public double Angle
{
get { return (double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
// Using a DependencyProperty as the backing store for Percentage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PercentageProperty =
DependencyProperty.Register("Percentage", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(65d, new PropertyChangedCallback(OnPercentageChanged)));
// Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StrokeThicknessProperty =
DependencyProperty.Register("StrokeThickness", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(1));
// Using a DependencyProperty as the backing store for SegmentColor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SegmentColorProperty =
DependencyProperty.Register("SegmentColor", typeof(Brush), typeof(CircularProgressBar), new PropertyMetadata(new SolidColorBrush(Colors.Red)));
// Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RadiusProperty =
DependencyProperty.Register("Radius", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(80, new PropertyChangedCallback(OnPropertyChanged)));
// Using a DependencyProperty as the backing store for Angle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(120d, new PropertyChangedCallback(OnPropertyChanged)));
private static void OnPercentageChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
CircularProgressBar circle = sender as CircularProgressBar;
circle.Angle = (circle.Percentage * 360) / 100;
}
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
CircularProgressBar circle = sender as CircularProgressBar;
circle.RenderArc();
}
public void RenderArc()
{
Point startPoint = new Point(Radius, 0);
Point endPoint = ComputeCartesianCoordinate(Angle, Radius);
endPoint.X += Radius;
endPoint.Y += Radius;
pathRoot.Width = Radius * 2 + StrokeThickness;
pathRoot.Height = Radius * 2 + StrokeThickness;
pathRoot.Margin = new Thickness(StrokeThickness, StrokeThickness, 0, 0);
bool largeArc = Angle > 180.0;
Size outerArcSize = new Size(Radius, Radius);
pathFigure.StartPoint = startPoint;
if (startPoint.X == Math.Round(endPoint.X) && startPoint.Y == Math.Round(endPoint.Y))
endPoint.X -= 0.01;
arcSegment.Point = endPoint;
arcSegment.Size = outerArcSize;
arcSegment.IsLargeArc = largeArc;
}
private Point ComputeCartesianCoordinate(double angle, double radius)
{
// convert to radians
double angleRad = (Math.PI / 180.0) * (angle - 90);
double x = radius * Math.Cos(angleRad);
double y = radius * Math.Sin(angleRad);
return new Point(x, y);
}
}
This controller works find but i want to change a little this shape and i am wonder if it possible to change this Circle into Half Circle, i try to change several properties like Angle but my Circle hasn't changed.
Is it possible to do that or i need to change the whole class ?

Resources