Silverlight 2.0 - Vytváranie inštancií ovládacích prvkov v kóde

Publikoval Michal Kočí dňa 10.5.2008 o 01:52 v kategórii Silverlight

V tomto príspevku si naprogramujeme analógové hodiny a na tomto príklade si okrem vytvárania inštancií ovládacích prvkov v kóde aj použitie časovača či niektoré možnosti práce s textom v Silverlight aplikáciach.

Analógové hodiny

Vytvoríme si analógové hodiny - kruh s čiarkami namiesto čísel a s textom namiesto čiar ručičiek. Hodiny budú vyzerať nasledovne:

XAML kód

Aby sme vytvorili takéto analógové hodiny, budeme potrebovať vykresliť:

  • Obrys ciferníka
  • 12 čiarok, každá bude signalizovať práve jednu hodinu
  • Hodinovú ručičku
  • Minutovú ručičku
  • Sekundovú ručičku

Obrys ciferníka vykreslíme jednoducho, ako elipsu - inštanciu prvku Ellipse. Jednotlivé čiarky, teda zárezy pre každú z dvanástich hodín budeme vykreslovať v kóde. Ručičky, keďže sú len tri a každá je iná, budeme vykreslovať ako text - inštancie triedy TextBlock. Ručičky budeme animovať v kóde, ukážeme si tak použitie časovača.

Ako prvé vykresléme obrys, teda elipsu. Tým, že nastavíme rovnakú šírku ako výšku bude elipsa v tvare kruhu:

<Ellipse Width="296" Height="296" Stroke="Black" Fill="Azure"
         Canvas.Left="2" Canvas.Top="2" />

Ďalej vykreslíme jednotlivé ručičky. Ich kód je veľmi podobný. Ručička je vlastne obyčajný TextBlock, s aplikovanými transformáciami - ScaleTransform ktorá ručičku natiahne, aby bola široká a siahala až skoro k obrysu ciferníka a RotateTransform ktorá bude ručiku animovať. RotateTransform si pomenujeme, aby sme s ňou mohli pracovať v kóde:

<TextBlock x:Name="HandHour" Text="HOUR" Foreground="CornflowerBlue"
           Canvas.Left="150" Canvas.Top="150" Canvas.ZIndex="1"
           FontFamily="Trebuchet MS" FontSize="16" >
    <TextBlock.RenderTransform>
        <TransformGroup>
            <ScaleTransform ScaleX="3" ScaleY=".5"/>
            <RotateTransform x:Name="HandHourRotate" Angle="0"/>
        </TransformGroup>
    </TextBlock.RenderTransform>
</TextBlock>

Celý XAML kód potom vyzerá nasledovne:

<UserControl x:Class="ClockClient.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="300" Height="300">
    <Canvas x:Name="LayoutRoot" Background="White">
        <Ellipse Width="296" Height="296" Stroke="Black" Fill="Azure"
                 Canvas.Left="2" Canvas.Top="2" />
        <TextBlock x:Name="HandHour" Text="HOUR" Foreground="CornflowerBlue"
                   Canvas.Left="150" Canvas.Top="150" Canvas.ZIndex="1"
                   FontFamily="Trebuchet MS" FontSize="16" >
            <TextBlock.RenderTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="3" ScaleY=".5"/>
                    <RotateTransform x:Name="HandHourRotate" Angle="0"/>
                </TransformGroup>
            </TextBlock.RenderTransform>
        </TextBlock>
        <TextBlock x:Name="HandMinute" Text="MINUTE" Foreground="LightSteelBlue"
                   Canvas.Left="150" Canvas.Top="150" Canvas.ZIndex="2"
                   FontFamily="Trebuchet MS" FontSize="16" >
            <TextBlock.RenderTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="2.5" ScaleY=".5"/>
                    <RotateTransform x:Name="HandMinuteRotate" Angle="30"/>
                </TransformGroup>
            </TextBlock.RenderTransform>
        </TextBlock>
        <TextBlock x:Name="HandSecond" Text="SECOND" Foreground="SkyBlue"
                   Canvas.Left="150" Canvas.Top="150" Canvas.ZIndex="3"
                   FontFamily="Trebuchet MS" FontSize="16" >
            <TextBlock.RenderTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="2.5" ScaleY=".5"/>
                    <RotateTransform x:Name="HandSecondRotate" Angle="60"/>
                </TransformGroup>
            </TextBlock.RenderTransform>
        </TextBlock>
    </Canvas>
</UserControl>

Programový kód

Aby hodiny stále zobrazovali aktuálny čas, je potrebné minimálne každú sekundu aktualizaovať ich výzor, teda treba zabezpečiť, aby sa v pravidelných intervaloch ručičky nastavili do takej polohy, aby zobrazovali presný čas. Na toto použijeme časovač - objekt typu DispatcherTimer. Pripravíme si metódu, ktorá zinicializuje tento časovač, teda nastaví interval a metódu, ktorá bude v danom intervale volávaná:

private void InitializeTimer()
{
    timer = new DispatcherTimer();
    timer.Interval = new TimeSpan(0, 0, 0, 0, 35);
    timer.Tick += new EventHandler(Timer_Tick);
    timer.Start();
}

