How do i apply a TranslateTransform on an existing 'Children' - c#

I'm rewriting this question to try and make it clearer to people what ive done, trying to do etc.
I am involved in a project where im trying to make some visual displays. One display is a dial, and i pretty much got entire code from this link.
https://github.com/mesta1/WPF-Circular-Gauge/blob/master/CircularGauge/CircularGaugeDemoWPF/CircularGaugeDemoWPF.csproj
I am now trying to do a verticle guage, bit like below.
now ive modified the dial code so background scale etc all works.
The issue im having is the moving indicator.
The dial code defines the dial in the xaml like the following.
<Path x:Name="Pointer" Stroke="#FFE91C1C" StrokeThickness="2"
Width="{TemplateBinding PointerLength}"
Height="{TemplateBinding PointerThickness}"
HorizontalAlignment="Center"
Data="M1,1 L1,10 L156,6 z" Stretch="Fill"
RenderTransformOrigin="0,0.5"
RenderTransform="{Binding RelativeSource=
{RelativeSource TemplatedParent}, Path=PointerLength,
Converter={StaticResource pointerCenterConverter}}">
and in the .cs file controls its position like the following.
TransformGroup tg = pointer.RenderTransform as TransformGroup;
RotateTransform rt = tg.Children[0] as RotateTransform;
rt.Angle = angleValue;
now as im only moving the indicator vertically up and down, i believe that i need a TranslateTransform. With the help of Sheridan. Ive changed my xaml to the following.
<Path x:Name="Pointer" Stroke="#FFE91C1C" StrokeThickness="2"
VerticalAlignment="Bottom"
Data="M 0,0 L 16,-5 L16,5 L0,0 z">
<Path.RenderTransform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
now im struggling with what my .cs needs to be to edit this. Currently im playing with
TransformGroup tg = pointer.RenderTransform as TransformGroup;
TranslateTransform rt = tg.Children[0] as TranslateTransform;
rt.Y = -10;
this however throws error "Cannot set a property on object 'System.Windows.Media.TranslateTransform' because it is in a read-only state."
i have been advised again by Sheridan to try assigning a new object adn replacing the old. But what i seems to try either throws errors or has no effect.

In order to transform a UIElement, you first need to apply a transformation element on it:
<Path ... >
<Path.RenderTransform>
<TranslateTransform />
</Path.RenderTransform>
</Path>
If you are using a TranslateTransform as your RenderTransform property value, then you cannot cast it to a RotateTransform. You can find out more from the UIElement.RenderTransform Property page on MSDN.
TransformGroup tg = pointer.RenderTransform as TransformGroup;
// tg == null
Likewise, if you try to cast this property value to a TransformGroup without setting one, then you will get a null value:
<Path ... >
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform />
<RotateTransform />
</TransformGroup>
</Path.RenderTransform>
</Path>
You can find out more from the TransformGroup Class page on MSDN.
...
TransformGroup tg = pointer.RenderTransform as TransformGroup;
// tg is a TransformGroup

Please find the simple working code of dynamic Transform. I hope It will help you
WPF
<Window x:Class="WPFAnimationSample2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<Ellipse x:Name="animateEllipse" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="61" Margin="99,78,0,0" Stroke="Black" VerticalAlignment="Top" Width="81" RenderTransformOrigin="0.5,0.5" >
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform X="1" Y="1"/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
<Button Content="Button" HorizontalAlignment="Left" Margin="180,256,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void checkAnimation()
{
TransformGroup tg = animateEllipse.RenderTransform as TransformGroup;
TranslateTransform rt = tg.Children[3] as TranslateTransform;
rt.X = rt.X+100;
rt.Y = 100;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
checkAnimation();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
checkAnimation();
}
}

Related

move a XAML border like a car meter

