One of the things that LEVEL51 has wanted to do for a while is to rewrite the SystemX in the new UI. We want it to be an App for mobile phones/Tablet as well and want it to share the code without having to rewrite many times. time
As for SystemX, it's written with Windows Presentation Foundation (WPF) technology , which, to me, is still the best UI framework and one of the fastest to build nice-looking applications. WPF's is Silverlight and Universal Windows App back then, let's have a look.
ThaiPBS Client for Windows Phone
KFC App for Windows 8
NostraMap for Windows Phone
Programs that look like this space We have been able to do this since 10 years ago. I wonder why the program looks like it hasn't gone from now on 😅
But the downside of WPF is that it is firmly tied to Windows as the name suggests, making it a cross-platform app. It was impossible in the past. Unless it's already Closed Source. It's also very complicated. Here, to the point that someone saw a business opportunity and created a Framework/Tools for Port Application written in C#/WPF to become a Web Application named http://cshtml5.com/ . I looked at it roughly. Works well ever It converts screen code written in XAML to HTML5 and translates C# code to JavaScript with Bridge.net. Of course we can just take the generated file and put it in Ionic's Cordova/Capacitor and make it. can become a Mobile App
From: CSHTML5.Samples.Showcase You guys are awesome. Render XAML to HTML.
And there is another group that is trying to do the same called uno platform https://platform.uno/ which uses the Render UI itself, which is the same idea as the WPF Rendering on Windows with DirectX (WPF aging 14 years ago~) Instead of trying to use native controls for each platform like Flutter uses Skia to render the UI, in terms of branding, it's better than making apps that look like villagers anyway.
You guys are very gods UNO Platform
Actually, for people who have used C#/WPF before like us, Uno is probably the most interesting option. But when we choose to use any platform, we have to consider how much Community there is ( actually, it's quite a lot for uno ) and how much we have to "fight" to use it. After trying the Demo of Uno for a while, then I started to see that We depend a lot on him since the Uno team developed all of our Frontends. what doesn't work We will stick with him until the problem is solved.
So we have to find each other again... Finally, come back to Blazor again before being confused that hey, you wrote App Windows, why are you messing with web writing technology? Let's look at Blazor together first...
Get to know Blazor
Blazor is an open source framework for developing Single Page Applications (SPAs) without the need to write client-side JavaScript (i.e. writable if requested). The framework is now included as part of ASP.NET Core. already This is Microsoft's development in a new way. ASP.NET Core is also an open source project. The project is supported by the .NET Foundation , whose members are Microsoft, Amazon Web Service, and Telerik, a subsidiary of Progress, which owns the famous ERP system.
Do you want to make merit and have your name in here? Make merit 1.5 million baht per year
The difference between Blazor and other SPA frameworks is that we don't have to spend time coding in JavaScript in order to make the server and web pages talk to each other, see NancyBlack 's Github (the LEVEL51 web-based system). ) You can see that only JavaScript has thrown 40% of the code already. Relatively proportional to the back of the house C# code, almost 3 times more, but in the Repo, it has Angular/Jquery code. that it is mixed with The results may not be exactly right. But if you take it from your senses, at least write JavaScript more than C# for sure 🤣 This doesn't include the LEVEL51 web page code at all.
By the time I chose to start using Blazor, I sat and considered several components. Because I feel that I should dismantle this 10 year old web system already. It has already been written in this post at this link. The post will be about using Blazor to act as a CMS system mainly, which is to focus on making websites, and this post will focus on Interactive.
In addition, there is one younger generation sent to view. There are 2 more frameworks that try to do similar to Blazor, which is to reduce JavaScript and allow Server to render more web pages. Two frameworks that are
- Hotwrite - Let us put a Component named <turbo-frame> and what's inside the turbo-frame can be sent as an Update from the Server. That being said, this idea is going to be the same as ASP.NET Ajax 13 years ago. Same,
Screencap from video on Hotwire web page,
similar to ASP.NET AJAX from 2007~ - Phoenix LiveView - Use Websocket to retrieve HTML that is rendered from server as well. Similar to Hotwire, you can write command to capture event from client by using attribute phx-click. Component code looks like this.
The "brightest" part of Blazor is that we can insert the Blazor code directly into any HTML page without having to change the parts. of the screen to make it very different from the original For those who have used AngularJS or Vue before, you will immediately feel familiar. before believing me Let's try to make a Todo List program that can be shared across devices. together is better
For this sample code You can find it on Github, nantcom/coresharp-blazorinteractive1: Sample Code from CoreSharp Blog (github.com).
Start creating a Todo List program.
For creating a new Project Blazor, you can study it from the past post. Please don't write it again. Let's go do that.
First of all, we will need a class to store data. Start by creating a Class by right clicking in Folder Data and selecting Add/Class and name it "Todo.cs".
and we will put the code like this
using System; using System . Collections . Concurrent; using System . Collections . Generic; using System . Linq; using System . Threading . Tasks; namespace CoreSharp . SampleBlazor . Data { public class Todo { public string Owner { get ; set ; } public bool IsDone { get ; set ; } public string Title { get ; set ; } public static ConcurrentBag < Todo > Items { get ; } = new (); } }
That is, in our Class there are 3 Attributes which are Owner, IsDone, Title. In C# language, having Get/Set after this is Getter/Setter And we'll call it a Property , but we don't do anything special with it (i.e. just put Get/Set). In this way, we will call Auto Implemented Property equal to creating Field (Variable in Class) and inserting Function Getter/Setter. that reads and sets the value of that variable exactly like this
private string _Owner; public string Owner { get { return _Owner; } set { _Owner = value ; } }
ConcurrentBag is a List or Array or Vector, whichever language it comes from. but what it is called ConcurrentBag That is, it can accept items added from multiple threads at the same time. The part that it declares statically is that it will be a Property with only 1 instance per 1 process of the program, therefore it is a Singleton .
And we need a new component for the todo screen, so we have to create it using the Add / Razor Component menu.
And to tell the Blazor system that this component can be accessed via /todo, just add Directive @page as follows.
@page "/todo"
If you look in the file, you will see that there is a part that says @code . In fact, we can also separate c# code into Code Behind files, but for ease of understanding. We'll include it in here first. Add the following code:
public bool IsNameSet { get ; set ; } public string Name { get ; set ; } public void SetName () { if ( string . IsNullOrEmpty (Name) == false ) { this . IsNameSet = true ; } }
The code that we added has 2 Properties and a Function for setting the value of Name. In the function, the Property IsNameSet will be changed to True only if the Property Name is not a null or null value.
Then we add HTML with Razor directive, similar to Vue and Angular:
- In an Element's Attribute, we can put expressions the same way as AngularJS/Vue can be used by using @( ... ) to wrap an expression. In the example, you can hide/display elements using Bootstrap's d-none / d-block classes.
- The value of the input can be bound to the property that we have created. Similar to AngularJS's ng-model or Vue's v-model .
- A simple HTML button can be fired as a command to call a function on the server side by adding an @ to the event name. For example, @onclick="SetName" means to call the recently created SetName function .
There is a small tip that we are definitely not confused between Blazor and JS. If we try to add a Tag Script, the compiler will not allow and tell us to use another method other than adding a Tag Script instead (can call script from c# too).
When you try to run it, you will see that when we type a name and press the button, the Form screen will be Hide (with CSS as display none) and an alert that says Hello followed by the typed name is displayed.
If we check Netowrk Activity, we can see that Blazor connects to the Server via Websocket and sends values back and forth between Servers using a very small bandwidth, only 350 Byte per 1 cycle. That is, Blazor does not send HTML from Server, but as far as I know, it's a command to modify the DOM as seen by the Server.
In addition, we can also write HTML in Razor in a way that is more visible and easier to understand by using @if like this. Check it out and find that it still uses the same bandwidth as Show/Hide with a class that Write as an expression
At this point, we can login foolishly. Now comes the step of making the screen able to add some Todo. Start by adding a little bit of code in the code section of the Todo.razor screen to support adding a new Todo.
The reason why we have to write Data.Todo is because we named this page Todo.razor, it will assume that this page class name is Todo as well. In addition, the Todo class that we created is in a different Namespace from us at the moment, so we have to add Data. to clarify
public Data . Todo NewItem { get ; set ; } = new (); public void SendNewItem () { var item = this . NewItem; this . NewItem = new (); item . Owner = this . Name; Data . Todo . AddItem ( item ); }
Then add HTML code, bind Event and bind Value as before.
- Let this input bind to a Property Title of a Property NewItem of type Todo (the Todo class we created initially).
- Notice that the Foreach statement is used to Iterate (Loop) into the ConcurrentBag. That we created before,
and we don't just Iterate, we sort the data by looking at the Property Title as well.
For those of you who have used AngularJS / Vue, you will probably be very familiar with it, because in number 2) is the same concept as ng-repeat and v-for , but the list that we work with is not a list on the JavaScript side, but a list in Server memory at all
If we try to run it, we will find that data from 2 Clients can be seen at the same time. But it may not be what we want.
Landmark
- If we unfocus the Inputbox (in Gif is the blue mouse click) data from another page It will be displayed immediately, that is, after binding processing, Blazor can detect that the List has changed.
- Information we add It is not immediately displayed in another screen until Event Binding occurs.
For those of you who have used other frameworks, you will begin to think that You'll need to use the Reactive Framework (Observable, Subject, ReplaySubject, BehaviorSubject…), but in C# there's a system called Events that we can use in this case.
This time we go back to Todo.cs in Folder Data again to add a little bit of code as follows:
- Create a new event named NewItemsAdded. Its type is an Action - which is a Function Pointer that .NET calls " Delegate " for a function that accepts nothing. and does not return anything. Functions that can be stored by Action are like public void DoSomething() { ... } etc.
Putting = delegate {} behind it means we'll put an Empty Function first. So that when we run Delegate, we don't have to enter If, check if this event has a null value every time. - Create a new static function to add a new item to the ConcurrentBag Items and broadcast an event named NewItemsAdded . Broadcast is written like a normal function call, but instead is entered as an event name.
On the Razor page, make a few changes to the code as follows:
- Override the OnInitialized() function to insert the code to execute after this component is started by adding the subscriber code to the Event NewItemsAdded. that we created. Notice that += is Subscribe or Add Delegate (Function Pointer) of Function. We go to Event NewItemsAdded
and from () => ... onwards is to create Function with Lambda Expression (similar to JavaScript), in which this function We call the InvokeAsync statement. InvokeAsync takes the parameter Delegate of the function we want to run in the UI thread. We pass the Delegate of the Component's StateHasChanged function
. We will notify Blazor that the state of this component has changed. Update the screen accordingly. - Instead of calling Items.Add directly, we change to call the function that we created instead.
When I try to run the program It will get the result as we want, that is, Add from one page and show it to another page as well.
Realtime even more with Sync Press Done Task too!
In order for the press status to be synced across the page as well, you can use an Event again as well. This time, we will use the event creation directly on the Todo.razor page like this.
private static event Action StateChanged = delegate { }; private void OnStateChanged () { Todo . StateChanged(); } protected override void OnInitialized () { Todo . StateChanged += () => { this . InvokeAsync ( this . StateHasChanged ); }; }
And when Done is clicked, Event Blur can be used to trigger a new HTML rendering as well.
< input class = "form-check-input" type = "checkbox" @ bind = " item . IsDone " @ onblur = " OnStateChanged " >
But the sync will happen after going to focus on another control first. The reason why we can't use @onchange is because @bind is already using @onchange because @bind will be changed by Compiler to become @onchange for us ( It's Syntactic Sugar), but that means we can actually write an event handler at @onchange inline with C# itself!
But with Complicate of Attribute checked of Tag input, we can't put that checked="@(item.IsDone)" , because if Attribute Changed is present, it is immediately considered "Checked" no matter what value you enter. So we have to create a function to release Attribute checked out only when IsDone value is True as follows:
private IEnumerable < KeyValuePair < string , object >> GetChecked (Data . Todo item ) { if ( item . IsDone) { yield return new KeyValuePair < string , object >( "checked" , "checked" ); } yield break ; }
Written like this is called an Iterator, we don't have to create an Array or List in order to return a series value. You can use a yield return like this. Let's study the example here.
The code in the checkbox will change like this:
< input class = "form-check-input" type = "checkbox" @ onchange = " ( e ) => { item . IsDone = ( bool ) e . Value; this . OnStateChanged (); } " @ attributes = " GetChecked ( item ) " >
Landmark
- At @onchange, we can write it as a Lambda of C# and refer to the item variable in the loop.
- @attributes call the GetChecked function by passing an item variable in order to let the Attribute chcked out only if the value of IsDone is true.
That's all!
So how does a program that messes with the hardware settings run with the web-based Blazor?
For this SystemX we designed from the beginning that the screen With the hardware setting that works separately, that is, the screen that is the UI, there is no code related to the Hardware setting at all, that is, the code that actually works is in the program called SystemX Agent, and all It communicates with each other with SignalR system, so we can easily make a new UI because UI is really only UI.
You can see that in addition to the Windows program screen, we also have a web screen that can be viewed from a mobile phone, namely SystemXZ, SystemX Deck that can be used to operate the machine, and SystemX Log Viewer that is also a web page.
SystemZ |
SystemX Deck |
SystemX Log Viewer |
So everything is ready to turn SystemX's WPF screen into a web too. We just haven't done it yet 😅
As for how we can make the original SystemX screen with HTML5 and make it run as a Windows program, let's follow along in the next part. Hello~
For the code, follow this path: nantcom/coresharp-blazorinteractive1: Sample Code from CoreSharp Blog (github .com)