This article introduces how to use .NET MAUI to develop an electronic wooden fish application. Taking the actual small application development as an example, through this development process, it introduces related knowledge points such as .NET MAUI, Blazor, and front-end. The applications involved in the article have been open sourced on Github, you can go to download and experience: https://github.com/sangyuxiaowu/MuYu
1. Background
We introduced earlier ".NET MAUI Development of Electronic Wooden Fish (Part 1)" , and then develop related functions. It mainly includes: tapping count and automatic tapping mode switching.
2. Relevant knowledge points
This article mainly includes the following relevant knowledge points:
- .NET MAUI Lifecycle
- .NET MAUI Blazor saves data before app closes
- The timer in Blazor, the use and start and stop of System.Threading.Timer
3. Development process
3.1 Tap count
Knock count here mainly records the number of clicks and the total number of clicks of the day. In order to satisfy the knocks of various gods, here we use long or use Preferences to store.
private long AllNum = 0; private long TodayNum = 0;
When the main page Index.razor renders for the first time, we read the stored count information.
protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { AllNum = Preferences.Default.Get("all_num", 0L); if (DateTime.Now.ToShortDateString() == Preferences.Default.Get("today","")) { TodayNum = Preferences.Default.Get("today_num", 0L); } } }
It should be noted that: AllNum is of long type, and the default value passed by Preferences.Default.Get is to infer the type of the obtained storage content, so 0L needs to be marked as long type here. Otherwise, there is no error in the first startup, and an error will be reported after the data is stored, and the data type cannot be converted.
Next, we only need to implement a counting addition and storage function:
void AddNum() { // current date var today = DateTime.Now.ToShortDateString(); // date stored string save_today = Preferences.Default.Get("today", ""); // The number of days has not changed, and the defense is knocked in the middle of the night if (save_today == today) { TodayNum++; }else{ TodayNum = 1; Preferences.Default.Set("today", today); } AllNum++; Preferences.Default.Set("today_num", TodayNum); Preferences.Default.Set("all_num", AllNum); }
3.2 Counting problem optimization
Although the previous section realized the tap count, there are some unreasonable aspects of the design: frequent writing of Preferences is not good after all. After all, there is no need to save this kind of application in real time. It is better to get the closing event of the application and save it when the application is closed.
But because of the mixed development model of .NET MAUI Blazor, the situation is a little more complicated, and the detours taken will not be mentioned here. No matter how we deal with it, we still need to understand .NET MAUI application life cycle . Here, in order to facilitate cross-platform processing, we can choose the cross-platform lifecycle event Stopped: this event will be triggered when the window is no longer visible. At this point we can do some key data storage.
As for the tap counting function, we use a global static class to manage it, which is convenient for access in Blazor and App. Create the following class HitCounter.cs in the Data directory:
public static class HitCounter { private static long count; private static long todayCount; private static string today; static HitCounter() { count = Preferences.Default.Get("all_num", 0L); todayCount = 0; today = DateTime.Now.ToShortDateString(); if (today == Preferences.Default.Get("today", "")) { todayCount = Preferences.Default.Get("today_num", 0L); } } /// <summary> /// Update count after hit /// </summary> public static void Increment() { if (today != DateTime.Now.ToShortDateString()) { today = DateTime.Now.ToShortDateString(); todayCount = 0; } count++; todayCount++; } /// <summary> /// Save data to Preferences /// </summary> public static void Save() { if (today != DateTime.Now.ToShortDateString()) { today = DateTime.Now.ToShortDateString(); todayCount = 0; } Preferences.Default.Set("today_num", todayCount); Preferences.Default.Set("today", today); Preferences.Default.Set("all_num", count); } /// <summary> /// total count /// </summary> public static long Count { get { return count; } } /// <summary> ///count of the day /// </summary> public static long TodayCount { get { return todayCount; } } }
To subscribe to Window lifecycle events, you need to override the CreateWindow method in the App class of the App.xaml.cs file and add an instance of the subscribed event there:
protected override Window CreateWindow(IActivationState activationState) { Window window = base.CreateWindow(activationState); window.Stopped += (s, e) => { Data.HitCounter.Save(); }; return window; }
In Index.razor, don’t override OnAfterRenderAsync , just assign it directly:
private long AllNum = Data.HitCounter.Count; private long TodayNum = Data.HitCounter.TodayCount;
However, the update of the hit count is not displayed in the Blazor interface in real time. When the display needs to be updated, for example, the action here is to open the setting menu and display it in the upper right corner, then you only need to reassign the value in the event of opening the menu:
void ShowMenu() { ShowSetting = true; // update data AllNum = Data.HitCounter.Count; TodayNum = Data.HitCounter.TodayCount; }
3.3 Automatic tapping
Automatic tapping and accumulating merit are also an important function of the electronic wooden fish. In Blazor, we can implement timers using System.Threading.Timer or JavaScript Interop. Here choose to use System.Threading.Timer to achieve.
First of all, we need to understand the parameters of Timer(TimerCallback callback, object state, int dueTime, int period);
parameter | illustrate |
---|---|
callback | The order will be executed repeatedly within the period time interval |
state | Parameters can be passed in to the object that you want to handle in the callback delegate |
dueTime | How long does it take for the callback to start executing |
period | Identifies how often to execute the callback |
In Index.razor we define and create a timer at OnInitialized, here to control start and stop, here dueTime and period are set to Timeout.Infinite . Timeout.Infinite is a constant used to specify an infinite waiting time, here it is guaranteed that the timer will not be triggered callback.
private Timer _timer;//automatic tap timer private bool _isTimerRunning = false;//timer start switch protected override void OnInitialized() { _timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite); base.OnInitialized(); }
After that, we only need to change the timing parameters by the following method to switch the start and stop status of the timer:
private void TimerSwitch() { if (_isTimerRunning) { _timer.Change(Timeout.Infinite, Timeout.Infinite); _isTimerRunning = false; } else { _timer.Change(0, 1000); _isTimerRunning = true; } }
4. Finally
So far, a small application of .NET MAUI Blazor has been developed, and a precompiled apk and a sideloaded windows_x64 application are provided on GitHub. Interested students can try it out.