I've got a gauge like a car counter. I put on the gauge center a XAML border like a needle.
I want to move my "needle" from 0 to desired value.
For example the 0 is -133° and my desired value is 15°. And I want to get the needle up to this desired value.
I want to move the needle degre by degre. I use a thread to do that but my needle don't move. It just at -133° and go to 15° directly.
It's the first time I use a thread in c#. I think I didn't do it correctly :)
My XAML needle:
<Border x:Name="Aiguille_Jour" Width="3" Height="45" Grid.Row="3" Grid.Column="3" Background="Black" Margin="0 0 0 40"
VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="120 120 0 0" RenderTransformOrigin="1 1">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-133"/>
<TranslateTransform/>
</TransformGroup>
</Border.RenderTransform>
<Border.Effect>
<DropShadowEffect BlurRadius="5" Color="Black" ShadowDepth="0"/>
</Border.Effect>
</Border>
private void Recup_ET_Affiche_Data()
{
//other code before....
//
Thread thread = new Thread(() =>
{
for(int i= -133; i <= 15; i++)
{
this.Dispatcher.Invoke(() =>
{
Update(i);
});
}
});
thread.Start();
//Other code after...
//
}
private void Update(int i)
{
RotateTransform rt_Jour = new RotateTransform(i);
Aiguille_Jour.RenderTransform = rt_Jour;
Thread.Sleep(10);
}
The other code is to put data in other objects in my window.
should I refresh the display?
Thank you in advance
I solved my problem.
instead of using a border like a needle, i use an image of a needle (more simple)
And I implemente the animation of the image with WPF animation. (more simple too)
[here] https://learn.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/animation-overview?view=netframeworkdesktop-4.8
My border was replace by :
<Image x:Name="AiguilleJour" Source="Aiguille.png" Grid.Row="3" Grid.Column="3" VerticalAlignment="Center" HorizontalAlignment="Center" Panel.ZIndex="500"
Height="45" Margin="0 0 0 40" RenderTransformOrigin="0.5 1">
<Image.RenderTransform>
<RotateTransform x:Name="RotationJour"/>
</Image.RenderTransform>
</Image>
My thread was replace by :
var rotationAnimation = new DoubleAnimation(-133, 15, TimeSpan.FromSeconds(1.5));
RotationJour.BeginAnimation(RotateTransform.AngleProperty, rotationAnimation);
Thank you for the help.

Copy wpf imageSource to another wpf imageSource

