C# :: Aufgabe #233

1 Lösung Lösung öffentlich

Erstellen eines WPF-Benutzersteuerelements (Analoguhr)

Anfänger - C# von RevTreb - 30.11.2018 um 15:32 Uhr
Es soll in WPF ein Benutzersteuerelement (UserControl) namens AnalogWatch erstellt werden.

Das Zifferblatt soll für jede Minute einen Strich/Linie enthalten.
Die Striche bei 5 bzw. 15 Minuten sollen dabei etwas länger und dicker sein, als der Rest.

Die 3 Zeiger werden ebenfalls nur als Gerade Linien dargestellt.
Sekundenzeiger lang und dünn.
Minutenzeiger etwas kürzer und etwas dicker.
Stundenzeiger kurz und dick.

Es soll zu jedem Zeitpunkt die aktuelle Systemzeit auf der Uhr abzulesen sein (bei Visual Studio
auch im Designermodus, wenn das Programm nicht ausgeführt wird)

Der Aufruf des Benutzerelements aus dem XAML-Code soll möglichst einfach gehalten werden.
Ideal wäre z.B.:
Quellcode ausblenden XML-Code
<Grid>
        <local:AnalogWatch/>
</Grid>


Das Format/Größe der Zeiger und des Ziffernblatts sollen sich automatisch an die Größe des Fensters anpassen.

Optional: Ein per Eigenschaft zu übergebendes Hintergrundbild einfügen

Viel Spaß/Erfolg :-)

Lösungen:

vote_ok
von RevTreb (860 Punkte) - 23.08.2019 um 11:22 Uhr
Hier mein Lösungsvorschlag dazu...

Anmerkung zu meinen eingereichten Aufgaben 233 und 237:
Vielleicht wäre es ja sinnvoll meine Lösungen als Muster zu veröffentlichen?
Die Aufgaben sind ja jetzt schon fast ein Jahr online... also entweder sie sind zu schwer,
oder sie interessieren keinen^^

Quellcode ausblenden XML-Code
<Window x:Class="Aufgabe233.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:Aufgabe233"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:AnalogWatch/>
    </Grid>
</Window>


Quellcode ausblenden XML-Code
<UserControl x:Class="Aufgabe233.AnalogWatch"
             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" 
             xmlns:local="clr-namespace:Aufgabe233"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid x:Name="myGrid">
        <local:CartesianCanvas x:Name="myCanvas">
           
        </local:CartesianCanvas>    
    </Grid>
</UserControl>


