Benutzer mit den meisten Antworten
Die beste Möglichkeit eine Uhr mit Sekundenanzeige darzustellen

Frage
-
Hallo Forum,
möglicherweise haben sich diese Frage schon ein paar Leute gestellt:
Ich möchte eine Uhr (sekundengenau) auf der UI (User Interface) anzeigen - wie mache ich das am besten?
Ich bin dabei meine alten, aber funktionalen und zuverlässigen Apps etwas aufzuräumen. Der Hauptsinn und -zweck einer davon ist die anzeige einer Uhr und das zuverlässige Wecken am Morgen. Realisiert ist das derzeit mit einem einfachen Timer, der jede Sekunde die Uhrzeit auf der UI aktualisiert. Das ist besser als jede Minute, da die App keine Atomuhr ist und die Abweichung damit nicht wirklich spürbar ist. Ich vermute, wenn man 37 Wochen (willkürliche Zahl) davor hockt, wird man sehen, wie eine Sekunde übersprungen wird. Die Einstellungen des Weckers erlauben alle Wochentage, oder nur bestimmte Wochentage (bspw. Mo-Die) auszuwählen. Demnach gibt es auch jede Sekunde die Abfrage, ob der Wecker für den aktuellen Tag aktiviert ist. Ist das der Fall, so wird ein Zeitvergleich ausgelöst. Dies würde ich als unsauber bezeichnen, aber es funktioniert: Der AlarmTimePicker gibt die Weckzeit vor. Sobald diese Zeit von dem aktuellen Zeitpunkt überschritten wurde (if (DateTime.Now.TimeOfDay >= AlarmTimePicker.Time...)) UND die Differenz zwischen den Sekunden des DateTimePickers und des aktuellen Moments kleiner sind als 1,5 (...DateTime.Now.TimeOfDay.TotalSeconds - AlarmTimePicker.Time.TotalSeconds < 1.5)), dann soll der Alarm tatsächlich ausgelöst werden. Die Herleitung dessen hat etwas damit zu tun, dass die Sekundenzählung des Systems nicht exakt ist, also nicht exakt einer Sekunde entspricht. Im Code (ohne vorherige Deklarationen) sieht das etwa so aus:
private void UhrzeitTimer() { DispatcherTimer uhrZeit = new DispatcherTimer(); // Initialisiert Timer uhrZeit.Interval = TimeSpan.FromSeconds(1); // Legt den Interval fest, nachdem etwas passieren soll uhrZeit.Tick += UhrZeit_Tick; // Even (Tmer_Tick), das nach jedem abgelaufenen Interval ausgeführt werden soll uhrZeit.Start(); // Startet den Timer } private void UhrZeit_Tick(object sender, object e) { TimeTextBlockHHMM.Text = DateTime.Now.ToString("HH:mm"); // Stellt die aktuelle Uhrzeit in Sunden und Minuten dar TimeTextBlockSS.Text = DateTime.Now.ToString(":ss"); // Stellt die Sekunden ddar UhrzeitTextBlock.Text = DateTime.Now.ToString("HH:mm:ss"); UhrzeitTotalSec.Text = DateTime.Now.TimeOfDay.TotalSeconds.ToString(); WeckzeitTotalSec.Text = AlarmTimePicker.Time.TotalSeconds.ToString(); WeckzeitTextBlock.Text = AlarmTimePicker.Time.ToString(); DifftTotalSec.Text = (DateTime.Now.TimeOfDay.TotalSeconds - AlarmTimePicker.Time.TotalSeconds).ToString(); // Prüfen ob Alarm aktiviert ist if (_AlarmState == true) { // Prüfen ob Alarm für alle Tage aktiviert ist if (_AlarmTaeglich == true) { // Zeitabgleich und Alarm, wenn Alarmzeit erreicht ZeitvergleichAlarmausloesen(); } else { // Prüfen ob Alarm für Wochentag aktiviert ist var wochentag = DateTime.Now.DayOfWeek; switch (wochentag.ToString()) { case "Monday": if (_AlarmMontagStatus == true) ZeitvergleichAlarmausloesen(); break; case "Tuesday": if (_AlarmDienstagStatus == true) ZeitvergleichAlarmausloesen(); break; case "Wednesday": if (_AlarmMittwochStatus == true) ZeitvergleichAlarmausloesen(); break; case "Thursday": if (_AlarmDonnerstagStatus == true) ZeitvergleichAlarmausloesen(); break; case "Friday": if (_AlarmFreitagStatus == true) ZeitvergleichAlarmausloesen(); break; case "Saturday": if (_AlarmSamstagStatus == true) ZeitvergleichAlarmausloesen(); break; case "Sunday": if (_AlarmSonntagStatus == true) ZeitvergleichAlarmausloesen(); break; } } } } private void ZeitvergleichAlarmausloesen() { if (DateTime.Now.TimeOfDay >= AlarmTimePicker.Time && DateTime.Now.TimeOfDay.TotalSeconds - AlarmTimePicker.Time.TotalSeconds < 1.5) Alarm(); }
Die Frage ist nun, gibt es für eine Anzeige der Uhrzeit, sowie für das Prüfen, ob der aktuelle Zeitpunkt mit einem Vergleichswert übereinstimmt, eine bessere Methode?
Vorweg: Das Updaten der UI werde ich über MVVM und Bindings realisieren, nicht über Textblock.Text=...
Gruß René
Antworten
-
Hallo René,
hier mal ein Beispiel auch von mir.
namespace AppZeit { /// <summary> /// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann. /// </summary> public sealed partial class MainPage : Page { public MainPageViewModel ViewModel { get; set; } public MainPage() { this.InitializeComponent(); ViewModel = new MainPageViewModel(); } } public class MainPageViewModel : INotifyPropertyChanged { string _zeit = string.Empty; public string Zeit { get { return _zeit; } set { _zeit = value; OnPropertyChanged(); } } public DispatcherTimer Timer { get; set; } public MainPageViewModel() { Timer = new DispatcherTimer() { Interval = new TimeSpan(0,0,1) }; Timer.Tick += Timer_Tick; Timer.Start(); } private void Timer_Tick(object sender, object e) { Zeit = DateTime.Now.ToString("T", new CultureInfo("de-DE")); } public event PropertyChangedEventHandler PropertyChanged = delegate { }; public void OnPropertyChanged([CallerMemberName] string propertyName = null) { // Raise the PropertyChanged event, passing the name of the property whose value has changed. this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
<Page x:Class="AppZeit.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AppZeit" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock Text="{x:Bind ViewModel.Zeit, Mode=OneWay}" FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Page>
Wenn man sich die App mal im Taskmanager anschaut, dann stellt man fest das sie kaum last erzeugt. Wenn Du willst kannst Du dir die App auch hier downloaden. Ich habe mal DateTime.Now getestet und es ist effizient dies auf zu rufen
Gruß Thomas
Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!
Cross Platform Canvas for UWP, Android, iOS
UWP Community Toolkit Sample App
Alle Größenangaben in UWP müssen durch 4 teilbar sein
- Bearbeitet Thomas Wycichowski Montag, 13. November 2017 03:31
- Als Antwort vorgeschlagen Ivan DragovMicrosoft contingent staff, Moderator Donnerstag, 16. November 2017 08:17
- Als Antwort markiert Ivan DragovMicrosoft contingent staff, Moderator Freitag, 24. November 2017 08:34
-
Hi René,
entschuldige bitte. Ich hatte am Abend das Forum übersehen und eine WPF-Lösung gezeigt. Hier mal eine Möglichkeit für UWP, die geringfügig anders ist als die von Thomas gepostete, ohne zusätzlichen Code im CodeBehind und mit Binding arbeitet und dadurch etwas mehr Last bringt, da Binding über Reflection arbeitet, was aber in diesem Fall vernachlässigbar ist.Mit der Bindung als statische Ressource erzeugt die CLR eine Instanz der Klasse und im Constructor wird der Timer gestartet. Statische Klassen/Menber sind da nicht erforderlich. Auch ist die Kenntnis der MainPage in der ViewModel-Klasse nicht erforderlich, da über Eigenschaftsbindung die Page den Wert der Eigenschaft aus dem ViewModel "abruft". NotifyProprtyChanged teilt dann lediglich der Oberfläche mit, dass ein neuer Wert vorliegt, den die Oberfläche "abrufen" kann.
XAML:
<Page x:Class="App1.Page03" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <local:Page03Zeit x:Key="vm"/> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource vm}"> <TextBlock Text="{Binding UHRZEIT, Mode=OneWay}"/> </Grid> </Page>
Code:
using System; using System.ComponentModel; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace App1 { public class Page03Zeit : INotifyPropertyChanged { public Page03Zeit() { DispatcherTimer UHRZEITTimer = new DispatcherTimer(); // Initialisiert Timer der Dispatcherklasse UHRZEITTimer.Interval = TimeSpan.FromSeconds(1); // Legt den Interval fest, nachdem etwas passieren soll UHRZEITTimer.Tick += UHRZEITTimer_Tick; // Gibt an, dass Timer_Tick nach jedem abgelaufenen Interval ausgeführt werden soll UHRZEITTimer.Start(); // Startet den Timer } public string UHRZEIT { get; set; } = "leer"; private void UHRZEITTimer_Tick(object sender, object e) { UHRZEIT = DateTime.Now.ToString(); OnPropertyChanged(nameof(UHRZEIT)); } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Bearbeitet Peter Fleischer Montag, 13. November 2017 05:57
- Als Antwort vorgeschlagen Ivan DragovMicrosoft contingent staff, Moderator Donnerstag, 16. November 2017 08:18
- Als Antwort markiert Ivan DragovMicrosoft contingent staff, Moderator Freitag, 24. November 2017 08:35
Alle Antworten
-
Hi René,
für die Anzeige der Uhrzeit ist zuerst zu klären, ob eine Analoguhr zu programmieren ist oder eine Digitaluhr.Für die Ermittlung der Alarmzeit würde ich einfach die Differenzzeit zwischen Alarmzeit und (minus) aktueller Zeit berechnen und damit ein asynchrones warten ausführen.
Für die sekundengenaue Zeitanzeige kann man das analog so machen. Jede Sekunde wird mit einem ε reduziert und der reduzierte Wert dient als Zeitintervall für das Warten auf die nächste Sekunde. Die letzte Sekunde der Minute dauert dann im Mittel um 60 x ε länger. Damit können dann alle Ungenauigkeiten eliminiert werden, indem in der letzten Sekunde auf die Differenz zur vollen Minute gewartet wird. So arbeiten z.B. die analogen Bahnhofsuhren. Der Motor für den Sekundenzeiger läuft geringfügig schneller und er wartet dann zur vollen Minute etwas.
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Hallo René,
ich würde eine analoge Uhr machen. Dafür würde ich mich an den globalen Rendering Prozess hängen. Dieser Tickt ca. alle 16ms. Windows.UI.Xaml.Media.CompositionTarget.Rendering. Mit einem LinearTween hätte man dann eine sehr schöne flüssige Bewegung.
static double LinearTween(double t, double b, double c, double d) { t /= d; return c * t * t + b; }
Ich habe das ganze mal für mein Canvas Framework als Beispiel entwickel. Eine Uhr ähnlich Darstellung sieht man ab Minute 1 in diesem Video in der Mitte ist die UWP App.
Beim Tick würde ich auch prüfen ob der Wecker jetzt eingeschalten werden muss.
Gruß Thomas
Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!
Cross Platform Canvas for UWP, Android, iOS
-
Hi,
das sind schöne Vorschläge. Ich hatte gar nicht an eine analoge Uhr gedacht. Das überlege ich mir mal!
Aber meine Frage zielt viel mehr darauf ab, ob es nicht einen ressourcenschonenderen Weg gibt. Vielleicht mache ich mir aber auch zu viele Gedanken. Gehen wir weiter von simplen Textblöcken aus.
Ist es bspw. Sinnvoller einen Timer mit Intervall 1 Minute zu wählen und jede Minute die Minutenanzeige hochzuzählen und NICHT DateTime.now anzurufen? Und einen zweiten Timer (Intervall 1s), der die Sekunden anzeigt (auch ohne DateTime.now) und zu jedem MinutenTick auf 0 gesetzt wird? Und jeweils um Mitternacht (ermittelt am MinutenTimer) wird DateTime.now abgerufen, sodass die Differenz nicht stetig anwächst.
Oder ist es wirklich der sinnvollste Weg, jede Sekunde DateTime.now anzurufen?
Was den Wecker betrifft, ich finde es auch unsauber, wie ich es jetzt mache, die Differenz aus Istzeit und Weckzeit zu bilden und das jede Sekunde. Da ich die Weckzeit als HH:MM einstelle, wäre es nicht logischer nur die Stunden und Minuten zu vergleichen? Dann hätte ich wieder ein Ungenauigkeitsproblem (was theoretisch gleichermaßen bei sekundengenauer Prüfung auftritt). Theoretisch wäre es im Grenzfall ja möglich, dass bei zweimaligem Aktualisieren (von DateTime.now) die gleiche Minute angezeigt wird, oder?
Wie groß kann die zulässige Differenz denn sein? Schließlich muss der Wecker zuverlässig sein. Andererseits darf die Differenz auch nicht so groß/klein sein, dass der Wecker 2 mal anspringt.
Ich vermute es ist notwendig einen weiteren Timer einzuführen, der die Weckfunktion deaktiviert. Angenommen bei sekundengenauem Zeitabgleich: Da bei zweimaligem Aktualisieren auch theoretisch dieselbe Sekunde angezeigt werden kann, muss also ab dem Weckerauslösen min. solange das Auslösen des Weckers deaktiviert werden, wie der Aktualisierungsintervall (1 Sekunde) ist. Gehen wir davon aus, dass der Mensch nicht in Sekunde 1 und Sekunde 2 wieder geweckt werden möchte, dann kann man diesen Unterdrückungstimer 1,5 oder such 2 Sekunden lang machen.
Ganz nebenbei, bei jedem Tick wird auch das PropertyChanged Ereignis ausgelöst, sodass die UI, also die Textblöcke überhaupt angepasst werden. Oder wäre es hier tatsächlich sinnvoller in der MainPage.cs den Textblock.Text = … bei jedem Tick upzudaten?
Gruß René
-
Hallo zusammen,
ich stelle fest, mein Problem ist etwas fundamentaler... Ich möchte folgendes erreichen:
Die MainPage.xaml enthält einen Texblock, vorerst nichts weiter. Die MainPage.cs enthält erst mal auch nichts, außer die gegebene Initialisierung und den Aufruf der Timer-Initialisierung, die gleich folgt. Ich füge eine Klasse "Zeit.cs" hinzu. Darin möchte ich mit dem Timer die Uhrzeit (und später den Weckerabgleich) sekündlich aktualisieren. Via PropertyChanged und Binding soll der Textblock die aktuelle Zeit anzeigen (entweder als String, oder mit IConverter, später...).
Der Textblock:
<TextBlock Name="ZEITTexblock" Text="{Binding UHRZEIT}"/>
MainPage:
public MainPage() { this.InitializeComponent(); DataZEIT.InitUhrZeitTimer(); }
Zeit.cs:
class ZEIT : INotifyPropertyChanged { private string UHRZEIT { get; set; } public void ChangeZEIT(string propertyName) { UHRZEIT = DateTime.Now.ToString(); NotifyPropertyChanged(propertyName); } public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
public class DataZEIT
{
private ZEIT _ZEIT = new ZEIT();public DataZEIT()
{
_ZEIT = new ZEIT();
}public static void InitUhrZeitTimer()
{
DispatcherTimer UHRZEITTimer = new DispatcherTimer(); // Initialisiert Timer der Dispatcherklasse
UHRZEITTimer.Interval = TimeSpan.FromSeconds(1); // Legt den Interval fest, nachdem etwas passieren soll
UHRZEITTimer.Tick += UHRZEITTimer_Tick; // Gibt an, dass Timer_Tick nach jedem abgelaufenen Interval ausgeführt werden soll
UHRZEITTimer.Start(); // Startet den Timer
}private static void UHRZEITTimer_Tick(object sender, object e)
{
ZEIT _ZEIT = new ZEIT();
_ZEIT.ChangeZEIT("UHRZEIT");}
}Ich mache die Funktionen ungern statisch, möchte aber auf der MainPage ausschließlich die Initialisierung aufrufen, sodass es dann automatisch läuft. Dass führt auch dazu, dass ich in der Zeit.cs die ganzen Objektverweise benötige.
Das Resultat ist leider nichts. Der Textblock zeigt keine Uhrzeit an.
Ist es überhaupt möglich eine separate Klasse autonom fahren zu lassen und mit Binding Inhalte von Variablen auf der MainPage anzuzeigen?
Gruß René
-
Hi René,
nachfolgend mal eine kleine Demo mit Deinen Codeauszügen:XAML:
<Window x:Class="WpfApp1CS.Window20" 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:WpfApp1CS" mc:Ignorable="d" Title="Uhr" Height="300" Width="300"> <Window.Resources> <local:Window20Zeit x:Key="vm"/> </Window.Resources> <Grid DataContext="{StaticResource vm}"> <Label Content="{Binding UHRZEIT}" VerticalAlignment="Center" HorizontalAlignment="Center"/> </Grid> </Window>
Code:
using System; using System.ComponentModel; using System.Windows; using System.Windows.Threading; namespace WpfApp1CS { public class Window20Zeit : INotifyPropertyChanged { public Window20Zeit() { disp = Application.Current.Dispatcher; DispatcherTimer UHRZEITTimer = new DispatcherTimer(); // Initialisiert Timer der Dispatcherklasse UHRZEITTimer.Interval = TimeSpan.FromSeconds(1); // Legt den Interval fest, nachdem etwas passieren soll UHRZEITTimer.Tick += UHRZEITTimer_Tick; // Gibt an, dass Timer_Tick nach jedem abgelaufenen Interval ausgeführt werden soll UHRZEITTimer.Start(); // Startet den Timer } Dispatcher disp; public string UHRZEIT { get; set; } = "leer"; private void UHRZEITTimer_Tick(object sender, object e) { disp.Invoke(() => { UHRZEIT = DateTime.Now.ToString(); OnPropertyChanged(nameof(UHRZEIT)); }); } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Hallo Peter,
danke für deine Antwort. Ich bin kein Fachmann, aber kann es sein, dass den Beispiel auf Windows Forms basiert? Ich kann das so nicht ganz umsetzen.
Der Dispatcher lässt sich so in meiner UWP App nicht umsetzen. "Typ oder Namespace wurde nicht gefunden". Einen richtigen Assemblyverweis schlägt mir IntelliSense nicht vor.
Weiterhin habe ich das Problem, dass ich den Timer starten muss. Daher rufe ich nach dem Initialisieren der MainPage (in der MainPage.cs) die in deinem Beispiel "Window20Zeit" genannte Funktion auf (ansonsten wird der Timer nie gestartet). Doch dann benötige ich wieder einen Objektverweis auf der MainPage, oder ich mache die "Zeit-Funktionen" statisch. Dadurch würde ich dann bei jedem Timer_Tick Event eine Objektinstanz erstellen.
Gruß René
-
Hallo René,
hier mal ein Beispiel auch von mir.
namespace AppZeit { /// <summary> /// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann. /// </summary> public sealed partial class MainPage : Page { public MainPageViewModel ViewModel { get; set; } public MainPage() { this.InitializeComponent(); ViewModel = new MainPageViewModel(); } } public class MainPageViewModel : INotifyPropertyChanged { string _zeit = string.Empty; public string Zeit { get { return _zeit; } set { _zeit = value; OnPropertyChanged(); } } public DispatcherTimer Timer { get; set; } public MainPageViewModel() { Timer = new DispatcherTimer() { Interval = new TimeSpan(0,0,1) }; Timer.Tick += Timer_Tick; Timer.Start(); } private void Timer_Tick(object sender, object e) { Zeit = DateTime.Now.ToString("T", new CultureInfo("de-DE")); } public event PropertyChangedEventHandler PropertyChanged = delegate { }; public void OnPropertyChanged([CallerMemberName] string propertyName = null) { // Raise the PropertyChanged event, passing the name of the property whose value has changed. this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
<Page x:Class="AppZeit.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AppZeit" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock Text="{x:Bind ViewModel.Zeit, Mode=OneWay}" FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Page>
Wenn man sich die App mal im Taskmanager anschaut, dann stellt man fest das sie kaum last erzeugt. Wenn Du willst kannst Du dir die App auch hier downloaden. Ich habe mal DateTime.Now getestet und es ist effizient dies auf zu rufen
Gruß Thomas
Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!
Cross Platform Canvas for UWP, Android, iOS
UWP Community Toolkit Sample App
Alle Größenangaben in UWP müssen durch 4 teilbar sein
- Bearbeitet Thomas Wycichowski Montag, 13. November 2017 03:31
- Als Antwort vorgeschlagen Ivan DragovMicrosoft contingent staff, Moderator Donnerstag, 16. November 2017 08:17
- Als Antwort markiert Ivan DragovMicrosoft contingent staff, Moderator Freitag, 24. November 2017 08:34
-
Hi René,
entschuldige bitte. Ich hatte am Abend das Forum übersehen und eine WPF-Lösung gezeigt. Hier mal eine Möglichkeit für UWP, die geringfügig anders ist als die von Thomas gepostete, ohne zusätzlichen Code im CodeBehind und mit Binding arbeitet und dadurch etwas mehr Last bringt, da Binding über Reflection arbeitet, was aber in diesem Fall vernachlässigbar ist.Mit der Bindung als statische Ressource erzeugt die CLR eine Instanz der Klasse und im Constructor wird der Timer gestartet. Statische Klassen/Menber sind da nicht erforderlich. Auch ist die Kenntnis der MainPage in der ViewModel-Klasse nicht erforderlich, da über Eigenschaftsbindung die Page den Wert der Eigenschaft aus dem ViewModel "abruft". NotifyProprtyChanged teilt dann lediglich der Oberfläche mit, dass ein neuer Wert vorliegt, den die Oberfläche "abrufen" kann.
XAML:
<Page x:Class="App1.Page03" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <local:Page03Zeit x:Key="vm"/> </Page.Resources> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource vm}"> <TextBlock Text="{Binding UHRZEIT, Mode=OneWay}"/> </Grid> </Page>
Code:
using System; using System.ComponentModel; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace App1 { public class Page03Zeit : INotifyPropertyChanged { public Page03Zeit() { DispatcherTimer UHRZEITTimer = new DispatcherTimer(); // Initialisiert Timer der Dispatcherklasse UHRZEITTimer.Interval = TimeSpan.FromSeconds(1); // Legt den Interval fest, nachdem etwas passieren soll UHRZEITTimer.Tick += UHRZEITTimer_Tick; // Gibt an, dass Timer_Tick nach jedem abgelaufenen Interval ausgeführt werden soll UHRZEITTimer.Start(); // Startet den Timer } public string UHRZEIT { get; set; } = "leer"; private void UHRZEITTimer_Tick(object sender, object e) { UHRZEIT = DateTime.Now.ToString(); OnPropertyChanged(nameof(UHRZEIT)); } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Bearbeitet Peter Fleischer Montag, 13. November 2017 05:57
- Als Antwort vorgeschlagen Ivan DragovMicrosoft contingent staff, Moderator Donnerstag, 16. November 2017 08:18
- Als Antwort markiert Ivan DragovMicrosoft contingent staff, Moderator Freitag, 24. November 2017 08:35
-
Hallo Thomas,
habe meine letzte Frage gelöscht. Nach einem ganzen Sonntag habe ich nun - maßgeblich dank deiner Hilfe - eine solide App. D.h. meine bestehende maximal verbessert. Ich bin aber sicher, dass es noch viel Potenzial gibt. Zum Beispiel im Zusammenhang mit x:Bind und dem PropertyChanged Event. Ich habe durch dein Beispiel (LINK) das MVVM-Konzept nun auch viel besser verstanden. Damit habe ich dann meine Zeit/Wecker-App grundlegend überarbeitet.
Ich habe nun "Globale Settings", die im json-Format gespeichert und nach einem Neustart des Systems wieder herangezogen werden. Um alles im Blick zu behalten, brauchte ich ein Debug-Panel (ein UserControl mit einfachen Status-TextBlocks). Das Binding war bis heute noch ein Problem. Sieht nun so aus (wobei einiges dazu kam und kommt):
public class GlobalSettings : INotifyPropertyChanged { public bool _WeckerStatus; public TimeSpan _WeckerWeckZeit { get; set; } public bool WeckerStatus { get { return _WeckerStatus; } set { _WeckerStatus = value; RaisePropertyChanged("WeckerStatus"); } } public TimeSpan WeckerWeckZeit { get { return _WeckerWeckZeit; } set { _WeckerWeckZeit = value; RaisePropertyChanged("WeckerWeckZeit"); } } public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); Model.GlobalSettingsData.saveGlobalSettingsAsync(); } } }
In XAML (aufs Wesentliche reduziert):
<TextBlock Text="WeckerStatus" Grid.Column="0" Grid.Row="0" /> <TextBlock Text="{x:Bind GlobalSettings.WeckerStatus, Mode=OneWay}" Grid.Column="1" Grid.Row="0" /> <TextBlock Text="WeckZeit" Grid.Column="0" Grid.Row="1"/> <TextBlock Text="{x:Bind GlobalSettings.WeckerWeckZeit, Mode=OneWay}" Grid.Column="1" Grid.Row="1" />
Für Play/Pause habe ich nur einen Button, der den entsprechenden MDL2 Content annimmt. Dazu brauchte ich einen Converter, der ein eigenes enum (mit Play und Pause) in den entsprechenden String für die Symbole im Button umwandelt. Das hat auch jetzt erst auf Anhieb funktioniert - nachdem ich endlich einige Zusammenhänge mehr verstanden habe.
Vielen Dank nochmal!
Gruß René