I want to copy imageSource to another imageSource. When A window contains image is closed, then I want to make image in A will be copied to B window image and then open B window.
I tried to test with below code. but it wasn`t work.
ADDED :
I upload more code about relevant image code. but im sorry that i cant upload full source code but If you ask, I will edit the related source and upload it.
A a = new A();
B b;
a.Closing += delegate {
b = new B();
b.img.Source = a.img.Source;
b.Show();
};
a.Show();
<!-- a.img xml -->
<Grid x:Name="ImageGrid" Margin="0,36,10,10">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="ScaleTransform"
ScaleX="{Binding ZoomRate}" ScaleY="{Binding ZoomRate}"/>
<RotateTransform/>
</TransformGroup>
</Grid.LayoutTransform>
<Image Name="img" HorizontalAlignment="Left" VerticalAlignment="Top"
Source="{Binding BitmapSource, Mode=TwoWay}"/>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top"
Name="canvas2"
Background="Transparent"
Width="{Binding Path=ActualWidth, ElementName=imgBefore2}"
Height="{Binding Path=ActualHeight, ElementName=imgBefore2}"
MouseLeftButtonDown="Canvas2_MouseLeftButtonDown"
MouseMove="Canvas2_MouseMove"
MouseLeftButtonUp="Canvas2_MouseLeftButtonUp"
MouseRightButtonDown="Canvas2_MouseRightButtonDown">
</Canvas>
</Grid>
<!-- b.img xml -->
<Grid x:Name="ImageGrid"
Background="Transparent"
Width="{Binding MaxWidth, ElementName=ScrollViewer}"
Height="{Binding MaxHeight, ElementName=ScrollViewer}"
MouseMove="Canvas_MouseMove"
MouseLeftButtonDown="Canvas_MouseLeftButtonDown"
MouseLeftButtonUp="Canvas_MouseLeftButtonUp"
MouseWheel="Canvas_MouseWheel">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="ScaleTransform" ScaleX="{Binding ZoomRate}" ScaleY="{Binding ZoomRate}"/>
<RotateTransform/>
</TransformGroup>
</Grid.LayoutTransform>
<Image x:Name="img" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top"
x:Name="canvas"
Background="Transparent"
Width="{Binding ActualWidth, ElementName=imgBefore}"
Height="{Binding ActualHeight, ElementName=imgBefore}"/>
</Grid>
public Mat src;
public BitmapSource BitmapSource {
get
{
return bitmapSource;
}
set
{
bitmapSource = value;
OnPropertyChanged("bitmapSource");
} // ViewModel Code
}
private void LoadImage(System.Drawing.Bitmap bm)
{
src = OpenCvSharp.Extensions.BitmapConverter.ToMat(bm);
OriginWidth = src.Width;
OriginHeight = src.Height;
BitmapSource = OpenCvSharp.Extensions.BitmapSourceConverter.ToBitmapSource(src);
} // Load Image for a.img

Zoom of image with scrollviewer in WPF

I have an Image viewer implemented using ScrollViewer
.XAML
<ScrollViewer Name="viewImg" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<Image Name="bigImage"
Source="/JoyOs.Media;component/img/Plugins/Photos/PHOTO.png"
PreviewMouseLeftButtonUp="ImageMouseLeftButtonUp"
PreviewMouseLeftButtonDown="ImageMouseLeftButtonDown"
PreviewMouseMove="ImageMouseMove" Stretch="Uniform"></Image>
</ScrollViewer>
ScrollViewer class .cs file
public partial class ImageList
{
private void ImageMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var img = (Image)sender;
img.ReleaseMouseCapture();
}
private void ImageMouseMove(object sender, MouseEventArgs e)
{
var img = (Image)sender;
if (!img.IsMouseCaptured) return;
Vector v = _startImgMove - e.GetPosition(viewImg);
viewImg.ScrollToHorizontalOffset(_originImgMove.X - v.X);
viewImg.ScrollToVerticalOffset(_originImgMove.Y - v.Y);
}
private void ImageMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var img = (Image)sender;
img.CaptureMouse();
_startImgMove = e.GetPosition(viewImg);
_originImgMove = new Point(viewImg.HorizontalOffset, viewImg.VerticalOffset);
}
}
I want zoom features implemented in it.Hence I tried to use ZoomBorder.cs class from this.
How to use ZoomBorder.cswith ScrollViewer ?
I tried Without ScrollViewer with only ZoomBorder.cs like this.
<local:ZoomBorder x:Name="border" ClipToBounds="True" Background="Gray">
<Image Name="bigImage"
Source="/JoyOs.Media;component/img/Plugins/Photos/PHOTO.png"
PreviewMouseLeftButtonUp="ImageMouseLeftButtonUp"
PreviewMouseLeftButtonDown="ImageMouseLeftButtonDown"
PreviewMouseMove="ImageMouseMove" Stretch="Uniform"></Image>
</local:ZoomBorder>
I can't answer the question for ZoomBorder's, but I have implemented zoom functionality myself in a different way. I have a ViewBox, which I bind the ScaleTransform of to a value in my ViewModel. Changing the value in my ViewModel then causes the contents of the ViewBox to zoom in and out depending on the value.
<Viewbox x:Name="vwbxZoom" Stretch="None">
<Viewbox.LayoutTransform>
<ScaleTransform ScaleX="{Binding ZoomLevel.Value}"
ScaleY="{Binding ZoomLevel.Value}" />
</Viewbox.LayoutTransform>
</ViewBox>
Here is a quick example. You may use any control as content of the Viewbox
<DockPanel>
<Slider x:Name="ZoomSlider" DockPanel.Dock="Bottom" Minimum="0.05" Maximum="2">
</Slider>
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<Viewbox Stretch="Uniform" VerticalAlignment="Center" HorizontalAlignment="Center">
<Border Width="2000" Height="2000">
<Border.Background>
<LinearGradientBrush StartPoint="0, 0.5" EndPoint="1, 0.5">
<GradientStop Color="Red" Offset="0.33"/>
<GradientStop Color="Green" Offset="0.66"/>
<GradientStop Color="Blue" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<Viewbox.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=ZoomSlider, Path=Value}" ScaleY="{Binding ElementName=ZoomSlider, Path=Value}"/>
</Viewbox.LayoutTransform>
</Viewbox>
</ScrollViewer>
</DockPanel>

Control not properly positioning programmatically

So im trying to place an Ellipse (named Dot) on a random location inside a grid.
But it only spawns on 1/4th (lower right) of the grid instead of the complete grid.
Example: When i put the margin of the Dot to Dot.Margin = new Thickness(0, 0, 0, 0); the Dot will spawn in the center of the screen. When i change it to Dot.Margin = new Thickness(200, 0, 0, 0); it will have a very small offset to the right but not even close to 200 pixels.
Outcome after creating alot of circles without removing them:
http://i.imgur.com/3XCMYyC.png
The red rectangle is the spawn area.
C#
//Gives Dot a position
public void placeDot()
{
//Give Dot random position
// The farthest left the dot can be
double minLeft = 0;
// The farthest right the dot can be without it going off the screen
double maxLeft = spawnArea.ActualWidth - Dot.Width;
// The farthest up the dot can be
double minTop = 0;
// The farthest down the dot can be without it going off the screen
double maxTop = spawnArea.ActualHeight - Dot.Height;
double left = RandomBetween(minLeft, maxLeft);
double top = RandomBetween(minTop, maxTop);
Dot.Margin = new Thickness(left, top, 0, 0);
}
//createEllipse method, used for creating new Dots
public void createEllipse()
{
spawnArea.Children.Remove(Dot);
//Create new Dot
DotImg.ImageSource =
new BitmapImage(new Uri(#"Images/hitcircle.png", UriKind.Relative));
Dot = new Ellipse() { Width = hitcircleSettingPath, Height = hitcircleSettingPath, Fill = DotImg, };
//Activates placeDot() method to give the Dot a random location
placeDot();
//Add Dot to the game area
spawnArea.Children.Add(Dot);
}
XAML This is the complete XAML i think there might be something wrong with the XAML but below is a shorter snippet where it does work how i want it but without the other stuff.
<Window x:Name="Grid" x:Class="ReactieSnelheid_Game.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ReactieSnelheid_Game"
mc:Ignorable="d"
Title="Dot Program" Height="768" Width="1366" WindowStartupLocation="CenterScreen" Cursor="Pen" Icon="Images/icon.ico" ResizeMode="NoResize" WindowState="Maximized" WindowStyle="None" Background="Black" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="{x:Null}" KeyDown="Grid_KeyDown">
<Grid x:Name="backgroundImageGrid" Margin="0,0,0,0" MouseDown="LayoutRoot_MouseDown">
<Grid.Background>
<ImageBrush x:Name="backgroundBrush" ImageSource="Images/background.png" Opacity="0.25"/>
</Grid.Background>
<Grid x:Name="gameWrapper">
<Grid.Background>
<SolidColorBrush Color="Black" Opacity="0.25"/>
</Grid.Background>
<Label x:Name="reactionTime" Content="1000ms" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontSize="40" FontFamily="PMingLiU-ExtB" Foreground="White"/>
<Label x:Name="circleSize" Content="150x150" HorizontalAlignment="Left" Margin="10,73,0,0" VerticalAlignment="Top" FontSize="26.667" FontFamily="PMingLiU-ExtB" Foreground="White" Background="{x:Null}"/>
<Label x:Name="dotCount" Content="000000" HorizontalAlignment="Left" Margin="28,0,0,-1" Foreground="White" FontSize="80" RenderTransformOrigin="0.5,0.5" FontFamily="PMingLiU-ExtB" VerticalAlignment="Bottom" Background="{x:Null}">
<Label.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleX="-11.056"/>
<RotateTransform/>
<TranslateTransform X="-8.305"/>
</TransformGroup>
</Label.RenderTransform>
</Label>
<Label x:Name="scoreCount" Content="9999999999999999" Margin="0,0,10,10" Foreground="White" FontSize="80" FontFamily="PMingLiU-ExtB" HorizontalAlignment="Right" Height="106" VerticalAlignment="Bottom"/>
<Rectangle x:Name="startTimerBtn" RenderTransformOrigin="0.39,-0.75" Margin="144,10,0,0" Width="128" Height="64" HorizontalAlignment="Left" VerticalAlignment="Top" MouseDown="startTimerBtn_MouseDown">
<Rectangle.Fill>
<ImageBrush ImageSource="Images/starttimer.png"/>
</Rectangle.Fill>
</Rectangle>
<Grid x:Name="spawnArea" Margin="0,115,0,121" Background="Red"/>
</Grid>
<Grid x:Name="pauseScreen">
<Grid.Background>
<SolidColorBrush Color="#FF81D650" Opacity="0.25"/>
</Grid.Background>
<Grid x:Name="pauseScreenBtns" Margin="389,153,389,152" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle x:Name="Startbtn" Height="332" Width="332" MouseDown="Startbtn_MouseDown" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="129,0,127,76">
<Rectangle.Fill>
<ImageBrush ImageSource="Images/Start.png"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="settingsBtn" MouseDown="settingsBtn_MouseDown" Margin="-5,189,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="129" Height="63">
<Rectangle.Fill>
<ImageBrush ImageSource="Images/settings.png"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="resetBtn" MouseDown="resetBtn_MouseDown" Margin="0,190,-6,0" Width="128" Height="62" HorizontalAlignment="Right" VerticalAlignment="Top">
<Rectangle.Fill>
<ImageBrush ImageSource="Images/reset.png"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="quitBtn" RenderTransformOrigin="0.39,-0.75" Margin="232,399,0,0" Width="129" Height="64" HorizontalAlignment="Left" VerticalAlignment="Top" MouseDown="quitBtn_MouseDown">
<Rectangle.Fill>
<ImageBrush ImageSource="Images/quit.png"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Grid>
</Grid>
</Window>
Shorter XAML
<Window x:Class="TESTPROJECTEN.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TESTPROJECTEN"
mc:Ignorable="d"
Title="MainWindow" Height="768" Width="1366">
<Grid x:Name="gameWrapper">
<Grid x:Name="spawnArea"/>
</Grid>
</Window>
Ellipse has Alignment = Stretch by default. But you set a fixed Width and Height so it looks like ellipse is centered. Try set location relative to top left corner
Dot = new Ellipse() { Width = hitcircleSettingPath, Height = hitcircleSettingPath, Fill = DotImg, };
Dot.HorizontalAlignment = HorizontalAlignment.Left;
Dot.VerticalAlignment = VerticalAlignment.Top;
it will have a very small offset to the right but not even close to
200 pixels.
First of all, as far as I remember, WPF doesn't use those numbers as pixels, but as some internal values. I can't remember the name of those units, but bottom line, they are not pixels.
Now for your actual problem:
Wpf grid places all his child controls by default in the center, so when you are putting the ellipse inside the grid and set a margin 200, it sets the margin from the center.
With that in mind, you can change your random algorithm to make the bubbles appear all over the grid:
//Gives Dot a position
public void placeDot()
{
//Give Dot random position
double halfSide = (spawnArea.ActualWidth - Dot.Width) / 2;
// The farthest left the dot can be
double minLeft = -(halfSide - (Dot.ActualWidth / 2));
// The farthest right the dot can be without it going off the screen
double maxLeft = halfSide - (Dot.ActualWidth / 2);
// The farthest up the dot can be
double minTop = -(halfSide - (Dot.ActualHeight / 2));
// The farthest down the dot can be without it going off the screen
double maxTop = halfSide - (Dot.ActualHeight / 2);
double left = RandomBetween(minLeft, maxLeft);
double top = RandomBetween(minTop, maxTop);
Dot.Margin = new Thickness(left, top, 0, 0);
}
I hope I didn't miss anything. I didn't run this code, so it might need adjustments, but this is the main line to follow.
Happy Coding! :)

Fill Ellipse based on percentage

I have created Ellipse in XAML.
Here is the code :
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Ellipse Width="400" Stroke="DodgerBlue" Height="400" StrokeThickness="75" Fill="Transparent">
</Ellipse>
</Grid>
Say the Ellipse is 100% if its 20% the blue color should fill only till that and also display the percentage text in the center (empty area) of ellipse.
EDIT
I have add text to display in center.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Ellipse Width="400" Stroke="DodgerBlue" Height="400" StrokeThickness="75" Fill="Transparent">
</Ellipse>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="20"
FontSize="125"/>
</Grid>
EDIT 2
Here is what how it looks like i am trying to acheive:
here the orange color with the 20% fill.
You can use an arc control preset in the assembly Microsoft.Expression.Drawing
It has properties like StartAngle and EndAngle which could be well manipulated accordingly.
<es:Arc x:Name="arc" ArcThickness="3" ArcThicknessUnit="Pixel" EndAngle="360" Fill="Black" Height="270" Canvas.Left="101.94" Stroke="Black" StartAngle="0" UseLayoutRounding="False" Width="269.941" Canvas.Top="12" />
Now what you could do using this control is : Just take two similar arcs One superimposing the other,
color the below one(1st arc) with Blue and give start and end angle properties to the red color arc(2nd arc) which would make your layout look like the way it is mentioned in design two.
Raw Usage:
<Canvas x:Name="canvas1" Margin="0,10,0,0" Height="300" Width="480" HorizontalAlignment="Center">
<es:Arc x:Name="arc" ArcThickness="3" ArcThicknessUnit="Pixel" Fill="Black" Height="100" Canvas.Left="0" Stroke="Blue" UseLayoutRounding="False" Width="100" Canvas.Top="0"/>
</Canvas>
<es:Arc x:Name="arc" ArcThickness="3" EndAngle="120" StartAngle="0" ArcThicknessUnit="Pixel" Fill="Blue" Height="100" Canvas.Left="0" Stroke="Blue" UseLayoutRounding="False" Width="100" Canvas.Top="0"/>
</Canvas>
Check this link as well
User control version would consist of two parts: XAML + code-behind.
XAML part:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Project.CustomControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d"
d:DesignHeight="40"
d:DesignWidth="40">
<Grid Width="40" Height="40">
<Ellipse StrokeThickness="3" Stroke="#FF89CE25"/>
<Path Stroke="Red" StrokeThickness="2" x:Name="arcPath">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="20,1">
<ArcSegment x:Name="myArc" SweepDirection="Clockwise" IsLargeArc="True" Point="20,1"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
Code-behind file (short version, no fluff):
public sealed partial class MyCustomControl : UserControl
{
public double ProgressValue
{
get { return (double)GetValue(ProgressValueProperty); }
set { SetValue(ProgressValueProperty, value); }
}
// Using a DependencyProperty as the backing store for ProgressValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ProgressValueProperty =
DependencyProperty.Register("ProgressValue", typeof(double), typeof(MyCustomControl), new PropertyMetadata(0.0, OnProgressValueChanged));
private static void OnProgressValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//throw new NotImplementedException();
MyCustomControl circularProgressBar = d as MyCustomControl;
if (circularProgressBar != null)
{
double r = 19;
double x0 = 20;
double y0 = 20;
circularProgressBar.myArc.Size = new Size(19, 19);
double angle = 90 - (double)e.NewValue / 100 * 360;
double radAngle = angle * (PI / 180);
double x = x0 + r * Cos(radAngle);
double y = y0 - r * Sin(radAngle);
if (circularProgressBar.myArc != null)
{
circularProgressBar.myArc.IsLargeArc = ((double)e.NewValue >= 50);
circularProgressBar.myArc.Point = new Point(x, y);
}
}
}
public MyCustomControl()
{
this.InitializeComponent();
}
}
Now, you can throw your CustomControl into any place in your XAML and bind the ProgressValue property to the respective data source. The arc would redraw itself and will fill the Ellipse shape proportionally to the value (a value from 0-100).

Categories