Quellcode ausblenden C#-Code
using System.Collections.Generic;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Aufgabe233
{
    /// <summary>
    /// Interaktionslogik für AnalogWatch.xaml
    /// </summary>
    public partial class AnalogWatch : UserControl
    {
        private const double ScaleFactor = 0.9;
        private enum StrichArt { m15, m5, m1 }
        private Dictionary<StrichArt, double> StrichLayout = 
            new Dictionary<StrichArt, double> {
                { StrichArt.m1, 1 }, { StrichArt.m5, 2 }, { StrichArt.m15, 3 }
            };

        private Timer myTimer;
        private double maxLength;
        private FrameworkElement myParent;
        private Zeiger hour, minute, second;

        public AnalogWatch()
        {        
            InitializeComponent();
            Loaded += AnalogWatch_Loaded;
            myTimer = new Timer(100);
            myTimer.Elapsed += MyTimer_Elapsed;
            myTimer.Start();
        }

        private void MyTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            setclock();
        }

        private void AnalogWatch_Loaded(object sender, RoutedEventArgs e)
        {
            myParent = Parent as FrameworkElement;
            SetSize(myParent.RenderSize);
            myParent.SizeChanged += MyParent_SizeChanged;
            buildWatchGeometry();
            buildZeiger();
            setclock();
        }

        private void buildZeiger()
        {
            second = new Zeiger(new Point(0, 0), new Vector(0, maxLength * ScaleFactor * 0.45),Zeiger.Art.second);
            second.Stroke = new SolidColorBrush(Colors.Black);
            second.StrokeThickness = 2;
            myCanvas.Children.Add(second);
            minute = new Zeiger(new Point(0, 0), new Vector(0, maxLength * ScaleFactor * 0.35), Zeiger.Art.minute);
            minute.Stroke = new SolidColorBrush(Colors.Black);
            minute.StrokeThickness = 5;
            myCanvas.Children.Add(minute);
            hour = new Zeiger(new Point(0, 0), new Vector(0, maxLength * ScaleFactor * 0.3),Zeiger.Art.hour);
            hour.Stroke = new SolidColorBrush(Colors.Black);
            hour.StrokeThickness = 8;
            myCanvas.Children.Add(hour);
        }

        private void MyParent_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            SetSize(e.NewSize);
            setclock();
        }

        private void buildWatchGeometry()
        {
            buildCircle();
            buildLines();
        }

        private void buildLines()
        {
            for (int angle = 0; angle < 360; angle=angle+6)
            {
                StrichArt LineArt = StrichArt.m1;
                if (angle % 30 == 0)
                    LineArt = StrichArt.m5;
                if (angle % 90 == 0)
                    LineArt = StrichArt.m15;
                double artFactor = StrichLayout[LineArt];
                Line line = new Line(new Point(0, 0), new Vector(0, maxLength * ScaleFactor * 0.5 * 0.1*(artFactor/2)));
                line.Stroke = new SolidColorBrush(Colors.Black);
                line.StrokeThickness = artFactor * 1.2;
                double lineLength = line.EndPoint.Y;
                line.AddTransForm(new TranslateTransform(0, maxLength * ScaleFactor * 0.48 - lineLength));
                line.AddTransForm(new RotateTransform(angle));
                myCanvas.Children.Add(line);
            }        
        }

        private void buildCircle()
        {
            myCanvas.Children.Clear();
            Circle c = new Circle();
            c.Stroke = new SolidColorBrush(Colors.Black);
            c.StrokeThickness = 5;
            c.Diameter = Width;
            c.ScaleFactor = ScaleFactor;
            myCanvas.Children.Add(c);
        }

        private void SetSize(Size size)
        {
            double height = size.Height;
            double width = size.Width;
            if (height >= width)
                maxLength = width;
            else
                maxLength = height;
            Width = maxLength;
            Height = maxLength;
        }

        public void setclock()
        {
            Dispatcher.Invoke(() =>
            {
                buildWatchGeometry();
                buildZeiger();
            });
            
        }       
    }
}


Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Aufgabe233
{
    public class CartesianCanvas : Panel
    {
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            Point middle = new Point(arrangeSize.Width / 2, arrangeSize.Height / 2);

            double x = 0.0, y = 0.0;
            foreach (UIElement element in base.InternalChildren)
            {
                if (element == null)
                    continue;

                element.RenderTransform = new MatrixTransform(1, 0, 0, -1, 0, 0);
                element.Arrange(new Rect(new Point(middle.X + x, middle.Y + y), element.DesiredSize));
            }
            return arrangeSize;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
            foreach (UIElement element in base.InternalChildren)
            {
                if (element != null)
                {
                    element.Measure(availableSize);
                }
            }
            return new Size();
        }
    }
}


Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Aufgabe233
{
    public class Line : Shape
    {
        public TransformGroup MyTransform
        {
            get { return (TransformGroup)GetValue(MyTransformProperty); }
            set { SetValue(MyTransformProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyTransformProperty =
            DependencyProperty.Register(
                "MyTransform",
                typeof(TransformGroup),
                typeof(Line),
                new FrameworkPropertyMetadata(
                    new TransformGroup(),
                    FrameworkPropertyMetadataOptions.AffectsRender
                    )
                );

        public Point StartPoint
        {
            get { return (Point)GetValue(StartPointProperty); }
            set { SetValue(StartPointProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartPointProperty =
            DependencyProperty.Register(
                "StartPoint",
                typeof(Point),
                typeof(Line),
                new FrameworkPropertyMetadata(
                    new Point(0,0),
                    FrameworkPropertyMetadataOptions.AffectsRender
                    )
                );

        public Point EndPoint
        {
            get { return (Point)GetValue(EndPointProperty); }
            set { SetValue(EndPointProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndPointProperty =
            DependencyProperty.Register(
                "EndPoint",
                typeof(Point),
                typeof(Line),
                new FrameworkPropertyMetadata(
                    new Point(0, 0),
                    FrameworkPropertyMetadataOptions.AffectsRender
                    )
                );



        protected override Geometry DefiningGeometry
        {
            get
            {
                return new LineGeometry(StartPoint,EndPoint,MyTransform);
            }
        }

        public Line()
        {
            MyTransform = new TransformGroup();
        }

        public Line(Point StartPoint, Vector Vec)
        {
            this.StartPoint = StartPoint;
            this.EndPoint = StartPoint + Vec;
            MyTransform = new TransformGroup();
            
        }

        public void AddTransForm(Transform transform)
        {
            MyTransform.Children.Add(transform);
        }
    }
}


Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Aufgabe233
{
    class Circle:Shape
    {
        protected override Geometry DefiningGeometry
        {
            get
            {
                double radius = Diameter/2 * ScaleFactor;                
                return new EllipseGeometry(new Point(0, 0), radius, radius);
            }
        }

        public double Diameter
        {
            get { return (double)GetValue(DiameterProperty); }
            set { SetValue(DiameterProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Radius.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DiameterProperty =
            DependencyProperty.Register("Diameter", typeof(double), typeof(Circle), 
                new FrameworkPropertyMetadata(0.0,FrameworkPropertyMetadataOptions.AffectsRender));

        public double ScaleFactor
        {
            get { return (double)GetValue(ScaleFactorProperty); }
            set { SetValue(ScaleFactorProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Radius.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScaleFactorProperty =
            DependencyProperty.Register("ScaleFactor", typeof(double), typeof(Circle),
                new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));

    }
}


Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace Aufgabe233
{
    public class Zeiger : Line
    { 
        public enum Art { second, minute, hour };

        public RotateTransform MyRotateTransform
        {
            get { return (RotateTransform)GetValue(MyRotateTransformProperty); }
            set { SetValue(MyRotateTransformProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyRotateTransform.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyRotateTransformProperty =
            DependencyProperty.Register("MyRotateTransform", typeof(RotateTransform), typeof(Zeiger),
                new FrameworkPropertyMetadata(
                        new RotateTransform(0,0,0),
                        FrameworkPropertyMetadataOptions.AffectsRender
                        ));



        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set
            {
                SetValue(AngleProperty, value);
                MyRotateTransform = new RotateTransform(Angle,0,0);
            }
        }

        // Using a DependencyProperty as the backing store for Rotation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register(
                "Angle",
                typeof(double),
                typeof(Zeiger),
                    new FrameworkPropertyMetadata(
                        0.0,
                        FrameworkPropertyMetadataOptions.AffectsRender
                        )
                );

        public Zeiger():base()
        {
            Angle = DateTime.Now.Second;
            MyRotateTransform = new RotateTransform(Angle, 0, 0);
            base.MyTransform.Children.Add(MyRotateTransform);
        }
        public Zeiger(Point StartPoint, Vector Vec, Art art) :base(StartPoint,Vec)
        {
            switch (art)
            {
                case Art.second:
                    Angle = - DateTime.Now.Second * 6;
                    break;
                case Art.minute:
                    Angle = - DateTime.Now.Minute * 6 
                            - DateTime.Now.Second * 6 / 60;
                    break;
                case Art.hour:
                    Angle = - DateTime.Now.Hour * 30
                            - DateTime.Now.Minute * 30 / 60
                            - DateTime.Now.Second * 30/ 60 / 60;
                    break;
                default:
                    break;
            }
            
            MyRotateTransform = new RotateTransform(Angle, 0, 0);
            base.MyTransform.Children.Add(MyRotateTransform);
        }
       
    }
}
1810189

Du scheinst einen AdBlocker zu nutzen. Ich würde mich freuen, wenn du ihn auf dieser Seite deaktivierst und dich davon überzeugst, dass die Werbung hier nicht störend ist.