Creating a compass

8 posts, 1 answers
  1. Eric
    Eric avatar
    2 posts
    Member since:
    Mar 2009

    Posted 24 Mar 2009 Link to this post

    I'd like to create a 360-degree gauge representing a compass, and I'd like the needle to be animated. When the needle goes from, say, North-West (315) to North-East (45), I'd like it to go the shortest way, through North (0). Similarly, when it goes from SW to SE, it should go through South (180).

    Can I achieve this with your gauge?

    Eric.
  2. Answer
    Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 25 Mar 2009 Link to this post

    Hi Eric,

    The compass-like radial gauge can be created using following XAML:
    <UserControl x:Class="TestGaugeApplication.Page" 
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
                 xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" 
                 xmlns:control="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Gauge"   
                 xmlns:gauge="clr-namespace:Telerik.Windows.Controls.Gauges;assembly=Telerik.Windows.Controls.Gauge"   
                 Width="500" Height="400">  
        <UserControl.Resources> 
            <ResourceDictionary> 
                <DataTemplate x:Key="TickLabelEmpty">  
                    <Grid /> 
                </DataTemplate> 
     
                <Style x:Key="MajorCustomTicks" TargetType="TextBlock">  
                    <Setter Property="FontFamily" Value="Arial" /> 
                    <Setter Property="FontSize" Value="14" /> 
                    <Setter Property="FontWeight" Value="Bold" /> 
                </Style> 
     
                <ControlTemplate x:Key="CompasNeedleTemplate" TargetType="gauge:Needle">  
                    <Grid x:Name="PART_Grid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">  
                        <Grid.ColumnDefinitions> 
                            <ColumnDefinition Width="*" /> 
                            <!-- Column 0 - tail --> 
                            <ColumnDefinition Width="*" /> 
                            <!-- Column 1 - pin point --> 
                            <ColumnDefinition Width="*" /> 
                            <!-- Column 2 - arrow --> 
                        </Grid.ColumnDefinitions> 
                        <Grid.RowDefinitions> 
                            <RowDefinition Height="*" /> 
                        </Grid.RowDefinitions> 
     
                        <Grid x:Name="PART_Shape" Grid.ColumnSpan="3" Margin="0">  
                            <Grid.ColumnDefinitions> 
                                <ColumnDefinition Width="50*" /> 
                                <ColumnDefinition Width="50*" /> 
                            </Grid.ColumnDefinitions> 
                            <Grid.RowDefinitions> 
                                <RowDefinition Height="0.3*" /> 
                                <RowDefinition Height="0.4*" /> 
                                <RowDefinition Height="0.3*" /> 
                            </Grid.RowDefinitions> 
     
                            <Path Grid.Row="1"   
                                  Grid.Column="1"   
                                  Stretch="Fill" 
                                  Fill="Blue"   
                                  StrokeThickness="1">  
                                <Path.Data> 
                                    <PathGeometry> 
                                        <PathFigure StartPoint="1,0.5">  
                                            <LineSegment Point="0,1" /> 
                                            <LineSegment Point="0,0" /> 
                                        </PathFigure> 
                                    </PathGeometry> 
                                </Path.Data> 
                            </Path> 
     
                            <Path Grid.Row="1"   
                                  Grid.Column="0"   
                                  Stretch="Fill" 
                                  Fill="Red"   
                                  StrokeThickness="1">  
                                <Path.Data> 
                                    <PathGeometry> 
                                        <PathFigure StartPoint="1,0">  
                                            <LineSegment Point="1,1" /> 
                                            <LineSegment Point="0,0.5" /> 
                                        </PathFigure> 
                                    </PathGeometry> 
                                </Path.Data> 
                            </Path> 
                        </Grid> 
     
                        <Grid Grid.Column="1">  
                            <Grid.RowDefinitions> 
                                <RowDefinition Height="0.125*" /> 
                                <RowDefinition Height="0.75*" /> 
                                <RowDefinition Height="0.125*" /> 
                            </Grid.RowDefinitions> 
                            <Grid.ColumnDefinitions> 
                                <ColumnDefinition Width="0.125*" /> 
                                <ColumnDefinition Width="0.75*" /> 
                                <ColumnDefinition Width="0.125*" /> 
                            </Grid.ColumnDefinitions> 
                            <Ellipse Grid.Column="1" Grid.Row="1" Stretch="Uniform" 
                                     Fill="White">  
                            </Ellipse> 
                        </Grid> 
     
                        <Grid Grid.Column="1">  
                            <Grid.RowDefinitions> 
                                <RowDefinition Height="0.25*" /> 
                                <RowDefinition Height="0.5*" /> 
                                <RowDefinition Height="0.25*" /> 
                            </Grid.RowDefinitions> 
                            <Grid.ColumnDefinitions> 
                                <ColumnDefinition Width="0.25*" /> 
                                <ColumnDefinition Width="0.5*" /> 
                                <ColumnDefinition Width="0.25*" /> 
                            </Grid.ColumnDefinitions> 
                            <Ellipse Grid.Column="1" Grid.Row="1" Stretch="Uniform" 
                                     Fill="White">  
                            </Ellipse> 
                        </Grid> 
                    </Grid> 
                </ControlTemplate> 
                  
                <Style x:Key="CompasNeedleStyle" TargetType="gauge:Needle">  
                    <Setter Property="Location" Value="OverCenter" /> 
                    <Setter Property="RelativeHeight" Value="0.4" /> 
                    <Setter Property="RelativeShift" Value="1.4" /> 
                    <Setter Property="Template" Value="{StaticResource CompasNeedleTemplate}" /> 
                </Style> 
            </ResourceDictionary> 
        </UserControl.Resources> 
        <Grid x:Name="LayoutRoot" Background="White">  
            <control:RadGauge x:Name="radGauge" Width="350" Height="350">  
                <gauge:RadialGauge> 
                    <gauge:RadialScale StartAngle="270" 
                                       SweepAngle="360" 
                                       Radius="0.83" 
                                       Min="0" 
                                       Max="360" 
                                       MajorTickStep="90" 
                                       ShowLastLabel="False" 
                                       LabelRotationMode="None">  
                        <gauge:RadialScale.Label> 
                            <gauge:LabelProperties ItemTemplate="{StaticResource TickLabelEmpty}">  
                            </gauge:LabelProperties> 
                        </gauge:RadialScale.Label> 
                        <gauge:TickList> 
                            <gauge:CustomTickMark Value="0">  
                                <gauge:CustomTickMark.ItemTemplate> 
                                    <DataTemplate> 
                                        <Grid> 
                                            <TextBlock Foreground="Blue" 
                                                       Style="{StaticResource MajorCustomTicks}" 
                                                       Text="N" /> 
                                        </Grid> 
                                    </DataTemplate> 
                                </gauge:CustomTickMark.ItemTemplate> 
                            </gauge:CustomTickMark> 
                            <gauge:CustomTickMark Value="90">  
                                <gauge:CustomTickMark.ItemTemplate> 
                                    <DataTemplate> 
                                        <Grid> 
                                            <TextBlock Foreground="Blue" 
                                                       Style="{StaticResource MajorCustomTicks}" 
                                                       Text="E" /> 
                                        </Grid> 
                                    </DataTemplate> 
                                </gauge:CustomTickMark.ItemTemplate> 
                            </gauge:CustomTickMark> 
                            <gauge:CustomTickMark Value="180">  
                                <gauge:CustomTickMark.ItemTemplate> 
                                    <DataTemplate> 
                                        <Grid> 
                                            <TextBlock Foreground="Blue" 
                                                       Style="{StaticResource MajorCustomTicks}" 
                                                       Text="S" /> 
                                        </Grid> 
                                    </DataTemplate> 
                                </gauge:CustomTickMark.ItemTemplate> 
                            </gauge:CustomTickMark> 
                            <gauge:CustomTickMark Value="270">  
                                <gauge:CustomTickMark.ItemTemplate> 
                                    <DataTemplate> 
                                        <Grid> 
                                            <TextBlock Foreground="Blue" 
                                                       Style="{StaticResource MajorCustomTicks}" 
                                                       Text="W" /> 
                                        </Grid> 
                                    </DataTemplate> 
                                </gauge:CustomTickMark.ItemTemplate> 
                            </gauge:CustomTickMark> 
                        </gauge:TickList> 
                        <gauge:IndicatorList> 
                            <gauge:Needle x:Name="needle" Style="{StaticResource CompasNeedleStyle}" /> 
                        </gauge:IndicatorList> 
                    </gauge:RadialScale> 
                </gauge:RadialGauge> 
            </control:RadGauge> 
        </Grid> 
    </UserControl> 

    Unfortunately you can’t switch build-in indicator’s animation to go the shortest way. It always walk from current to the next value through the values in the [current, next] interval. But you can create your own animation routines which will work as you’d like. You need check if the sweep angle between current and next value is more than 180 degree and then operate depends on the test result:

    1. If distance is less than 180 degree – create 1 double animation which will change value of the needle from current to next value.
    2. If distance is more than 180 degree – create 2 double animations. One from current value to 0 or 360 depends on direction of change. And second from 0 or 260 to the next value.

    Following code represents how it can be done:

    /// <summary>  
    /// Indicates whether the 2 animations should be used  
    /// </summary>  
    private bool doubleAnimation = false;  
     
    /// <summary>  
    /// From value for second animation  
    /// </summary>  
    private double secondFromValue;  
     
    /// <summary>  
    /// To value for second animation  
    /// </summary>  
    private double secondToValue;  
     
    private void SetNewValueToIndicator(double newValue)  
    {  
        CreateAndRunAnimation(needle.Value, newValue);  
    }  
     
    private Storyboard CreateAnimation(double fromValue, double toValue, Duration duration)  
    {  
        // Create DoubleAnimations and set their properties.  
        DoubleAnimation needleValueAnimation = new DoubleAnimation();  
     
        needleValueAnimation.Duration = duration;  
     
        Storyboard storyboard = new Storyboard();  
        storyboard.Children.Add(needleValueAnimation);  
        Storyboard.SetTarget(needleValueAnimation, needle);  
        Storyboard.SetTargetProperty(needleValueAnimation, new PropertyPath("(Needle.Value)"));  
     
        needleValueAnimation.From = (double.IsNaN(fromValue) ? 0 : fromValue);  
        needleValueAnimation.To = toValue;  
     
        return storyboard;  
    }  
     
    private void CreateAndRunAnimation(double fromValue, double toValue)  
    {  
        Duration duration;  
        double distance = Math.Abs(toValue - fromValue);  
        if (distance > 180)  
        {  
            doubleAnimation = true;  
            if (toValue > 180)  
            {  
                secondFromValue = 360;  
                secondToValue = toValue;  
                toValue = 0;  
            }  
            else if (fromValue > 180)  
            {  
                secondFromValue = 0;  
                secondToValue = toValue;  
                toValue = 360;  
            }  
            duration = new Duration(TimeSpan.FromSeconds(0.4));  
        }  
        else 
        {  
            doubleAnimation = false;  
            duration = new Duration(TimeSpan.FromSeconds(0.8));  
        }  
     
        Storyboard storyboard = CreateAnimation(fromValue, toValue, duration);  
        storyboard.Completed += new EventHandler(Storyboard_Completed);  
        storyboard.Begin();  
    }  
     
    private void Storyboard_Completed(object sender, EventArgs e)  
    {  
        if (doubleAnimation)  
        {  
            // Create a duration of 1 seconds.  
            Duration duration = new Duration(TimeSpan.FromSeconds(0.4));  
     
            Storyboard storyboard = CreateAnimation(secondFromValue, secondToValue, duration);  
            storyboard.Begin();  
        }  

    All the best,
    Andrey Murzov
    the Telerik team


    Check out Telerik Trainer , the state of the art learning tool for Telerik products.
  3. DevCraft banner
  4. Eric
    Eric avatar
    2 posts
    Member since:
    Mar 2009

    Posted 26 Mar 2009 Link to this post

    Many thanks.

    Eric
  5. Danette Cross
    Danette Cross avatar
    2 posts
    Member since:
    Nov 2009

    Posted 30 Jun 2010 Link to this post

    What references do we use for the storyboard and doubleanimation? I have using statements for Telerik.Windows.Controls and Telerik.Windows.Controls.Guages but seems I am missing an assembly reference.
  6. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 01 Jul 2010 Link to this post

    Hi Danette,

    The Storyboard and DoubleAnimation classes are contained in the System.Windows.Media.Animation namespace.

    Sincerely yours,
    Andrey Murzov
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  7. Chris
    Chris avatar
    2 posts
    Member since:
    Aug 2011

    Posted 01 May 2012 Link to this post

    Sorry to Hijack the thread but we have just purchased the RadControls and we have a need to create a compas that allows the user to move the needle and subsequently the guage value with the mouse, so they can in essence select a bearing by draging the needle, could anyone provide me with some guidance with the best way to achieve this ??


    Many Thanks in advance

  8. Andrey
    Admin
    Andrey avatar
    1681 posts

    Posted 03 May 2012 Link to this post

    Hello Chris,

    Simply set the RadialScale.IsInteractive property to true and you will be able to drag the needle and position it by clicking on the scale.

    Kind regards,
    Andrey Murzov
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  9. Chris
    Chris avatar
    2 posts
    Member since:
    Aug 2011

    Posted 04 May 2012 Link to this post

    Many Thanks, it worked liked a charm :-)
Back to Top
DevCraft banner