V metóde Timer_Tick, ktorá bude volaná každých 35 milisekúnd potom vypočítame správny uhol, pod ktorým má byť ručička zobrazená a tento nastavý príslušným ručičkám, respektíve ich otočným transformáciám. Algoritmus je jednoduchý a to rozdeliť si ciferník, ktorý vlastne predstavuje 360° na rovnako veľké úseky predstavujúce hodiny alebo minúty, alebo sekundy (v závislosti od typu ručičky) a potom veľkosť tohto úseku prenásobiť aktuálnou hodnotou hodín alebo minút, alebo sekúnd:

void Timer_Tick(object sender, EventArgs e)
{
    DateTime actualTime = DateTime.Now;
 
    int hour = actualTime.Hour;
    int minute = actualTime.Minute;
    int second = actualTime.Second;
    int millisecond = actualTime.Millisecond;
 
    int totalMinutes = ((hour > 11 ? hour - 12 : hour) * 60) + minute;
    int totalSecond = (minute * 60) + second;
    int totalMillisecond = (second * 1000) + millisecond;
 
    double hourAngle = (360 / 720.0) * totalMinutes;
    hourAngle -= 90;
    HandHourRotate.Angle = hourAngle;
 
    double minuteAngle = (360 / 3600.0) * totalSecond;
    minuteAngle -= 90;
    HandMinuteRotate.Angle = minuteAngle;
 
    double secondAngle = (360 / 60000.0) * totalMillisecond;
    secondAngle -= 90;
    HandSecondRotate.Angle = secondAngle;
}

No a ešte nám chýbajú značky pre jednotlivé hodiny. Tie budeme vykreslovať ako čiary (objekty typu Line) a budeme ich potrebovať 12 a mali by byť natočené vždy v násobkoch 30°. V cykle potom týchto čiar vytvoríme 12. Aby boli zobrazené, musíme ich pridať na Canvas a to metódou Add jeho vlastnosti Children. Ako však nastaviť pozíciu, teda dependency property Canvas.Left a Canvas.Top? Tieto sa nastavujú metódou SetValue ktorá ako parametre prijíma danú dependency property a hodnotu, ktorá sa má tejto vlastnosti nastaviť. Celý kód inicializačnej metódy potom vyzerá nasledovne:

private void CreateHourLines()
{
    for (int i = 0; i < 12; i++)
    {
        Line line = new Line()
        {
            X1 = 0,
            X2 = 0,
            Y1 = 140,
            Y2 = 147,
            Stroke = new SolidColorBrush(Colors.Black),
            StrokeThickness = 2,
            RenderTransform = new RotateTransform() { Angle = i * 30 }
        };
        line.SetValue(Canvas.LeftProperty, 150);
        line.SetValue(Canvas.TopProperty, 150);
 
        LayoutRoot.Children.Add(line);
    }
}

Celý kód potom vyzerá nasledovne:

using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
 
namespace ClockClient
{
    public partial class Page : UserControl
    {
        DispatcherTimer timer;
 
        public Page()
        {
            InitializeComponent();
            InitializeTimer();
            CreateHourLines();
        }
 
        private void CreateHourLines()
        {
            for (int i = 0; i < 12; i++)
            {
                Line line = new Line()
                {
                    X1 = 0,
                    X2 = 0,
                    Y1 = 140,
                    Y2 = 147,
                    Stroke = new SolidColorBrush(Colors.Black),
                    StrokeThickness = 2,
                    RenderTransform = new RotateTransform() { Angle = i * 30 }
                };
                line.SetValue(Canvas.LeftProperty, 150);
                line.SetValue(Canvas.TopProperty, 150);
 
                LayoutRoot.Children.Add(line);
            }
        }
 
        private void InitializeTimer()
        {
            timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 0, 0, 35);
            timer.Tick += new EventHandler(Timer_Tick);
            timer.Start();
        }
 
        void Timer_Tick(object sender, EventArgs e)
        {
            DateTime actualTime = DateTime.Now;
 
            int hour = actualTime.Hour;
            int minute = actualTime.Minute;
            int second = actualTime.Second;
            int millisecond = actualTime.Millisecond;
 
            int totalMinutes = ((hour > 11 ? hour - 12 : hour) * 60) + minute;
            int totalSecond = (minute * 60) + second;
            int totalMillisecond = (second * 1000) + millisecond;
 
            double hourAngle = (360 / 720.0) * totalMinutes;
            hourAngle -= 90;
            HandHourRotate.Angle = hourAngle;
 
            double minuteAngle = (360 / 3600.0) * totalSecond;
            minuteAngle -= 90;
            HandMinuteRotate.Angle = minuteAngle;
 
            double secondAngle = (360 / 60000.0) * totalMillisecond;
            secondAngle -= 90;
            HandSecondRotate.Angle = secondAngle;
        }
    }
}

Záver

Veľmi jednoduchá aplikácia Vám teda poodhalila, ako používať časovač či ako dynamicky vytvárať inštancie prvkov. Miminum kódu a výsledok môže byť prívetivý. Ešte na aplikáciu poštvať dizajnéra a výsledok by bol ozaj pôsobivý...

Mohlo by ťa tiež zaujímať

Páčil sa ti príspevok?

Zdieľaj príspevok alebo si ho odlož na neskôr

Sleduj ma

Ak nechceš premeškať príspevky ako je tento, sleduj ma na Twitteri, alebo ak máš RSS čítačku, môžeš sledovať môj RSS kanál.