Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This is a short book written by Gabriele del Giovine and translated into English by Julie Hirt.
Welcome to Learn Wisej.NET, a premier resource for developers seeking to advance their knowledge and stay up-to-date on the latest developments in the Wisej.NET ecosystem. Our platform is home to articles from distinguished and respected authors in the industry, offering practical insights on a range of topics related to Wisej.NET.
Whether you are an experienced .NET developer or new to the field, Learn Wisej.NET provides valuable resources to help you enhance your skills. The articles cover best practices, coding techniques, performance optimization, and much more, all written by .NET developers with extensive experience in the industry.
To easily access the search bar and explore Wisej.NET articles on any topic, scroll to the top of the page and click on the search bar icon. Once clicked, a search box will appear, allowing you to enter keywords or phrases related to your desired topic.
Below you will find a list of authors who contribute their expertise to LearnWisej.NET. Each author brings knowledge from their industry experience. Explore their articles and gain valuable industry insights from these experts at LearnWisej.NET.
iX Magazine: From Desktop to Web app with Wisej.NET
The German iX Magazine featured Wisej.NET for modernizing Windows applications into true web & cloud solutions.
Going Native with Wisej.NET Hybrid
If you're targeting native devices like Android or iOS, then Wisej.NET's Hybrid apps can help you build your app quickly (using the Wisej.NET ecosystem).
Dr. Veikko Krypczyk
Developer and Technical Author
Consider the question of how to "future-proof" an application and create a scalable architecture that separates our logic from models and views. What are the different approaches one could take to build a "21st century web app" using Wisej.NET?
In this article, we will explore two approaches: Model-View-Controller (MVC) and Model-View-ViewModel (MVVM).
Many desktop applications for the Windows operating system were created using the Windows Presentation Foundation (WPF) graphics framework. WPF offers a modern declarative way of creating user interfaces and supports a variety of features that simplify the development of complex applications. The technical foundations and concepts of WPF include:
XAML (eXtensible Application Markup Language): XAML is a declarative markup language to define the user interface. It allows UI elements and their properties to be described structurally, supporting the separation of layout and logic.
Data Binding: Data binding allows UI elements to be bound directly to data sources. This simplifies the synchronization of data between the user interface and the underlying data model.
Styling: A style consists of a collection of properties that can be applied to one or more UI elements. This enables consistent UI design and simplifies changing the application's appearance.
UI Controls: There are a variety of UI controls that can be used to create interactive user interfaces, such as Button, TextBox, ListView, and DataGrid. These controls are customizable and support data binding and styling.
Commands: A command is an action triggered by the user interface and processed in the ViewModel or code-behind. It ensures the separation of user interface and logic.
Wisej.NET offers a platform for transforming desktop applications into powerful web applications. It combines the flexibility of HTML and JavaScript with the power and familiar paradigms of .NET and Windows Forms. Wisej.NET relies on a server-side architecture where most of the application logic is executed on the server. This offers several advantages, including increased security and the ability to implement complex business logic without compromising performance. The server only sends the necessary UI updates to the client, optimizing bandwidth usage and enhancing application responsiveness.
The framework provides an extensive library of UI components similar to those of Windows Forms and WPF. These components are highly customizable and support modern web technologies like HTML5, CSS3, and JavaScript. Examples of UI components include Buttons, Text Boxes, List Views, Data Grid Views, Tree Views, and many others, including advanced widgets like diagrams, maps, schedulers and third-party integrations.
Wisej.NET supports extensive data binding capabilities that allow UI components to be easily bound to data sources. The application can be customized using a flexible styling engine (theming). The framework uses an event-driven programming model similar to that of Windows Forms. One can respond to user interactions server-side, simplifying the implementation of application logic. Client events can be sent to the server via Web Sockets or AJAX and be processed there. Additionally, Wisej.NET supports responsive design principles that ensure applications look good and function on various devices and screen sizes. This is achieved through flexible layout managers and adaptive styling.
Wisej.NET integrates seamlessly into the .NET world and supports the use of .NET languages such as C# and VB.NET. This allows the reuse of existing .NET libraries and frameworks. Applications can be deployed both in the cloud and on-premises. Integration with Visual Studio facilitates development and debugging processes, while visual designers and property editors assist in the creation and customization of user interfaces.
Developing Line-of-Business Application for the Enterprise with Wisej.NET
Migration of WPF applications is possible with manageable effort. The often-used MVVM architectural pattern can be maintained. The view needs to be recreated using the graphical designer and UI components of Wisej.NET. The other application layers (ViewModel, Model) can be adopted without changes. Data bindings and commands are also supported by Wisej.NET components. This retains the application structure and reduces the effort compared to a redevelopment.
Additionally, Wisej.NET offers numerous other features such as built-in support for responsiveness and the integration of modern web technologies and standards. Wisej.NET provides extensive customization and extension options for user interfaces.
Julie Hirt is a software developer at Ice Tea Group specializing in the modernization of legacy desktop systems using Wisej.NET. The first programming language she learned was Python, so it holds a special place in her heart. In her spare time, she enjoys coding games in C#/Unity, which she features on her itch.io page.
Iulia Pitutiu is a Modernization Specialist and Project Leader at fecher GmbH.
Since 1996, I have been a project leader for web applications based on Microsoft architecture, developing various applications like portal engines and content management systems with an architecture similar to that of Microsoft Sharepoint Portal Server. In recognition of my expertise, I received the first Microsoft MVP award for Windows 2000 Server in 2002, which was reconfirmed in 2003 and 2004.
🔗
MVVM is a way of separating an application into three interconnected parts: the data storage (Model), user interface (View), and connection between the view and the model (ViewModel).
The Model represents the application's data. It provides an interface for storing, accessing and updating the data. The model exposes the data in the database. The ViewModel connects the data in the model to the view via databinding. The ViewModel is also responsible for setting up event handlers that trigger when items in the view are interacted with by the user. The View focuses on the user interface and displaying the data provided by the ViewModel. It does not directly interact with the database or handle data retrieval operations. Instead, it binds to properties exposed by the ViewModel to display the data and captures user input to pass it back to the ViewModel for further processing.
MVVM enables a clear separation of concerns which improves code maintainability. ViewModel components can be shared across multiple views, which promotes code reusability. Data binding automates UI updates when data changes, leading to a more responsive user interface. MVVM's testability allows for focused unit testing of the ViewModel, ensuring robust and reliable application behavior.
You can find the full code of the sample project here:
The MVVM pattern is a common architectural approach in WPF applications. It separates application logic (Model), the user interface (View), and binding logic (ViewModel)
Model represents the business logic and data of the application. It is independent of the user interface and does not know how the data is presented. View is the user interface and describes how the data is displayed. It mainly consists of XAML and defines the layout and visual elements of the application. It is bound to the ViewModel. Examples of view content are XAML files that define layouts and UI elements such as Buttons, Text Boxes, and Lists. The ViewModel acts as a link between the View and Model. It contains the binding logic and commands that enable interaction between the user interface and the model. The ViewModel presents the Model's data in a form suitable for binding to the View and responds to user interactions by executing commands. It implements the INotifyPropertyChanged
interface to send notifications of data changes to the View and contains commands to process user actions.
The goal is to migrate without fundamentally changing the application layers.
Thomas Althammer feels at home in two worlds: software development and data protection/information security. After having worked for 15 years as a software architect for healthcare applications, he is now particularly concerned with IT compliance and strategies for modernizing complex line-of-business applications.
🔗
Dino Esposito authored over 20 books and 1000 articles in a so far 25 years long career. It is commonly recognized that his books and articles helped the professional growth of thousands of .NET and ASP.NET developers and software architects worldwide. Dino started back in 1992 as a C developer and witnessed the debut of .NET, the rise and fall of Silverlight and the ups and downs of various architectural patterns.
🔗
Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them.
🔗
🔗
Jeremy is a Principal Program Manager for .NET Web Frameworks at Microsoft. Jeremy wrote his first program in 1982, was recognized in the "who's who in Quake" list for programming the first implementation of "Midnight Capture the Flag" in Quake C and has been developing enterprise applications for 25 years with a primary focus on web-based delivery of line of business applications.
🔗
Dino Esposito on hypes & realities around business applications, and his view on Wisej.NET as a game-changer in enterprise modernization.
We must first agree on what’s intended by business applications. My definition of a “business application” is: a critical software application that executes vital tasks for the everyday operations of an enterprise. Such applications are for the most part “legacy” in the sense that they are so critical that replacing them with fancy newer clones is a serious matter. So they stay there, year after year, run 24x7, and technologically speaking get older every day.
However, at some point there’s the unavoidable need to rewrite existing applications or build new ones to serve new needs. Nobody could realistically afford years of design and coding – not much for the costs but for the time to market. Today, business apps are almost all web applications writing to some database, calling into some API and exposing a few endpoints for others to call. They need a robust authorization layer, tens of forms, validation rules and quick CRUD code. In the folds of this, there might be room for some nice algorithms, coding of freaky business rules but the core is one: Web UI and CRUD.
The question boils down to “can current trends in web development satisfy the need of having quick-dirty-and-working business apps?” With all the hype on low-code, my answer is a sounding NO. A plain web monolith, ideally sliced into logical layers, is good as long as the team has a strict policy for coding - common architectural patterns, common writing style, shared library of tools, and plenty of ready-made private Nuget packages to reuse logic. It’s not about web trends and fancy libraries. It’s all about having powerful tools for the job and the people.
I currently work in sport-tech and our company developed and maintains software platforms for running everyday operations in worldwide professional tennis and padel tournaments. If something goes wrong about on-court operations, player bookings or scoring, it’s likely on us. Our systems are not rocket science and do not rely on an infinite workforce.
Our business applications are ASP.NET web applications with Azure SQL database underneath written as modular monoliths. Altogether, the topology of all our platforms makes for a rather intertwined graph of independent services. Each node, though, is a monolith according to the principles of layered clean architecture. No micro services, no server-less, no buses (if not for syncing purposes when needed): just a bit of cache and optimal SQL commands. It’s easy for everyone to pick up and all of our newbies feel home in days. By the way, we are 15 people and except me all of them joined with zero practical software experience. Only the youngest two come from a computer academic background.
Our goal is having a working set of technologies and a clear architectural pattern and stick to that. In this way, we teach how to do things right, letting developers figure out whether (and why) it was also the right thing.
When I started my career it was the season of Windows Forms that Microsoft turned into Web Forms for the web. That moved thousands of developers from the client/server space to the Windows and Web space. Following the sirens of frontenders we lost pragmatism and grew up a generation of technology purists. Technology serves us – not the other way around.
Wisej.NET takes the client/server old-but-terribly-effective programming style to the next (and modern) level. It really shields you from most of the weirdness of HTML and CSS and JavaScript (many may remember that was the same slogan of ASP.NET Web Forms 20+ years ago) and at the same it sits in between your frontend and your backend. Wisej.NET doesn't require you to touch your very valuable pieces of business logic and databases but puts on top of everything a modern user experience. Magically enough, this applies whether you are keen to build a new business app or renew/restyle an existing critical one.
I specifically question two points: blind fidelity in what others (big companies) have done and the religion of unit testing. It’s OK for Netflix or Amazon to have “invented” a highly fragmented architecture someone called microservices. But what worked for them (and was a matter of survival) may not necessarily work for you, especially if you’re a startup.
Start simple and grow with your business. When I say this, someone typically replies that such a crazy level of micro development helps reach a viable product sooner. If your startup has to do with software, it’s a clear issue to have unstable products. If it just needs software to support other types of business, it’s another business built on fragile foundations. Blind adoption of technologies is something I really condemn.
Nearly the same for unit tests. Not because you have code coverage of 100% you’re safe. The religion of unit tests is crazy. At the same time, it’s “mandatory” to think of software so that it can be testable. But design for testability is a completely different thing.
Overall simple question and a foregone answer. There’s no silver bullet and it’s just a way of rewriting from scratch: starting with an empty appropriate container to fill up with as many reusable pieces you can have or identify.
There are not many approaches to migrate a legacy app. And Wisej.NET does help significantly in doing so. It lets you quickly create an empty modern web container to fill up and link to isolated pieces of the legacy backend logic.
Isolating sore points – that is, wrapping relevant blocks of code everybody scares to touch into black boxes hidden behind publicly contracted interfaces. Once you have a clear list of connection points to readymade (safeguarded) business logic, you can easily build any UI and interaction paradigm on top of it. Including the next big thing of conversational programming.
We keep an eye on Wisej.NET as a platform for quick development of new applications. Our sport-tech company is not probably the ideal adopter of Wisej.NET because we have–in some way–our own internal platform that gives the same services. But we applied most of the same principles. On the other hand, I have recommended Wisej.NET to a client company needing to migrate to the web a huge Windows application managing operations in a large hospital and that was a blast. Smoother than in your sweetest dreams.
Thank you, Dino!
The interview was conducted by .
Dino Esposito
Microsoft MVP & Consultant
Gabriele del Giovine
Freelance Consultant, Software Systems Architect.
Jon Hilton
.NET Blogger & Developer
Dr. Veikko Krypczyk
Developer and Technical Author
Jeremy Likness
Program Manager for .NET Data at Microsoft
Iulia Pitutiu
Technical Lead at Fecher
Levie Rufenacht
Software Developer
at Ice Tea Group
Thomas Althammer
Managing Director Althammer & Kill
The transformation from desktop applications to web apps is gaining significance as companies increasingly prioritize broad accessibility and platform independence. Many desktop programs were developed using the Windows Presentation Foundation (WPF) graphics framework for the Windows operating system. This article outlines an efficient method for migrating such applications.
Many organizations face the challenge of transforming desktop applications into web applications. Web applications offer numerous advantages that make migration attractive:
Platform independence: Web applications run in the browser of the respective system, regardless of the operating system.
No local installation required: A central setup can be performed, instead of installing on each device.
Centralized maintenance: Updates and maintenance are carried out centrally on a server, often using cloud services.
Immediate multiuser capability: Web applications allow multiple users to work simultaneously.
Automatic updates: Web applications can be updated automatically without user intervention, significantly reducing maintenance efforts.
Better integration: Web applications can be more easily integrated with other web services and APIs, enhancing functionality and user experience.
Higher security: Through centralized management and standardized security protocols, web applications are often better protected against threats.
Scalability: Web applications can be more easily scaled to handle increasing user numbers or data volumes.
There are various approaches for changing the technological base of software:
Redevelopment: This process is often time-consuming, costly and brings high risks. The associated costs usually exceed those of migration projects significantly.
Using standard software: This approach makes sense if business processes can be covered with minimal adjustments by standard software.
Software migration: This approach refers to the migration from an old to a new technological environment. Migration is more than a simple upgrade and involves a fundamental change in the software infrastructure. Strategic planning is essential for successful migration. In the best case, tools are available to support the process.
The technological basis of web applications differs from that of a desktop application:
Client-Side (Front-End)
HTML
Defines the structure of the web page, including elements like texts, images, links, forms
CSS
Styling and layout of the web page, determining the appearance of HTML elements.
JavaScript
Adds interactivity and dynamic behavior, enabling DOM manipulation.
Frameworks and Libraries
Structured approaches to development, facilitating the implementation of complex user interfaces.
Server-Side (Back-End)
Server Technologies
Processes requests, executes business logic, interacts with databases, sends responses back to the client.
Server Scripting Languages
Write server-side logic that generates dynamic content and processes requests.
Databases
Store and manage data, enabling the ability to get, insert, update and delete data.
APIs
Facilitate communication between software components and services, exchanging data between client and server.
Network and Security
HTTP/HTTPS
Protocols for communication between client and server.
SSL/TLS
Secure data transmission over the internet by encrypting communication.
Cookies and Sessions
Store and track user data, enabling consistent and personalized user experience.
Deployment and Hosting
Web Server
Hosts web content, processes requests.
Cloud Services
Provide scalable and flexible solutions for hosting and managing web applications, offering automatic scaling, backups, and integrated security features.
A common model is Single Page Applications (SPA), which ensures high responsiveness of applications through an intelligent concept of data exchange and view updates. Planning and selecting the right migration approach are very important for project success.
MVC and MVVM are similar architectural approaches, and it can be confusing to understand the difference between the two. We will compare two Wisej.NET projects that achieve the same result- a simple DataGridView that displays information about students, and allows the user to add additional students. One project is written using a MVC approach and the other uses a MVVM approach.
You can view both projects on GitHub here: https://github.com/iceteagroup/wisej-architecture-examples
Let's get the similarities out of the way first- both MVC and MVVM have a View and a Model. In fact, you can use a very similar View and the same Model whether you are using MVC or MVVM! So the difference in MVC vs MVVM really comes down to the difference in the Controller vs the ViewModel.
In MVC, the controller is typically more tightly coupled to the view than the ViewModel is in MVVM and may not be as easily reusable across different views. In our example project, the controller is Page1.cs, and the view is Page1.designer.cs. So, in this case, they are extremely tightly coupled- they're actually part of the same class! Note that you could instead take a less tightly coupled approach, by creating a seperate Controller class. In that case, the Page1.cs part of the code would be known as the code-behind: the part of the code that connects the Controller to the view.
In MVVM, the ViewModel binds to the View. This ensures that data is synchronized between the database and the view as soon as it is changed. (Whereas in MVC, you have to manually call a function to synchronize the data.)
In order to understand how event handlers are set up in MVC vs MVVM, let's look at the simple example of creating a Button with a Click event.
We'll start by creating a Button object in the designer.
The designer-generated code is generated in Page1.Designer.cs. The code that involves creating the Button, as well as setting its location and text belongs in the view. Because our view is Page1.Designer.cs, this code is fine- it doesn't need to be moved or edited.
// Designer-generated code that belongs in the View (looks the same in MVC and MVVM)
this.button1 = new Wisej.Web.Button();
this.button1.Location = new System.Drawing.Point(740, 298);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(146, 37);
this.button1.TabIndex = 1;
this.button1.Text = "Add Student";
What about when we attach an event handler to the Button to handle the Click event? That's where we can really see the difference between the MVC and the MVVM approaches. Here's the code that the designer produces when we attach an event handler:
// Page1.Designer.cs
this.button1.Click += new System.EventHandler(this.button1_Click);
// Page1.cs
private void button1_Click(object sender, System.EventArgs e)
{
// code that runs when the button is clicked
}
In MVC, this code is fine as-is. Page1.Designer.cs is our View, and it's fine to set up the Click event handler in the view. And Page1.cs is either our controller or our code-behind. Either way, that's the correct place to put the button1_Click()
function. (Note that if Page1.cs is the code-behind, the contents of the button1_Click()
function should consist of calling a function in the controller.)
However, with MVVM, this code is in the wrong place! Both of these code snippets would need to be moved to the ViewModel. The event hander is set up in the constructor of the ViewModel, and the button1_Click()
function is defined as part of the ViewModel class.
// ViewModel (MVVM)
//Constructor
public ViewModel(Page frm)
{
//Add an event handler to the "Click" event of the "button1" control
button1.Click += button1_Click;
}
private void button1_Click(object sender, EventArgs e)
{
// code that runs when the button is clicked
}
So, as you can see, it's a little more intuitive to write MVC code (as compared to MVVM), especially when you are using the designer to edit the view. MVVM can certainly be implemented in Wisej; you just have to know which designer-generated lines of code you need to move to the ViewModel.
MVC is simpler than MVVM, which makes it easier to implement.
With Wisej.NET, the designer generates event handler code in Page1.cs, which makes it very natural to incorporate that code into the code-behind or into the controller.
MVC is harder to maintain than MVVM. Because the controller is so tightly coupled to the view, .
Databinding automatically synchronizes data between the View and the database.
Some designer-generated code (event handlers) will have to be moved to the ViewModel class.
by
In a previous article, we analyzed the general theme of migrating legacy Windows Forms applications to the web. A common practice entails wrapping up the backend of the application in an API layer and exposing it—altogether—as a service in a microservices architecture or a more traditional tiered (e.g., ASP.NET Core) application. In both cases, the sore point is the full redesign of the user interface with HTML5, CSS and JavaScript (or comprehensive frameworks such as Angular or React). Another nontrivial point to add up is that when you choose a modern frontend layer the mapping between UI connectors and legacy backend endpoints may not be as direct as it should be. The need of yet another intermediate query layer (e.g., GraphQL) emerges, adding costs and effort to the final bill.
In this context, Wisej.NET represents an alternate, and fairly unique, option.
At a first glance, Wisej.NET looks like yet another library of controls and components for allegedly rapid ASP.NET-based web development. True, but not exhaustive. Actually, Wisej.NET is a full platform tightly integrated with Visual Studio aimed at building and debugging ASP.NET Core applications. To qualify an otherwise conventional ASP.NET Core application as a Wisej.NET application, you just need to attach a custom piece of middleware in the startup.cs file.
app.UseWisej();
As Wisej.NET is ultimately also a library of tailormade components, you also need to reference a client-side framework that will drive any further interaction between the client and the server environment.
<script src="wisej.wx"></script>
Wisej.NET supports various types of web templates that ultimately result in prepackaged projects for a number of scenarios: plain web application but also web desktop application, web page application and user control library.
In which way is Wisej.NET different and relevant?
The programming model provided by Wisej.NET mirrors the desktop Windows application development model. Writing a native Wisej.NET application comes much smoother if members of the team have past experience in Windows Forms or WPF programming and are just looking to shift towards web application development. Wisej.NET simplifies numerous intricacies linked to conventional web development tools like HTML, CSS, and JavaScript. By design, the result of this effort of streamlining development is a programming model that closely resembles Windows Forms.
Even though Wisej.NET is a component-centric framework to craft modern and up-to-date web applications, it deliberately abstracts the reality of HTML, CSS and JavaScript coding to a higher-level programming model, more centered on a plain business-oriented action/reaction scheme rather than on deep knowledge of the implementation details of today’s mainstream front-end technologies.
A Wisej.NET application is based on a container named Form. The Form is a collection of visual elements each of which holds a physical position within the container and exposes a number of visual and nonvisual properties. The Visual Studio dedicated environment supplies a toolbox of components to create any necessary markup in a GUI-driven way. Any client/server interaction takes the form of an event triggered on a visual component that some handler will process.
All handlers are programmed in a companion class file that goes hand-in-hand with the Form container. This code-behind class has the same relevance of a controller class in a plain ASP.NET Core MVC application. Put another way, the set of all code-behind classes of a Wisej.NET application form the presentation layer of the new web application. From there, you can simply connect the public endpoints of your existing Windows Forms application backend or create a bunch of new layers (e.g., application, domain, infrastructure as in Domain-driven Design).
Wisej.NET can be used to build any classic or single-page web application but its primary use-case remains the migration of a Windows Forms legacy application to a modern, highly interactive and responsive web application.
At the beginning of the .NET development era, the vast majority of developers were coming from client/server development and Web Forms for the web, and Windows Forms for desktop, provided a unified programming model that made knowledge of web technologies little or no relevant at all. A lot changed since then, and the abstraction layer on top of raw web development became progressively thinner. UI frameworks such as Bootstrap made a good attempt of raising the abstraction again but all they achieved was graphical. New conceptual components born (e.g., navbar, input groups, dropdown menus) but still requiring explicit tons of HTML and CSS and completely devoid of behavior.
Wisej.NET brings the clock back to the time in which programming web applications shielded developers from having a significant grasp of web technologies. It’s a pleasant discovery for the newest generations of programmers and a healthy return to the origins for those still engaged in legacy Windows Forms applications.
Wisej.NET is a web development framework with the following unique characteristics.
Component-based
Single Page application
Abstracted frontend technologies
Real-time updates to UI
You assemble your Wisej.NET applications using various pre-built controls and components in a comfortable Visual Studio shell. This speeds up development and ensure consistency in design. The resulting Wisej.NET application typically follow a single-page application architecture, where only parts of the page are updated dynamically, leading to a smoother user experience. No in-depth know-how of front-end technologies such as HTML, CSS, and JavaScript is strictly required. This is advantageous if the team is more comfortable with back-end technologies and want to avoid deep involvement in front-end development. Finally, Wisej.NET provides real-time updates, allowing you to create interactive applications that can update in real time without requiring full page refreshes.
The last point is fairly interesting. The behavior of a typical Wisej.NET application, in fact, internally resembles closely the behavior of an ASP.NET Server Blazor application. (See Figure 1.) In a Blazor application, after an initial negotiation between the browser and the application backend, it’s a continuous exchange of web sockets packets transmitting request details and receiving DOM updates.
The similarity with the ASP.NET Server Blazor architecture ends at the layout level, as the exchange protocol, including format of data sent and received, is proprietary.
A Wisej.NET application is therefore an ASP.NET Core application. Why not using ASP.NET Core then? There are two main debatable points. ASP.NET has a large and active community, which means you can find plenty of resources, tutorials, and libraries to support your development. Wisej.NET is a proprietary product with excellent documentation and tutorials but nowhere near to the community of a general-purpose web platform made in Microsoft as it is ASP.NET. On a much more technical tone, it should be noted that ASP.NET allows you to have more direct control over the front-end technologies used in your application. This can be beneficial if you have specific design or functionality requirements but detrimental in the perspective of rapid application development. And quite problematic if you have a legacy Windows application to turn into a web application.
Wisej.NET runs on top of ASP.NET Core but provides its own programming model, much closer to ASP.NET Web Forms than to ASP.NET MVC. As a result, it’s about two fairly different programming platforms whose only point in common is the programming language whether C# or Visual Basic .NET. The way you design the presentation layer and connect a client event to a server-side handler is radically different and so it is for the startup of the application and a number of configuration aspects.
A Wisej.NET application blinks to the application internal organization of ASP.NET Web Forms and uses the ASP.NET Core hosting runtime just to inject its own middleware and gain access to the server file management.
It’s not a matter of voting what’s better and what’s worse: it’s two different things that do the same job of building modern web applications. Of these two platforms, you have to choose one. Which one, then? Not surprisingly, it depends on your needs and your skills.
The first decision point is whether you are looking for building a new application from scratch or migrating and existing ASP.NET Web Forms or Windows Forms application. In the latter case, Wisej.NET represents by far the primary option to evaluate. It may still not be ideal for you, but it’s definitely an option you might want to check. On personal experience, a large Windows Forms application with a few custom painting functions has been made deployable on the web in days by a small team of two people with no external help from consultants and only based on publicly available documentation.
What kind of people?
They were experienced and seasoned developers with a solid database background and a technical learning path rooted in the Web/Windows forms model. We were so happy that started considering the option of using Wisej.NET also for newer projects to build from scratch or evolve from a native ASP.NET Core foundation.
In doing so, though, we found out that for developers with no previous exposure to the Forms-based programing model of the early 2000s the Wisej.NET model looked weird at the minimum. Nothing that a brilliant developer can’t grasp in weeks but still a new learning path to go through.
In summary, choosing between Wisej.NET and ASP.NET depends on your specific needs and background. If you're more comfortable with desktop development and want a component-based approach with real-time updates, Wisej.NET might be a good fit. On the other hand, if you're looking for a more flexible and versatile framework with a strong community and integration with Microsoft technologies, ASP.NET could be the better choice. Always consider the current state of the technologies and your project requirements when making a decision.
by Gabriele del Giovine, translated by Julie Hirt
Wisej.NET is a set of tools, integrated into the Microsoft ASP.NET application architecture and into the Microsoft Visual Studio Visual Development Environment. Its main target is the development of Line of Business (LOB) Web applications at the Enterprise level. Wisej.NET applications are real time, highly dynamic and adhere to the Responsive Web Application (RWA) and Progressive Web Application (PWA) models.
The development environment is Microsoft Visual Studio for Windows with .NET Framework 4.8 and .NET Core 6+. Therefore the deployment includes operating systems such as Windows, Linux, MacOS, Raspberry Pi OS, for execution on the server side and Windows, Linux, MacOS, iOS and Android systems for execution on the web browser client side with the ability to interface with the client's hardware.
A project can be compiled simultaneously for multiple .NET targets, and conditional compilation criteria can be used to differentiate code based on the target.
Deploying applications in Docker containers is supported through the automated creation of a dockerfile.
From the point of view of the client, the applications work with all major browsers without requiring the installation of add-ons. The supported browsers are the following:
Microsoft IE 11
Microsoft Edge
FireFox
Google Chrome
Apple Safari
Opera
Wisej.NET has two main components that interact with each other. These components are the server part of Wisej.NET, consisting of .NET components, and the client part, consisting of JavaScript components executed within the browser.
The server part contains all the code that governs objects such as Application, Session, Authentication, Load Balancing, Application Logic, Data Access and interaction with all other systems related to the application. It is therefore partially similar to all the classic concepts that are at the basis of any web application, regardless of the development paradigm used. Where Wisej.NET diverges from some of the paradigms for the development of Web applications in vogue at the moment is the fact that Wisej.NET allows you to have all the control logic, the presentation logic and obviously the low-level logic for accessing data on the server side. The server is any server capable of executing .NET code. In Windows the server of choice is IIS.
The client part contains the UI components, page management components, and the communication components that control the communication between the page containing the UI and the server. This two-way and real-time communication uses various communication technologies depending on the capabilities of the client browser and any limits imposed by the security and traffic control systems. All traffic is http/https and native WebSocket (if supported by the browser and the server.)
It is important to note that Wisej.NET exchanges only the messages necessary for the management of the various UI components within the client browser. Therefore a Wisej.NET client that manages the display of data in a database table will never receive the underlying dataset or the endpoints of any REST/SOAP services that provide access to the data. However, it is always possible to inject JavaScript code into the HTML page at the page or single UI component level and interact with third-party HTML/ASP.NET/JavaScript components present in the page or in non-Wisej.NET pages present in the context of the application.
Another advanced functionality made available by Wisej.NET is the capability to invoke .NET functions present on the server side directly from a JavaScript client by decorating this function with the appropriate attribute. It also allows .NET code to invoke JavaScript functions on the client side.
In terms of scalability and availability, Wisej.NET, being an ASP.NET server-side application, inherits all the characteristics of .NET and the possibility of integrating any mechanisms developed for this purpose in .NET. Wisej.NET, however, offers the possibility to set some threshold values related to parameters such as maximum number of sessions, allocated memory, maximum CPU usage and response time. These values can be used to test the server's ability to allocate a new session based on the current workload before the server becomes overloaded. This unique feature allows a public load balancer to determine which node of the NLB cluster is best suited to take on the new session.
The development paradigm used by Wisej.NET is probably the feature that most differentiates this framework from the majority of traditional HTML/JS/CSS web frameworks based on templating engines (Blazor, Angular, React, ASP.NET MVC, PHP, JSP).
A Wisej.NET application is a Single Page Application (SPA) where all the UI elements are JavaScript components and all the logic is handled by the corresponding .NET components on the server side. It is, in a way, similar to Java’s Vaadin Framework and Eclipse Rapid Application Platform.
Wisej.NET applications are also real-time, where the server can push updates to the browser at any time, and are highly dynamic: applications can create and manage all components at runtime and don’t have to rely on rigid preconfigured HTML templates.
The main difference is that Wisej.NET, being a tool for developing LOB applications that by their nature must follow the PWA model, fills a gap common to many traditional template-based web development tools: the lack of an accurate design surface which allows for simple event management. Wisej.NET's solution is to adopt the same paradigm used in the development of Windows Forms, WPF or Xamarin applications by reusing the same UI design and coding methods: C# or VisualBasic for business logic and visual logic on the server, and JavaScript for the client side components.
The advantages of using this approach are many:
The application remains consistent in terms of language, code management, debugging and deployment, including the use of Docker containers and environments on Microsoft Azure.
The types of files are reduced to the essential minimum, greatly facilitating the management of the source code and the deployment.
The application can be installed on a variety of operating systems and hardware platforms without having to install special runtimes.
The architect is free to use the architectural model he deems most suitable.
It is possible to convert developers with a desktop development background in an extremely fast and productive way.
Does not require knowledge of JavaScript client development frameworks.
Does not require specific knowledge of HTML, CSS and other web page management languages.
It is possible to reuse most of the existing back-end code, including transactional data access, without having to create large sets of REST/SOAP endpoints.
It is possible to reuse almost totally and with very little effort existing WinForms applications including the forms themselves (in a Windows Forms project that does not use third-party components, the conversion work comes down to changing the System.Windows.Forms namespaces to Wisej.Web.)
Includes a large library of advanced controls and extensions. Many of the extensions are available in source format so that they can be adapted to specific needs. These extensions allow you to interact with the client's local hardware (Geolocation, Cameras, sound, local storage), cloud services (GoogleMaps, Amazon S3), or interact with third-party ASP.NET/JavaScript component libraries (SyncFusion, Infragistics, Kendo, DevExtreme).
While every day enchanting stories of groundbreaking and revolutionary frameworks dominate the tech headlines and clog our online feeds, substantial value persists in a forest of legacy applications constructed upon steadfast frameworks that stood the test of time. It’s line-of-business applications (LoB) that, as lame and ordinary as they may seem, take bread home and embody the cumulative wisdom and expertise garnered over years of conscientious development. A large number of these applications were built with the .NET Framework and continue running steadily on premise, both on aged desktop PCs and ultimate laptops.
The .NET Framework is now 20 years old--the same as 3 geological eras along the software scale of time. Interestingly, .NET came out at a very favorable crossroads of times. In the late 1990s, mainframe apps were old enough to justify a rewrite and the breakthrough of Internet made it appealing and plausible also to the eyes of those holding the purse’s strings. Many organizations embraced .NET and Windows Forms to keep on coding desktop applications in a modernized, client/server style. At the beginning of the millennium, in fact, Web applications were still largely perceived as freaky things to look down skeptically.
Today, it’s another business world and probably another crossroads of times. Furthermore, another breakthrough—generative artificial intelligence—looms on the horizon. It’s another time in which facing the costs of rewriting legacy applications looks overall plausible also to purse’s holders.
What is most effective way to upgrade line-of-business .NET applications?
Line-of-business .NET applications are typically Web Forms and Windows Forms applications; in some cases, even former Windows Forms applications hastily (when not sloppily) converted to Windows Presentation Foundation (WPF). None of these technologies is being further developed; upgrading, therefore, means changing the technology stack.
Companies that lately embarked on such migration projects ended up with fairly convoluted architectures mainly centered on:
Mainstream frontend technologies like Angular and React to give users the thrill of a modern user experience
Some GraphQL query layer to pick up data from a variety of heterogeneous resolvers and mutators working on top of the existing—legacy—code and underlying databases
The business logic gets wrapped up in GraphQL-friendly APIs and parts of the legacy application that can’t be easily modularized survive in Docker containers and communicate in some way with the rest of the world. It works, but more often than not the final artifact is much more expensive and slower than the original.
Migrating, anyway, is a tough decision on the management side.
A big rewrite is always a fascinating option that always excite developers, but always sounds risky to management. How to blame them? Allocating substantial funds without a definite assurance of success, as competitors surge ahead, lacks any appeal. At the same time, remaining inert is equally untenable: the symptoms that brought to at least consider a rewrite won’t just naturally resolve over time.
In a nutshell, keeping LoB applications in shape over time, and in sync with the needs of the business, is an unavoidable problem. In my everyday CTO work in charge of application development, I make a point of never building applications larger than a critical size and keeping them never behind a relatively recent version of the framework of choice.
Easier said than done, though.
Now, suppose you have an old-fashioned-but-happily-working Windows Forms application—perhaps written in Visual Basic .NET. In your dream book, you may have penciled the following goals:
Nice and attractive new web user interface (Windows Forms is a desktop application)
The same proven business components for the parts of the application that need no changes
New business components to code with newer technologies
Sounds like just a wishful dream? Dream or not, it’s just what many organizations deal with.
If you’re looking for a sort of one-size-fits-all kind of approach for migrating legacy applications, then the approach outlined above is the only plausible option, with some bitter-sweet aspects left to everybody for contingent evaluation of the impact.
Frontend heavily based on JavaScript and subsequent perennial lock-in in the framework of choice
Introduction of an additional gateway layer (and related complexity) to orchestrate communication with the legacy components
Within the gateway layer, another layer to encapsulate the legacy components and expose their core functions as API endpoints
Essentially, you end up with an architecture like below.
Refactored, the original monolithic Windows Forms application looks now much more multifaceted and complex. It is an entirely new application founded on the same beating heart, with additional features and significantly higher runtime costs (on top of the development costs paid upfront to go live).
Anything else? You bet!
First and foremost, the logic of legacy business components is unaltered. If you need to modify anything there, sadly all the architectural effort you put in won’t be of much help. Changing the legacy code remains the same problem of touching potentially fragile code: finding developers, testing for regression, compiling old technologies, deploying on physical machines.
Second, what if new functions need some transactional behavior across distinct microservices? Worse yet, what if transactions span over legacy components?
Generally speaking, distributed transactions are intricate and failure-prone operations due to the numerous components involved. Committing such transactions across different machines (when not data centers) inevitably leads to long-running processes moreover not completely reliable. So, the optimal guideline is not to resort at all to distributed transactions. With a good design and due forethought, dividing the new functions into microservices could potentially eliminate the need for inter-service transactions. Yet some more complex scenarios remain and may require maintaining consistency across new and legacy business processes. To achieve that, you can add even more complexity via 2-phase commit protocols and distributed transaction mangers or accept the system would be eventually consistent at some point in the future.
Eventual consistency, though, is the exact opposite of what users experienced before with the legacy application. In summary, the canonical approach leads to a lot of costs to rewrite and run plus a not necessarily optimal user experience made of eventual consistency and heavy client workloads.
Turning a legacy application into a microservice architecture is a high-impact rewrite approach that tends to sandbox the legacy core and integrate it with a completely new application. A legacy Windows Forms application is made of:
A user interface layer made of forms
A presentation layer made full of event handlers in code-behind classes
The two layers are tightly coupled by design and must be severed in order to migrate the application to the web. Whatever type of application rewrite you may plan, it is necessary to move code-behind classes into controllers bound to some API endpoints. In the .NET stack, the most obvious step is embedding the business code of the legacy application into ASP.NET Core controllers. Your legacy code can be recompiled for a newer .NET runtime or for a compatible .NET Standard runtime.
Once you can trigger parts of the original business logic from within a browser client, the user interface can be based on any JavaScript-intensive frontend framework but also a more traditional server-side rendering engine such ASP.NET Razor views or pages. You can even go with Blazor—version 6 or the newest version coming with .NET Core 8.
Any Windows Forms (or WPF) legacy desktop application has a user interface and a presentation layer incompatible with web technologies. It’s certainly also a matter of visual styles and component functionality but, albeit relevant, that’s not even the sorest point. For one thing, Windows and Web frontends use a different runtime host and different threading models. A Windows Forms application will likely use customizable blocking message and dialog boxes that are not natively available within any browser. More, it may be using drag-and-drop and specific graphic painting of the screen. Not to mention the programming model that is comparable to ASP.NET Web Forms but radically different in ASP.NET Core MVC.
In a nutshell, to migrate a Windows Forms application to the web you need to severe the presentation from the rest and wrap the rest in a web API layer and then completely rewrite the presentation with the web frontend technology of choice and face any possible structural rendering limitation.
So, is migrating the frontend of a Windows Forms application to a full HTML-based web frontend a real mission-impossible task? Apparently, it is. Unless you take a look at Wisej.NET.
Wisej.NET exists since 2015 but, personally, I never heard of it till recently when porting a large Windows Forms application to the web became crucial and urgent. We looked around, examined best and worst practices, read about public case-studies and came just to the conclusions outlined so far in the article. We could have left the backend nearly intact but rewrite from scratch the presentation layer. It looked like a long-but-doable thing. We then started building a new ASP.NET Core application—our bread and butter—only to find out that linking new user interface events and existing handlers was not so direct. Many adjustments to the former code-behind handlers proved necessary and we only looked into the simplest use-cases. We were seriously concerned when we heard of Wisej.
Now, in nearly three decades of career that started with COM and Visual Basic I counted quite a few attempts of building frameworks and libraries to migrate from Visual Basic to Visual Basic .NET and from Windows to web. All the attempts I was aware of, though, went in the direction of automatically rewriting the existing code using a new programming model closer to the target platform.
In this regard, Wisej.NET is different as it just provides the “same” Windows Forms programming model over the web. It definitely replaces and rewrites the user interface but with two enormous benefits:
No need to switch the mindset of Windows Forms developers and no need to learn different best practices
No need to touch the backend, namely the code-behind classes
The slogan “From Windows Forms to Web in just a few minutes” is clearly emphatic and eye-catching. It takes minutes only for demos good for videos or conference talks. But it takes a reasonable amount of time to port large projects vital for an organization; an amount that is a fraction of the time it would take to turn the solution in an uncertain microservice architecture. And when I say “port”, I just mean releasing to production a new ASP.NET Core, Blazor-like application that is 100% equivalent to the legacy code and open to further extensions.
In this article we look at the four aspects of typical SCADA applications that make them quite complex to implement as web applications using HTML-based frameworks.
Today’s production sites are all connected and entirely digital. Integrated control centers connect to industrial devices on the shop floor using serial interfaces, bus systems, as well as wired and wireless networks. In many cases, SCADA plays a key role in conjunction with OPC servers in Microsoft Windows-based environments. This article explores web-enabling and web migration options for SCADA systems using the Wisej framework based on .NET.
SCADA stands for Supervisory Control and Data Acquisition. It is used to describe applications that monitor and control external devices. Usually, it refers to software systems with a UI that are connected to manufacturing machines, sensors, or any kind of controller. These systems can be as simple as turning a pump on or off and as complicated as monitoring or programming an entire manufacturing plant.
Traditionally SCADA systems have always been built using C/C++ with either a text UI or graphical UI, and more recently using C# WinForms/WPF (or Java Swing or AWT). In most cases, the hardware where the application runs is directly connected to the controllers, allowing the software to communicate with the devices in real time. Which means that the vast majority of SCADA systems are desktop applications, or fat clients of sorts. Even distributed or networked SCADA systems are still not web applications. Why is that?
Let’s look at the four aspects of typical SCADA applications that make them quite complex to implement as web applications using HTML-based frameworks:
Monitoring
Controlling
UI complexity
Local Hardware connectivity
When evaluating HTML-based framework for building SCADA applications, industrial-grade requirements have to be met. These frameworks require developers to build the UI by typing HTML text, to manipulate the UI by concatenating HTML strings or building HTML elements, and process the user input through posts. This usually includes, to a different degree, frameworks and technologies like ASP.NET, PHP, JSP, Blazor, Angular, etc.
A typical SCADA monitoring application displays a dashboard that is updated when it receives an input from the local controllers the hardware is connected to. The update can be detected in two ways: push and pull.
In the push model, the controller driver fires an event or calls a registered callback when the input changes. In the pull model, the application queries the controller at regular intervals. For a local application this is quite straightforward: the callback triggers a UI update with the new values, while the polling can run at a short interval and update the UI when the values change.
Doing the same for a web application adds a new set of problems: Now we have the push or pull model for the controllers connected to the web server (server-side), and we have to also manage a push or pull model for the browser(s) update (client-side).
For the server-side push model, the controller is connected to the web server and the callback triggers code running on the web server, while the UI is in a web browser on a different machine and potentially on multiple clients at the same time. The only way for the web server to tell the browsers that something changed is to use a WebSocket connection, which is fine, but you need to code it, wire it and manage it. You can use SignalR or the WebSocket directly, but it’s still something you need to add to your web application and wire it, build some kind of communication protocol, etc.
Once you solve the server-side push model and you receive the update data pushed to the client browser in a JavaScript method, you have to update the UI in the browser. But if your system is HTML-based, you need to update the DOM in JavaScript and/or you have to fire an Ajax request and receive the updated fragment. It’s all up to you. Try it with something as simple as a dashboard with only 6 sliders, 6 spinners, 6 gauges, and a liquid container as shown in the following screenshot:
Applications migrated or modernized using the Wisej framework can benefit from a layer of abstraction by using just the C# language to build complex reusable components that render as JavaScript widgets and are wired to the server.
In order to realize the monitoring application in the screenshot, JavaScript is not needed at all. Wisej builds the client side JavaScript widgets pre-wired with a server side component.
While monitoring is the process of receiving data from the connected devices and displaying it in real time, controlling is the opposite: send updates to the devices in response to user actions as quickly and reliably as possible.
Traditional HTML based systems require a page submit to send all the data back to the server and make it quite difficult to wire pointer, touch, keyboard events in real time and to send the updated data to the server. Typically you need to attach to all sorts of native browser events, pack the event data, invoke a remote API, deal with state, session, etc. All on your own and always reinventing the wheel from scratch.
Wisej controls (and any third party widget integrated in Wisej) automatically send any update to the server using small, fast, WebSocket or HTTP packets without waiting for the user to submit anything. As a result, a Wisej SCADA application behaves very close to a local desktop application even when the browser is thousands of miles away.
Things aren’t usually as simple as shown in the above emulation applications. Typical user interfaces for control centers connect a large number of monitor views. For example, Wisej is being used at a tire production plant to manage the production floor. In this setup, up to 80,000 parameters are fed into the application and shown in a complex real-time Web view:
At German EME GmbH, Wisej is being used to manage glass production machines and recipe management. Amtech’s Encore, the leading ERP for the packaging industry, is powering packaging and corrugator production sites around the world with computer-to-machine communications based on SCADA. Wisej is the technical base framework for the entire solution, consisting of hundreds of modules and views.
With Wisej you can build complex, composite and reusable .NET components that fire server-side events, update the browser, and receive browser updates easily using the Visual Studio designer, and reuse it as many times as you need to.
With Wisej you can also draw or paint custom controls from the server directly into the browser. The liquid container in the previous screen is a C# component that is painted on the server side using standard paint and graphics calls and updated on the browser in real time.
In fact it’s this one: https://www.codeproject.com/Tips/989219/Liquid-Container-Control. A WinForms control that has become an HTML5 JavaScript widget based on Wisej without changing a single line of code.
private void LiquidContainerControl_Paint(object sender, PaintEventArgs e)
{
CreateRegion(0);
PaintBackground(e);
Paintimage(e);
PaintText(e);
}
Another option for real time drawing of complex controls is to use the Canvas component and use the full Canvas HTML5 syntax in C# or VB.NET and see it displayed on the client browser in real time.
Wisej applications can also become fully local thanks to our self-hosting platform. Using the "single source - multiple targets" approach, Wisej applications can be deployed to a number of different platforms:
Web servers (public and private)
Cloud services (AWS, Azure, …)
Embedded servers (self-hosting)
Desktop (self-hosting with Web view)
Android and iOS (using Wisej Mobile)
A single Wisej app can run as a local desktop application, and access local resources otherwise unavailable through the browser, using one of our 4 frames: Chromium, Edge, IE, and Firefox. The following screenshot illustrates the same application shown above running as a local desktop application using the same source code and just a different deployment method:
Wisej has been used by a number of SCADA-based applications around the world across different industries: from managing glass-production, tire manufacturing, to packaging and corrugator plants. Its embedded real-time protocol makes it an ideal choice for web-based applications of any complexity.
To learn more about Wisej and how it is being used in connection with SCADA and OPC setups, please review the following case studies:
For further information please contact the Wisej Sales team: [email protected]
by Jon Hilton
If you're targeting native devices like Android or iOS, then Wisej.NET's Hybrid apps can help you build your app quickly (using the Wisej.NET ecosystem) and tap into device specific functionality when you need it.
Hybrid apps are native apps which run on your target device's hardware (for example, that old Android phone have lying around!)
With Wisej.NET's Hybrid projects you can use WiseJ.NET to build your application largely as you would if you were targeting desktop or the web.
Plus you can interact with Device APIs, such as biometrics, the status bar, flashlight etc.
But how does it work?
Let's explore the high level architecture of hybrid apps, and the Wisej.NET projects you'll need to get up and running.
The first thing you'll need is the application that will actually run on the native device.
This is the one that will be compiled to a platform specific executable for your target device(s).
To create this, look for the project template called Wisej.NET Hybrid Client Application.
When you create a hybrid client application project you'll be asked which platforms you want to target, and how you want to run your Hybrid app (App Mode).
Specifically you'll be asked whether you want to run your Hybrid app remotely or locally.
Which begs the question, what exactly is Local or Remote when we're talking about Hybrid apps?
To answer that, we need to understand how the Hybrid client app actually works.
The client app has two primary jobs:
Display your app (via a WebView component)
Enable interactions with the native device (via a JavaScript bridge)
Whether you opt for a local or remote Wisej.NET app the hybrid client itself will always run on the device.
It will be compiled to executables that will run on the device(s) you're targeting.
There it uses a WebView to render your Wisej.NET application and handle interactions (such as when the user selects items on the screen, or enters text).
The client project is of limited use by itself. It needs another project which it can render via the WebView component.
This is where you get to choose whether you want that separate project to be Local or Remote.
But what's the difference between the two options?
A local project, also known as an Offline project, will be deployed alongside your native client app.
It will run on the native device itself (via an embedded web server).
This offers more options for interacting with the native device APIs (more on that shortly) but also means you have to redeploy your native app every time you make changes.
Choosing Local/Offline requires you to push new versions out via the relevant app store(s) for your target platform(s) whenever you wish to deploy a new version.
A remote project is a Wisej.NET project which can run anywhere on the Internet (wherever you choose to deploy it).
You can use any Wisej.NET web application for this, or the dedicated Wisej.NET Hybrid Remote Application template.
In the config for the Hybrid Client App you can specify the URL for the remote site:
builder
.UseMauiApp<App>()
.RegisterAppServices()
.UseWisejHybrid((config) =>
{
// Provide the startup URL for the Hybrid WebView.
config.StartupUrl = "http://localhost:5000";
});
The Hybrid Client App will then render the remote site via a WebView control.
The big benefit here is you can easily deploy updates to the web server and all devices which are pointing to that hosted web page will automatically use the latest version.
This saves paying the "deployment tax" of getting the app through the release cycle for the various app platforms, and makes it faster/easier to deploy your app.
It does however mean your app is dependent on having a live connection to the Internet. If you need offline capability Local/Offline apps are a better bet.
With either of the hosting options your users can view and interact with the app (whether it's running locally, or hosted via a web server).
But this alone would be pretty limiting if you couldn't also write code in your application to interact with the device.
For example, you might want to change the colour or text in the status bar, or read data from the device's sensors.
For that, your app, running via the WebView, needs a mechanism to reach 'outside' of the WebView, to make calls to the device.
This is where the JavaScript bridge comes in.
You can make calls to the Device
singleton in your Wisej.NET application, to interact with the various device APIs.
Those interactions are handled via the JavaScript Bridge (part of the WebView, running on the device) which makes the underlying calls to the relevant device API.
Device.Flashlight.TurnOn();
Here, for example, we're able to switch on the device's flashlight.
We don't need to know the specifics of how to make this work via Android, or iOS, as that's handled by the Device
abstraction.
When we make the call to Device.Flashlight
the instruction to switch it on will be sent to the device, from the WebView via the JavaScript bridge.
So far everything we've explored works for both local/offline and remote projects.
But if you opt for a local/offline project you can interact with the device via .NET MAUI as well.
First you need to enable MAUI in the .csproj file for your Local app.
<PropertyGroup>
<UseMaui>true</UseMaui>
</PropertyGroup>
With that in place you can now make calls to Microsoft.Maui
.
#if ANDROID || IOS
Microsoft.Maui.Devices.Vibration.Default.Vibrate(TimeSpan.FromSeconds(1));
#endif
Here we make the device vibrate for one second using .NET MAUI's API.
Incidentally, this also shows how to target specific platforms in your code. In this case this code will only execute for Android or iOS devices.
Wisej.NET hybrid opens the door to building native apps.
When you create a hybrid app you need to choose whether to deploy your app to the native device, or host it externally.
From there you can write code to interact with the device via the Device
singleton and/or, in the case of Local applications, Microsoft MAUI.
To continue your hybrid journey check out the step by step guide below:
Wisej.NET allows nearly automated migration of WinForms applications. However, migrating WPF applications requires a bit more effort. Wisej.NET replicates the standard UI controls of WinForms almost 1:1 as JavaScript controls. Properties, behavior, and events are also identical. That is why the migration effort is significantly reduced. For WPF applications the UI is declaratively created using XAML code. It must be recreated for migration to a Web application. An appropriate alternative in the form of a Wisej.NET control must be identified for each UI control from the WPF application. User interface creation can be facilitated by using the graphical designer in Visual Studio.
Wisej.NET offers a data binding option for each UI element. This feature is used to maintain the architecture based on the MVVM concept. The implementation of data binding is shifted from the XAML code (View) to the C# code (View) of the web app. The other layers of the application (ViewModel, Model) can be adopted without changes. Table 2 lists the steps for migrating a WPF application to a Wisej.NET-based web application.
Table 2: Adaptation steps for migrating a WPF application to a web application.
Analysis of existing UI
Identification of the XAML controls used and their properties.
Mapping of UI Controls
Assignment of WPF controls to Wisej.NET controls. Layout and UI design can be done using the graphical designer and layout containers.
Adjustment of Data Binding
Shifting data binding from XAML code to C# code. Commands from the ViewModel are bound in the C# code of the code-behind file.
Adoption of Model Layer
Unchanged adoption of existing Model files.
Implementation of new View
Creating the user interface in Wisej.NET using the Visual Studio designer.
The starting point is a WPF application (Figure 2).
Let's analyze the typical properties of this WPF application:
Binding UI Elements (View) via DataContext property to the ViewModel, for example:
<DataGrid ItemsSource="{Binding Path=Persons, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"…/>
Model provides Data Classes, for example, the Person
class:
public class Person {
public int Id { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
}
ViewModel binds data to UI Elements of the View:
public partial class PersonViewModel : ObservableObject
{
private ObservableCollection<Person> persons;
public RelayCommand AddItem { get; set; }
public ObservableCollection<Person> Persons {
get { return persons; }
set {
persons = value;
OnPropertyChanged("Persons");
}
}
public PersonViewModel() {
AddItem = new RelayCommand(AddItemCommandExecute);
...
}
private void AddItemCommandExecute() {
Person p = new Person() { Id = persons.Count, FirstName = "Ben", Name = "Müller" };
Persons.Add(p);
}
}
The ViewModel implements the INotifyPropertyChanged
interface.
This synchronizes data between the View and the ViewModel automatically. The ObservableCollection data type is used for the data list (Persons
), which automatically forwards changes to the View.
User Actions (Button Click) are forwarded from the View to the ViewModel via Commands:
<Button
... Command="{Binding Path=AddItem}"/>
The web app was created using Wisej.NET. The ViewModel (PersonViewModel
) and Model (Person
) classes were adopted unchanged. The user interface was built with Wisej.NET UI controls, using the graphical designer of Visual Studio (Figure 3).
Modern UI controls such as DataGrid were used. These UI controls are JavaScript controls that were adapted via CSS to modern themes such as Bootstrap or Material Design.
Wisej.NET offers layout containers that enable responsive design of the web app. This ensures correct display on different screen sizes and resolutions.
Data binding occurs in the code-behind files. Here, the properties of the UI elements are bound to the properties of the ViewModel. For example, the binding for the collection property looks like this:
dataGridView1.DataSource = viewModel.Persons;
The DataSource
property of the DataGrid control is bound to the Persons
property (data type: ObservableCollection<Person>
`) in the ViewModel. This synchronizes data between the ViewModel and the View (DataGrid).
In the ViewModel, the commands for user interactions were modified. In the WPF version of the application, there was, for example, the following typical source code for a command that adds an object of the Person class to the list of persons (Persons
):
// Define command
public RelayCommand AddItem { get; set; }
// Initialize command in the class constructor
public PersonViewModel() {
AddItem = new RelayCommand(AddItemCommandExecute);
...
}
// Execute method of the command
private void AddItemCommandExecute() {
Person p = new Person() { Id = persons.Count, FirstName = "Ben", Name = "Müller" };
Persons.Add(p);
}
The RelayCommand
class, for example, comes from an MVVM framework or is defined in a custom base class. This base class provides the functionality for a command. This source code needs to be adapted for the web version:
// Define command
public Command AddItemCommand { get; set; }
// Initialize command and assign execution method
AddItemCommand = new Command(
execute: args => {
Person p = new Person() { Id = persons.Count, FirstName = "Ben", Name = "Miller" };
Persons.Add(p);
}
);
The new base class Command
comes from the Wisej.NET framework. The command from the WPF application only needs to be adjusted concerning the data type. No further changes are necessary.
With these adjustments, a WPF desktop application can be technically migrated to a modern web application. The application's functions remain preserved.
In addition to the mentioned measures, there are other important adjustments that need to be considered when migrating a WPF application to a web-based application. These include, for example:
Adjustments and extensions of the application's functionality: For example, in the business logic.
Elimination of errors adopted through automatic migration.
Changes to access local hardware.
Adjustments due to switching from single to multiuser operation.
Implementation of responsive design principles: The layout and UI components dynamically adapt to the screen size and orientation.
Implementation of security mechanisms: For example, measures like authentication and authorization.
These tasks are examples; Specific implementation needs will vary based on the project.
This article shows how a simple offline ERP application can be built with Wisej.NET Hybrid.
Wisej.NET Hybrid is a product from Ice Tea Group that allows developers to build both offline (local) and online (remote) Wisej.NET applications for Android, iOS, Windows, and MacCatalyst. These applications can be designed using the WYSIWYG designer in Visual Studio.
To get started, we'll create a new project in Visual Studio using the Wisej.NET Hybrid Client Application template.
The Wisej.NET Hybrid Client Application is the client application that allows developers to build and deploy applications to Android and iOS targets. The project is similar to the default .NET MAUI app template but specifically configured for use in Wisej.NET projects.
For this demo, we'll call this new project "ERP.App":
After creating the Client application, we should have a view like this that shows the configuration of the Hybrid application in Startup.cs:
Next, we'll go ahead and add a new project to the existing solution:
This time we are going to add a Wisej.NET Hybrid Offline Application. This template is used to create Wisej.NET applications that do not require a connection to an external web server. Alternatively, developers can use the Wisej.NET 3 Hybrid Web Page Application template to create "online" applications that do require a web server.
Since we are creating a disconnected application for this example, we will name it "ERP.Offline":
After creating the ERP.Offline project, let's connect it to the ERP.App project. Right-click ERP.App and select Add -> Project Reference. Select the ERP.Offline project:
Next we can connect it to launch when we start up the ERP.App project by uncommenting the .UseWisejOffline<>
line and referencing the offline project:
After creating the project, we'll see the Program class that defines the entrypoint for the offline application:
Pages are great when creating applications that need to run on both desktop and mobile, but for this demo we are only going to target mobile (Android and iOS) so a Form might be more intuitive.
In this case, we're going to delete the Page1 view and replace it with a new Login Form (Window):
To set the default theme of the project, go to the Offline.json file and set the theme property:
After creating the new item we have our "blank" window template in the Visual Studio designer:
On the left-hand side of the Visual Studio window you will see the Toolbox which contains a number of components available for use in the designer like buttons, textboxes, grid, etc.
I'm going to drag-and-drop the controls from the Toolbox onto the designer to create a simple login form:
I added a FlexLayoutPanel to the view and docked it to fill. The FlexLayoutPanel control can automatically layout child controls with a vertical or horizontal orientation.
Next, we can go ahead and double-click the "Sign In" button create a C# event handler in the code-behind file.
At this point, we should go ahead and connect our data source, like a SQLite database, to validate against. In this demo we're going to skip this part to continue on to other topics.
To connect the Login view to display when the application starts up for the first time, update the Program.Main method:
The next step of this sample application is to create a portal that shows a list of areas we can interact with such as users and documents.
We'll go ahead and add another new Empty Window to the project to visualize this.
For the portal, I added two controls from the Toolbox: a Label and a FlowLayoutPanel. The Label is for the title and the FlowLayoutPanel is to display our actions for this user.
Going back to the Sign-In button let's connect the two views now:
Next, let's create a control for each action item now using a Wisej.NET UserControl. Right-click the project, select Add -> New Item -> Wisej.NET 3 -> UserControl.
After designing the view, I should have something like the following:
The view includes two controls: a Label for the title and a PictureBox for an icon.
I also modified the CssStyle property of the UserControl to show a nice shadow:
We can create a new constructor in the code-behind file that allows us to intialize the label and picturebox properties of the control based on the actions we create later:
To wrap up the first part of this article, let's add some actions to the portal so we can see how the layout will work:
Now let's go ahead and run the application with an Android Emulator:
The startup page looks pretty good - except that the status bar is white with gray text. Let's go ahead and fix that in OfflineProgram:
The Device singleton is the gateway into interacting with native device functionalities such as the status bar, native tab bars, biometric authentication, and more.
In this case we're setting the color of the status bar according to our theme.
After running the application again, the login view should look like this:
and if we click "Sign In" we should see some sample actions:
In the next part of this blog, we'll go into more detail on adding and configuring actions within the ERP application.
border-radius: 8px;
box-shadow: rgba(0,0,0,0.31) 5px 5px 10px 0px;
by
This document describes how you can benefit from using Azure files in your .NET web application.
During a web-enabling process, one of our standard tasks that we analyze with our customers revolves around file management. Our experience has proven that desktop applications heavily use file manipulation, therefore we need to provide modern options to support this feature within web-enabled applications.
Wisej.NET Framework is very flexible and extensible when it comes to file handling. It provides an abstract interface for file access enabling customers to use any file system that their web applications need: either the built-in default implementation for the standard disk-based file system or several alternative implementations for cloud-based file storage.
Nowadays, it is attractive to deploy web applications to an environment like Microsoft Azure. Therefore, it makes sense to use an Azure file integration as a supplement or total replacement for traditional on-premise file servers.
In this article, I am describing the way a Wisej.NET web application can interact with cloud files stored on Microsoft Azure Blob Storage. To connect your web application to Azure Blob Storage, there are a couple of easy steps to follow.
Azure subscription
Azure storage account
Install Azure.Storage.Blobs
12.10 library in your solution:
Install System.Runtime.CompilerServices.Unsafe
version 4.6.0:
The Compiler Services will require a binding redirect in the Web.config file:
Install System.Buffers
version 4.5.1:
The System.Buffers
will require a binding redirect in the Web.config file:
The file system implementation for the Azure Blob storage needs to be placed in a class that implements the Wisej.NET IFileSystemProvider
, I named my new class AzureBlobProvider
:
The authentication to the Blob storage can be done in many ways like the indicates – the actual authentication is not in the current scope of this article, but rather the file access part is more relevant. Independent of the authentication type, for the actual Azure Blob storage connection, we need to know the account and container name – which is stored in our AzureBlobProvider
class members:
Scenario 1: Import Operation
For a standard import operation, the end user should be able to select the file path from the Azure storage. To achieve the file import, I have used a Wisej.NET Open File Dialog where I added both a local folder and an Azure Blob storage container just to show case that both file systems can be used together:
If we run the sample, the user is prompted with a file picker dialog – as shown, the Azure Blob Storage folder structure is displayed:
This is possible because the Wisej.NET Open File Dialog calls GetDirectories
and GetFiles
methods from our AzureBlobProvider
class:
For displaying the file properties, the AzureBlobProvider
class implements methods like GetFileSize
or GetCreationTime
.
After the user selects the imported file, the web application may require processing the file content. On the server side, this operation means downloading the Azure Blob item and reading its content.
The implementation for downloading and reading a Blob item’s content is here:
Scenario 2: Export Operation
Another common scenario is when the web application is generating a report which should be uploaded to either a pre-defined or a user-defined Azure location. To simulate the export, in my sample, you can type in the file content and then click the Upload button:
After clicking the Upload button, the Save File Dialog allows you to specify the location and the name of your file:
The result will be uploaded in Azure Storage:
Wisej.NET is a very powerful web application framework which enables taking advantage of the latest .NET technologies.
For further examples on how to implement cloud storage file access, I recommend the Amazon S3 file system implementation provided by ITG, which was also my support for developing the Azure Storage file system provider.
Upgrade Windows Forms apps to cross-platform web and mobile applications.
by
Windows Forms (WinForms) is a widely used application development framework for building desktop applications on Windows. The framework powers mission-critical applications across the globe spanning every industry imaginable. The framework, which was open-sourced on December 4, 2018, is maintained by an active community of developers with over 230+ contributors and daily check-ins.
While the framework is actively maintained, a number of problems may exist in companies that cause them to consider moving away from WinForms. These could be due to both internal and external pressures such as demand from customers, management, or “future-proofing” the technology stack to align with industry trends.
Thanks to remote work, customers and employees demand that applications be accessible at any time, from anywhere in the world, and on any platform. Using a remote desktop may provide a quick solution to provide teams with access to internal applications, but doesn’t provide a solution to the underlying issue that is the modernization of the application itself.
For some companies, the user experience might be a core focus that drives them towards application modernization on the web. It’s becoming increasingly common that customers access applications via a web interface instead of traditional methods such as an executable download. They expect the application to look and “feel” like a modern web application.
When customers analyze a company’s product over its competitors, all functionality being equal, it makes sense for the customer to choose the web-based solution over the company providing a desktop solution.
A common but simple thought by management might be “how do we stay competitive?” and the solution usually involves an analysis of industry trends and how they can be applied to existing software products. Oftentimes this can require a rewrite of the existing product in an entirely new technology that is unknown or involves a significant learning curve for developers at the company.
Desktop applications often require manual installation and updates which can lead to errors or delays in accessing new features or security patches. Additionally, supporting multiple versions of a desktop application can strain resources, as developers need to allocate time and effort to maintain compatibility and provide technical support for older versions. These factors can contribute to a higher cost and effort associated with maintaining and updating desktop applications
Wisej.NET is an enterprise-scale web development framework that caters to the needs of existing WinForms developers. Wisej.NET allows these developers to be productive on day one by removing the need to learn JavaScript, HTML, and CSS and instead use only C# or VB.NET.
Each one of the hundreds of controls available in Wisej.NET contains two components: a server-side C# object and a client-side JavaScript object. Both components are always synchronized meaning that changing the text of an input element on the client’s browser will automatically update the Text property of the corresponding TextBox .NET object. The two sides communicate using a single, secured, and session-specific API endpoint that utilizes JSON and WebSocket.
Wisej.NET comes with a pixel-perfect designer for Visual Studio installed via the Visual Studio Marketplace. Similar to Windows Forms, a set of controls exist in the toolbox which can be dragged, dropped, and modified on the visual designer. Every control added to the Wisej.NET designer is a pure HTML5 web component as an Input, Button, or iFrame element.
Wisej.NET adopts a development paradigm that's similar to WinForms, providing a responsive event-driven programming model that is familiar to many .NET developers. This approach allows developers to create highly interactive web applications using the .NET framework, leveraging the event-driven style of WinForms.
Wisej.NET can accommodate modern architectural patterns such as Model-View-Controller (MVC) and Model-View-ViewModel (MVVM). In the case of MVC, Wisej.NET can handle the controller's actions, where business logic is processed, and efficiently manage the views for presenting data. As for the MVVM pattern, Wisej.NET can be used to bind ViewModel properties to the user interface, enabling changes in the model to automatically update the view and vice versa.
In an effort to simplify migrations from WinForms to web, Wisej.NET integrates all of the WinForms controls as web controls under a new namespace. For example, System.Windows.Forms.Button becomes Wisej.Web.Button.
Wisej.NET’s API includes classes such as TextBox, Button, ComboBox, DataGridView, and many others that can be configured, styled, and added into layout objects instances of classes such as FlowLayoutPanel, FlexLayoutPanel, TableLayoutPanel, and others.
The following example shows the creation of a Wisej.NET Form:
The following is a screenshot of the previous example:
The same development paradigm that is shown above can be applied to mobile development with Wisej.NET Hybrid. Hybrid applications are web applications that can interact with native device features such as biometric authentication, native document scanning, utilization of native UI components such as status bar colors, the native tab bar, and more.
Wisej.NET Hybrid is built on top of .NET MAUI and can leverage any MAUI feature and more.
WinForms is a great framework for creating powerful desktop applications, but in the case that the application needs to be modernized for the web, Wisej.NET provides a great solution. Wisej.NET’s visual designer and development paradigm make it easy for WinForms developers to use their existing knowledge to build or migrate enterprise-scale applications for the web. Wisej.NET supports any architecture and includes a set of 100+ web controls.
Wisej.NET is available for free to independent developers.
For enterprise inquiries reach out to .
by
Wisej.NET brings a familiar desktop development experience to the web without losing any of its benefits.
This article covers: a case study that explores the challenges with line-of-business applications, migrating enterprise-scale applications to the web with Wisej.NET, comparing Wisej.NET to WinForms, and component library integrations for Syncfusion, DevExpress, and Infragistics.
Moving Windows Forms, WPF, VB6 or other legacy applications to the web can be hard and expensive. The migration roadblocks can be endless when evaluating current web technologies. If you’re the new guy tasked with rewriting the entire application in Angular or another web technology... good luck!
Wisej.NET was built with this in mind. By taking the convenience of the WinForms object model and integrating many advanced JavaScript components, Wisej.NET brings a familiar desktop development experience to the web without losing any of its benefits. Applications that originally could only run on Windows become available on every HTML5-compliant device in existence.
Developers considering migrating from Gupta Team Developer, VB6, and WinForms will have the option to keep their existing business logic, gain a greatly modernized UI, and the same workflow. Wisej.NET and Ice Tea Group offer automated migration solutions for Gupta Team Developer, Visual Basic 6, and WinForms applications to a fully modernized web-based platform. Semi-automated migration paths are also available for Microsoft C++, Windows Presentation Foundation (WPF), Silverlight and Visual FoxPro.
Developers considering full-stack development tools for their projects will have the opportunity to leverage the scalability, flexibility, and security that Wisej.NET has to offer.
Today’s world of enterprise-scale business applications usually shows a feature-rich history of evolvement and enhancements, making the transition from Windows desktop to native web a difficult, risky, and sometimes unrealistic task. What are typical challenges and how does it work?
Let’s take a brief look at an example from the public sector:
Harris Local Government (part of the Constellation Software Group) decided to modernize 150 separate Windows modules developed in Visual Basic 6 to an integrated web-based platform. Harris serves more than 5,500 small and mid-sized municipalities with a wide variety of solutions such as vehicle registration, real estate, personal property, and utility billing. The software suite includes financial modules such as accounting, fixed assets, and payroll.
Figure 1 provides a screenshot from an existing desktop user interface built with VB6:
Developing a complex enterprise-scale application from scratch is usually not an option that works. Here are typical obstacles to take into consideration:
Time, effort, risk and cost of a manual rewrite are in many cases beyond consideration. An application with hundreds or thousands of forms and dialogs may take dozens of man/years to rewrite and test from scratch.
There is a huge risk when doing a manual rewrite without the ability to take over business logic and code from the legacy application. Usually, algorithms and logic have been developed over decades and can deal with an infinite number of data input and output scenarios. The effort around quality assurance and testing for 100% backwards compatibility is enormous.
The user base has been trained and accustomed to the existing application. Workflows, screens, and manuals should get a nice brush up and a fresh design, but the general functionality cannot be changed entirely, especially for ISVs with end-customer installations.
Another limiting factor is R&D. Most companies cannot afford to maintain an existing application and build a new replacement over many years. The development team would have to be doubled. Programmers are hard to find and the experience and knowledge of “silver coders” is extremely valuable for software vendors.
Thanks to Wisej.NET and the consulting team of fecher (one of Ice Tea Group’s preferred implementation partners), 150 separate modules from Harris Local Government got turned into a great looking modern web application suite based on Microsoft .NET with C# and Wisej. Figure 2 shows the same window as before, now part of a modern UI with a new Navigation Bar and Tabbed MDI interface that was built to consolidate separate applications into one unified solution.
The modernization was completed in four phases:
Visual Basic 6 projects got converted into C# projects.
All projects got consolidated in a Visual Studio solution and rebased to use Wisej.NET components.
New navigation controls such as the sidebar on the left and the tab bar on the top have been built to support 150 different modules in the new integrated application suite.
Based on the mockups of a UI/UX designer, the Wisej.NET Theme Builder was used to provide a brand-new look and feel to the Harris Local Government application.
Wisej.NET is a Single Page Application (SPA) framework based on .NET for C# and VB.NET. It emphasizes the best of both worlds, desktop and web development, by taking controls that have a familiar look and feel to the web - modernized and enhanced. Imagine rewriting 1,000 screens (with 100,000 controls), 500 reports, 800 stored procedures, from scratch with the current web technologies and the resulting maze of templates, HTML, JS, and overlapping CSS, not to mention the naïve way of interacting with the server that may work for a real-estate web site but is utterly inadequate for the type of Line of Business (LOB) applications we see daily. With Wisej.NET, it’s possible to migrate your existing business logic without any risky changes to the code base.
The best part of using Wisej.NET? You won’t have to touch HTML, JavaScript, or CSS (if you don’t want to). Wisej.NET has wrapped a full .NET control set spanning from simple buttons, combo boxes, and text boxes to more complex controls like data grids, data repeaters, ribbons or navigation bars. It’s as easy as picking a control from the Visual Studio Toolbox and dropping it on the page.
The development environment of WinForms and Wisej.NET have a similar look and feel. With Wisej.NET, you can drag and drop controls from the toolbox, double click to add event handlers, and wire properties the same as you would in WinForms.
With Wisej.NET there’s no more worrying about integrating stylesheets in your application. The styling is done using the Wisej.NET Theme Builder and loaded into your project automatically as a single JSON file. The Theme Builder allows you to customize the look and feel of all Wisej controls. You can use one of the 10 predesigned themes (see ), create your own custom theme, or just modify any base theme using smaller theme mixin files. Ice Tea Group’s professional services team is happy to help and provide suggestions for redesigning existing applications.
Once you choose your theme, you can test it in real-time using the URL of any active Wisej.NET application. If the Theme Builder doesn’t have the styling functionality you need, every control in Wisej.NET has a CssStyle attribute that lets you configure optional CSS for that control.
While no JavaScript programming is required, it can be useful for integrating third-party widgets. Wisej.NET includes a large set of free and premium extensions including Google Maps, FullCalendar, and speech recognition. If a new control is needed, the Wisej.NET Widget control is able to wrap any third-party JavaScript control and turn it into a server-wired designable component.
For a full list of included extensions, visit:
Starting with version 2.2, Wisej.NET will include a Progress Web Application integration (PWA) module, and a set of premium packages for native iOS and Android applications. Developers will be able to create Wisej.NET applications that control any aspect of the device from the server, and don’t need to know any HTML, CSS, JavaScript, Java, or Swift to use the mobile packages. It can all be done from C# or VB.NET. The packages will also include responsive templates, making responsive design a breeze.
The Wisej.NET Mobile Integration package gives web developers the power to design complex business applications for mobile without touching the native code, and leverage all the native functionality of the device, including but not limited to biometric authentication, haptic feedback, screen brightness, native alerts, native toolbars, tabbars and more!
Handling device events on the server is easy. This is how you can add handlers for selecting tab bar items, toolbar items, subscribe to notifications, and just about any other event that occurs on the device.
Using Wisej.NET’s responsive profiles and the pixel-perfect WYSIWYG designer, developers can manipulate form sizes, control appearances, text to display, and more to fit every client’s screen.
Wisej.NET offers self-hosting through an OWIN / Katana web server that runs as a process or service on the machine.
For developers that want to retain their application for the desktop, Wisej.NET can run as a desktop application in either Chromium, Firefox or light 500KB IE-based app. The window’s frame is fully customizable and doesn’t require an internet connection to operate. The application can leverage some native Windows functionality like the MessageBox or the file system. The full source code is provided to allow developers to work with authentication, logging, and directory browsing.
Wisej.NET has integrated several third-party component libraries. These JavaScript widgets now act as familiar server-side .NET controls, eliminating the need for developers to use JavaScript, HTML, CSS, or Ajax requests to work with them. Using these widgets in Wisej.NET is as easy as using any WinForms control. For example, changing a widget’s background color:
Current desktop developers will be able to adjust to the Wisej.NET framework with relative ease, resulting in reduced training costs and increased productivity. They’ll find that they’re able to develop productive software to take to market very quickly. Additional training and support are available for developers that want to gain a deeper understanding of the Wisej.NET framework.
Companies all around the world have used Wisej.NET to modernize their applications, from small to extremely large. Wisej.NET is being used in many industries including agricultural, medical, construction, manufacturing, financial, and many more. From individual development teams to multi-billion-dollar corporations, Wisej.NET excels in giving developers the tools they need to get the job done. Find out what others are saying about Wisej.NET:
For consultation inquiries contact us at .
Translated article first published in the German iX Magazine June 2025
by
The Wisej.NET framework makes it easy for .NET developers to migrate proven desktop applications to the web or to develop new applications. Version 4 brings modern language features, more flexible markup and better portability.
When companies want to optimize their software platforms, they often want to transfer established desktop programs to the web or develop new applications using the skills and tools already available in their team. This is where the Wisej.NET framework comes in, which can be used to create web applications with C# or VB.NET in Visual Studio. It combines the .NET development environment with the advantages of modern web technology. Unlike traditional web frameworks, which often require extensive knowledge of HTML, CSS and JavaScript, Wisej.NET allows you to create web applications with a graphical designer and an extensive library of UI components directly in Visual Studio. The process is comparable to the development of desktop applications with Windows Forms, WPF and similar technologies.
Single-page web applications created with Wisej.NET synchronize continuously between client and server by exchanging small differential Ajax/JSON packets via a WebSocket connection (an HTTP fallback is possible). Wisej.NET enables a flat learning curve for .NET developers, as it is based on familiar concepts and tools. It also integrates third-party components and freely available JavaScript controls. It is possible to create responsive web applications that run on different devices and operating systems. The manufacturer - the US-based Ice Tea Group - has now released version 4 of the framework.
Wisej.NET is suitable both for the migration of existing desktop applications and for the development of modern web apps. Many companies have been using Windows Forms or WPF applications for years, which often contain complex business logic. In such cases, a complete redevelopment for the web is cost-intensive, risky and often unnecessary. Migration has advantages:
A large part of the existing C# or VB.NET code can be adopted, as Wisej.NET is based on the .NET ecosystem.
Developers can continue to work with familiar concepts such as events, controls, layouts and the designer.
With the available controls, layout containers and the designer, classic desktop UIs can be quickly converted into modern web interfaces.
Thanks to the HTML5 rendering engine, apps can easily be made responsive - a prerequisite for use on mobile devices or in different browser environments.
In addition to migration, Wisej.NET is also suitable for the complete development of complex business applications, especially if teams come from the .NET environment. The advantages here are:
With Wisej.NET, single page applications (SPA) are created with high performance through updates based on WebSockets, which are equipped for complex requirements.
The integrated graphical designer allows an interactive design in which application interfaces can be created for the browser in a similar way to WPF or Windows Forms.
Developers can use JavaScript interoperability to integrate external JavaScript libraries or write their own web components.
Wisej.NET runs on the server in a NET application. Therefore, security standards, authentication and deployment are simplified.
Wisej.NET has a modular architecture that combines classic .NET paradigms with the possibilities of modern web development. Developers remain in the world of Visual Studio and C# (or VB.NET), but work with technologies that use current web standards such as HTML5, CSS3 and JavaScript under the hood. Wisej.NET is based on a server-client model that executes the application on the server under .NET, while the user interface is displayed in the browser with JavaScript components. It communicates in real time via WebSockets. The architecture has various key points (Fig. 1):
Server-side logic: The business logic remains on the server; no additional APIs or services need to be implemented to separate UI and logic.
Client-side rendering: The user interface is rendered as an HTML5 application.
The application is displayed in the browser with a JavaScript-based runtime environment.
Unlike classic web frameworks, the UI model in Wisej.NET is stateful, similar to desktop apps. This means less effort when managing states and navigation.
Instead of complete page reloads, Wisej.NET only sends changes (deltaupdates) to the client, resulting in high performance and minimal latency.
Wisej.NET contains a component-based UI system with many predefined controls such as buttons, text fields, combo boxes, menus, data grids and other UI controls (Fig. 2). The UI is defined either via the Visual Studio Designer or declaratively via Fluent Markup. Wisej.NET follows a component-oriented approach reminiscent of WinForms, but with modern features such as themes, responsive layout, dynamic styling and data binding. The following features are particularly relevant when developing business products: Design time support with a live preview of the web application, data binding for various objects and data sources in NET, theming with complete control over the visual appearance and JavaScript interoperability for the integration of external web libraries and UI components (such as D3.js and Chart.js).
Wisej.NET runs in the .NET ecosystem and works with both the classic .NET Framework as well as .NET Core. This proves to be particularly helpful in migration projects, as older desktop applications are usually based on the .NET Framework and have not yet been converted to .NET Core. Older applications based on the .NET framework can be migrated to a web application immediately and an update to .NET Core can be carried out later.
.NET developers can access all common libraries, including Entity Framework (EF Core) for database connection. It is possible to host Wisej.NET applications on a web server (Windows, Linux), use Docker, deploy them via the cloud (e.g. Azure App Service) and package them via Wisej.NET Hybrid into a cross-platform application that can be used offline with a local web server in order to address devices without network access.
Wisej.NET 4 optimizes existing functions and also brings new features. It takes into account the .NET Core Designer, contains a cross-platform library for managed graphics (System.Drawing.Managed), Fluent Markup for UI design and implements Markdown for text-based UI controls. Another new feature is the complete integration of the graphic designer for .NET Core projects in Visual Studio 2022 (from version 17.6). Previously, .NET Core was fully supported; however, in order to use the visual designer, a reference to the .NET Framework was still required. The development team has now removed this dependency and forms and user controls can now be edited visually with the new .NET Core-based Designer (Figure 3). Wisej.NET 4.0 continues to work with both .NET Framework and .NET Core.
Previously, Wisej.NET relied on GDI+ and System.Drawing to draw custom interfaces, which could be problematic in .NET Core environments - especially under Linux and macOS. With version 4, Wisej.NET introduces Managed Graphics, a fully managed rendering system based on the universal graphics library Image Sharp. The advantages: platform-independent drawing under Windows, Linux, macOS and Docker, compatible with current .NET versions and high resolution display (High-DPI) including scaling and smoothing. Existing drawing code can be ported with minimal adaptations. GDI+ can still be used for existing projects.
With Fluent Markup, user interfaces can be defined by code. C# code is used to declare the UI. Developers are familiar with this approach from .NET MAUI. Fluent markup code is useful for creating dynamic user interfaces at runtime, for example to generate forms based on data structures. The syntax is more compact and easier to read than the object-oriented notation (see above).
Fluent Markup can also help to create UI templates for reusable modules. Version 4 natively supports the rendering of Markdown and extended HTML in controls with a text label. This function is useful for document views or report components, for example.
The new version 4 comes with numerous detailed improvements that make the framework more developer friendly and modern. For example, the development team has implemented the ControlRendered
and ControlUpdated
events for all control elements. This allows you to customize the
rendering behavior and react to changes in the browser without having to create your own subclasses. All C# project templates have also been revised: They now use modern .NET language features
such as file-scoped namespaces and toplevel statements, which makes the code clearer and leaner. An upgrade tool is available for the migration from Wisej.NET 3.x to 4.0. Existing .NET Framework 4.8 and VB.NET projects can still be maintained.
Wisej.NET offers a flexible licensing model that includes both developer and server licenses (detailed information can be found at ). In addition to the free Community Edition, the Professional and Enterprise Editions are available for developers. The Community Edition is suitable for individual developers, students, teachers and small companies with an annual turnover of less than 1 million US dollars and up to five developers. The Professional Edition (1,190 US dollars) offers extended functions and is designed for professional development teams. The Enterprise Edition (USD 1,690) includes all the functions of the Professional Edition as well as additional premium features and support services.
For the operation of Wisej.NET applications on servers, separate licenses are required, which differ according to the number of CPU cores and the recommended number of concurrent users (Recommended Clients). The free Express license is designed for one core and up to one hundred users. The standard license (USD 690) is valid for two cores and up to 250 users. For larger use cases, there is the Advanced License (USD 2,490, four cores and up to one thousand users) and Data Center Licenses (USD 7,990 to 28,990).
There are several tools and frameworks that compete with Wisej.NET in individual areas - depending on the application scenario, technology preference and project requirements.
Blazor creates .NET web apps with C# instead of JavaScript; different rendering models are available: WebAssembly, static server and server. Blazor is also available in .NET and has a large community. One disadvantage compared to Wisej. NET is that there is no visual designer, more complex state handling is required and direct (automatic) migration of Windows Forms and WPF applications is not possible.
In JavaScript and TypeScript frameworks with .NET integration, Angular and React can be used for the front end and ASP.NET Core for the back end. Here, the existing .NET code can only be reused in approaches for the business logic on the server. The browser-based clients must be created from scratch based on the selected technology.
The Mobilize.NET WebMAP tool for C# and Angular migrates .NET desktop applications (WinForms, WPF) into web apps. WebMAP analyzes the existing C# code and extracts business logic and UI. It automatically transforms them into a web architecture with Angular frontend and ASP.NET core backend. Despite the automation, the approach also has its limitations: the original UI is not adopted one-to-one. Mobilize.NET WebMAP converts the business logic automatically, but the UI is new in Angular to design and customize. In addition, there is no visual designer available. The frontend is designed in Angular code or in HTML and CSS, which requires additional knowledge.
Wisej.NET therefore has a special position for the software modernization of WinForms and WPF applications; in particular, the graphical designer with an event and component-based approach makes the transfer of existing code much easier.
Wisej.NET 4 can be used to create web-based business applications for .NET Framework and .NET Core. It is designed for developers who want to transfer existing WinForms and WPF applications to a modern web environment without having to change fundamental paradigms. With server-side logic, a component-based UI model and the visual designer, the development approach remains familiar - with simultaneous access to modern web functions such as WebSocket, responsive layout and integration of third-party JavaScript controls and components. The new version brings important technical innovations such as Managed Graphics, Fluent Markup and NET Core-compatible designer support.
With Wisej.NET Hybrid, there are additional options for use beyond the web browser. Existing applications can be deployed across platforms as native apps for Windows, macOS, iOS and Android - including access to local system resources. The integration takes place with .NET MAUI and a web-based Wisej.NET app is thus hosted in a native .NET MAUI container.
Note: This text is an unauthorized translation from the iX Magazine June 2025. The original text in German is available for download here:
// Dims the device’s screen to half of its possible brightness
Device.Brightness = 50;
// Uses the device’s built in biometrics or passcode to authenticate user
var authenticated = Device.Authenticate("Reason for authenticating");
// somewhere in your code
Device.TabBar.Selected += TabBar_Selected;
…
private void TabBar_Selected(object sender, DeviceTabBar.SelectedEventArgs e)
{
AlertBox.Show($"{e.Button.Text} was clicked!");
}
// this is updated automatically on the client.
this.ejCircularGauge1.Options.backgroundColor = "white";
// handling any event from the widget on the server.
this.ejCircularGauge1.Widget.legendItemClick += this. ejCircularGauge1_LegendItemClick;
var textBox = new TextBox
{
LabelText = "Name:",
Location = new Point(20, 20),
Size = new Size(150, 24)
};
var binding = textBox.DataBindings.Add("Text", dataSource, "FirstName");
binding.Format += (s, e) => {
e.Value = e.Value.ToString().ToUpper();
};
var button = new Button
{
Text = "Click me",
Location = new Point(20, 50),
Size = new Size(80, 32),
};
button.Click += (s, e) => {
// Do something on click.
};
page.Controls.Add(button);
page.Controls.Add(textBox);
using Wisej.Web.Markup;
page.Controls(
new TextBox()
.LabelText("Name:")
.Location(20, 20)
.Size(154, 24)
.Bind("Text", dataSource, "FirstName",
format: v => v.Value.ToString().ToUpper()),
new Button()
.Text("Click me")
.Location(20, 50)
.Size(80, 32)
.OnClick(b =>
{
// Do something on click.
}
));
Reusability of existing .NET code
visual designer for web UIs
Fast migration of WinForms and WPF applications
Integration of JavaScript and third-party UI components
case studies about successful projects from different industries
limited community compared to mainstream web frameworks
Dependence on the provider for long-term further development
WPF migration with XAML-based UI code currently without designer support
Software migration harbors the risk of inheriting deficiencies in the architecture
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Wisej.Core;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using System.Threading.Tasks;
using System.Text;
using System.IO;
using System.Configuration;
using System.ComponentModel;
using System.Drawing;
using System.Text.RegularExpressions;
using Azure;
namespace AzureBlobStorageSample
{
/// <summary>
/// Implementation of the Azure Blob Storage provide.
/// Provides access to the Azure Blog Storage as a file system.
/// </summary>
public class AzureBlobProvider : IFileSystemProvider
{
/// <summary>
/// </summary>
public string AccountName
{
get { return this.accountName; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentNullException("accountName");
this.accountName = value.Trim();
}
}
private string accountName;
/// <summary>
/// Returns or sets the name of the Azure Blob Container
/// </summary>
internal string BlobContainer
{
get { return this.blobContainer; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentNullException("blobContainer");
this.blobContainer = value.Trim();
}
}
private strong blobContainer;
private void btOpenFile_Click(object sender, EventArgs e)
{
var fileContent = string.Empty;
var filePath = string.Empty;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Title = "Select file to import";
openFileDialog.Roots.Add(new FileSstemProvider("\\network shared path\subpath01\... ", "My Local Files"));
openFileDialog.Roots.Add(this.AzureBlobProvider);
openFileDialog.Filter = "Text Files (*.txt)|*.txt|CSV Files (*.csv)|*.csv|XML Files (*.xml)|*.xml";
openFileDialog.FilterIndex = 3;
openFileDialog.AddExtension = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
txtImportFilePath.Text = openFileDialog.FileName;
}
}
}
/// <summary>
/// A visible Window in the browser.
/// </summary>
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
}
// event handler for Form / Window initialization.
private void Window1_Load(object sender, EventArgs e)
{
// create a new TextBox with a label.
var textBox = new TextBox { LabelText = "Enter your email" };
// create a new Button with some text.
var button = new Button { Text = "Sign up" };
// create a "success" label.
var label = new Label();
// attach to the button's click event.
button.Click += (se, ev) =>
{
label.Text = $"Welcome: {textBox.Text}";
};
// add the controls to the view.
this.flexLayoutPanel.Controls.AddRange(new Control[] { textBox, button, label });
}
}
/// <summary>
/// A visible Window in the browser.
/// </summary>
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
}
// event handler for Form / Window initialization.
private void Window1_Load(object sender, EventArgs e)
{
// change the status bar color.
Device.StatusBar.BackColor = ColorTranslator.FromHtml("#353A40");
// change the bottom bar color.
Device.BottomBar.BackColor = ColorTranslator.FromHtml("#353A40");
}
private void buttonLogin_Click(object sender, EventArgs e)
{
var username = this.textBoxUsername.Text;
var password = this.textBoxPassword.Text;
LoginUser(username, password);
}
private void LoginUser(string username, string password)
{
// insert login logic.
Device.Popups.DisplayAlert("Welcome", $"Welcome back, {username}");
}
}
Wisej.NET is a powerful platform that abstracts away the nuances of client/server interactions while embracing all the web has to offer, including third-party HTML5-based solutions, it is the perfect tool for building enterprise web applications.
This article covers: Obtaining the software, and does a demo walkthrough that looks at instantiating controls, real-time updates, third-party integration, data-binding at scale, embedded Media, and custom painting. Then we briefly look at features and integrations like: Pixel-Perfect WYSIWIG Designer, Theme Builder Control Library Integrations, and Native Mobile Integration.
Wisej.NET provides a platform and set of tools designed to ease the burden of developing, testing, deploying, and maintaining complex business software. It bridges the gap between traditional WinForms-style desktop applications and the web by empowering developers to leverage their existing .NET and C# skills.
Developers are able to use the tools they are familiar with, like a pixel-perfect design surface and drag-and-drop controls with the languages they are comfortable with (C# or VB.NET) to build real-time web applications without having to learn HTML, CSS, or JavaScript. They can stay in their favorite IDE like Visual Studio to take advantage of features like IntelliSense, integrated debugging, design surfaces and source control integration. Front-end developers can leverage their knowledge of web technologies to augment applications and seamlessly integrate third-party packages like HTML5 charting libraries and ASP.NET controls.
To bridge the gap between legacy desktop applications and modern web applications, Wisej.NET provides several features that create a straightforward path to migrate existing applications or build green field projects without having to learn a new set of technologies. Support for touch and gestures is built into the software with emulation to support the desktop experience, localization is a first-class citizen, and the applications Wisej.NET produces are out-of-the-box responsive. That means they are readily accessible from desktop browsers, mobile smartphones and tablets.
To get started, visit the Wisej.NET site:
There you can browse the various pricing options and download a trial.
The sample application is available at:
http://demo.wisej.com/codeproject
You can download the source code at:
Download the latest build of Wisej.NET from https://wisej.com to get started. You should be able to compile and debug the application. The application uses a 64-bit SQLite driver. There are two options to ensure that you are able to successfully debug. You can either run as Administrator and use the full IIS server locally, or you can set IIS Express to run in 64-bit mode as shown in the following dialog.
One advantage of Wisej.NET is that it can generate local executables to run the same application you access from your web browser as a standalone or self-hosted application. In the folder for the project, you will find a "Wisej.Application.exe" that you can execute to test this functionality.
The first thing you’ll notice when you access the site is the desktop experience. The code to create the desktop experience is as simple as adding a desktop control to the application. Wisej.NET automatically handles allowing users to drag components, scale, resizing, minimizing, etc. Tap on the grid of squares in the lower left to expand a "start menu" of options.
In Visual Studio, navigate to the "Popups" folder and double-click "StartPopup." This will open the designer experience for the pop-up menu. You can see the various buttons, click once to view properties and double-click on them to view the code-behind. Launching another component is simple. Double-click on the "Background Task" button in the designer. You will see the following code that simply creates an instance of the "BackgroundTask" control and then invokes it.
The first example we’ll review is the "Background Tasks." Traditionally, building a real-time pipeline between a web browser client and server host involves scaffolding technologies like WebSockets or SignalR and building logic on both sides to handle dataflow and exceptions. Run the Background Tasks example press "Start" to see it running:
The same dialog is available in the design-time view through Visual Studio:
Note the list of controls on the left side that are ready for drag-and-drop positioning on the canvas with the same experience that WebForms developers are used to. The controls to maximize, tile, and close the dialog are all built-in and require no additional code to work. Right-click on the control to view the code-behind.
The important code is in the "button1_click
" event.
The code iterates through 100 items and pauses for one half second each loop. The browser app still runs asynchronously and is not blocked by the operation. The Wisej.NET platform provides the "StartTask
" method to initiate a background task, and refreshing data real-time is as simple as calling the "Application.Update
" method! All of the real-time connections between the browser and server are handled automatically, so the developer can focus on what’s important (the core code logic) and not worry about the nuances of how the client and server connect. Wisej.NET support for real-time communications handles Websockets, HTTP, or Websockets with HTTP fallback, all selectable via a simple configuration setting.
Next, click on "Widget Integration" in the demo app. You will be presented with an editing control:
The control allows you to edit a document and use advanced features like formatting, inserting links and emojis, even embedding images. The control actually uses the open source HTML project CKEditor. This example illustrates how easy it is to integrate third-party open source libraries, even when they are not .NET-based projects.
Grids are often the centerpiece of enterprise applications and building interactive grids that provide basic Create/Read/Update/Delete (CRUD) capability and advanced features like filtering, grouping, and sorting can be difficult in web-based applications. Wisej.NET features a data-binding system for controls that is both intuitive and easy to implement. To see the results in action, choose the "Data-binding" example in the demo app. You should see a grid like this:
The grid is full-featured. Tap on a column header to sort, and tap again to toggle the order between ascending and descending. Tap on any row to edit in the associated dialog, or double-tap on the grid to edit inline ("Excel-style"). You can insert new rows and tweak your updates until they are ready to be saved to the server. The save features a dismissible alert notification that indicates success or a modal dialog to diagnose failure.
Open the grid in Visual Studio and right-click to view the code-behind. This is an incredibly powerful feature of Wisej.NET that many web developers likely wish they had in the past. The grid is fully implemented through C# server-side code, and Wisej.NET automatically handles all of the binding, updates, and dataflows without the developer having to write any client JavaScript code!
Loading the initial data set is as simple as filling a standard data adapter. The demo app uses a SQLite database to store and update sample data:
The code to save a record provides a success alert and shows a modal error dialog when an issue is encountered, again all done with straightforward C# code that automatically controls the browser experience.
It is important to note how easy it is to provide client modals with server-side code. This is often a challenge in other frameworks due to the logic necessary to handle the asynchronous dialog and ensure that the modal successfully disables other content in the app. Notice the previous code example provides a straightforward, sequential, server-side C# workflow but successfully provides an interactive modal experience in the browser.
In more advanced scenarios, you may have to write code to page and skip records in the database and filter based on advanced criteria. Wisej.NET simplifies this process by allowing you to focus on one set of code. If you can make the code work on the server, Wisej.NET will connect it to the browser and display the appropriate output for you. No more writing two sets of code, one for the server and one to handle all of the events in the browser!
Click on the "Media" option from the start menu to view the media experience. The dialog box includes embedded video with position tracking and an embedded PDF document that you can view and navigate without leaving the current webpage.
You can open the media control in Visual Studio and click on the individual components to see their properties. Wisej.NET has built-in "Video" and "PDFViewer" controls that can simply be dragged onto the design surface to use and are bound to their related documents. The properties control shows how the PDFViewer control is bound to a document that is included with the project:
The PDF viewer control is flexible and offers support for pdf.js, native PDF documents, Google Docs and custom viewers.
For the position tracking in the media element, a simple event is wired in to trigger when the time position changes and handled with the following code:
The code is handled just like a desktop app and all of the updates to the browser are coordinated automatically by Wisej.NET.
The final dialog to cover in this review is "Custom Painting." Tap on the option and you’ll see a modal dialog with a fully rendered Mandelbrot set and a smiling face.
Despite the number of pixels and level of detail, you can see the Mandelbrot renders exceptionally fast – just over 50ms on my machine. If you look at the code behind, the image is rendered on the server using a bitmapped object. Once it is completed, a special "DrawImageUnscaled
" call is made to transfer the bitmap contents onto the browser-based canvas (again, without the developer having to learn or create any client-side code).
The smiling face is a "canvas" control that enables you to write server-side code to render a client-side HTML5 canvas. This allows you to create complex custom web controls entirely in .NET that are fully HTML5 compliant.
In addition to supporting web-based deployments, the Wisej.NET platform allows you to package your application as a local executable that can be run from the desktop. It supports identical functionality to the web-based project as a standalone program. This enables distribution without a web server. The executable is generated in the root directory of the project, and this is an example of running it with the canvas widget.
Everything done with Wisej.NET revolves around the framework’s patented pixel-perfect designer. The WYSIWIG designer allows you to see exactly what the application will look like at runtime. Drag and drop controls from the toolbox onto the page and see for yourself. The designer also allows developers to create responsive profiles: pages that will fit any device.
Wisej.NET comes with a powerful theme builder that allows developers to modify the look and feel of any control within their application. Different themes and styles can be applied to debug applications running in production with ease. Each Wisej.NET control also comes with a “CssStyle” attribute that applies the custom style during design-time.
In addition to the third-party widgets integrated in the sample, Wisej.NET now features the Syncfusion, DevExpress, and Infragistics JavaScript component libraries available as drag-and-drop controls in the designer. The integration simplifies the migration process for developers concerned about losing their native desktop application’s charts, graphs, and other controls.
The Wisej.NET Mobile Integration package for Android and iOS offers web developers the power to integrate native device functionalities into their Wisej.NET applications. Native scrolling and a custom theme will give the user a responsive experience. Use the device’s biometric authentication capabilities, native toolbars, tabbars, and more. Handle any native device event in Wisej.NET using a custom handler.
Load Balancer support
Support for native mobile integrations (iOS and Android)
Support for progressive web applications (PWA)
Robust architecture that is resistant to some of the top OWASP attacks
For developers, the challenge of building a complex enterprise web application with features like large customizable grids and real-time data updates can be daunting. It may seem even more complex when the requirement is to migrate a legacy WinForms-style application to the web without losing the features business users have grown accustomed to, like being able to use their keyboard for data entry and editing values inside a live grid. Fortunately, Wisej.NET is a tool that eases the burden of the challenge and provides a direct path from desktop applications to the web. As a powerful platform that abstracts away the nuances of client/server interactions while embracing all the web has to offer, including third-party HTML5-based solutions, it is the perfect tool for building enterprise web applications.
This review just scratched the surface of the capabilities Wisej.NET provides. In addition to what was covered here, there are dozens more out of the box controls and features like comprehensive theming that can be applied while the application is running. It is possible to target both a web application and a desktop application from the same codebase. All of the functionality is available as a drag-and-drop, designer-based .NET application that leverages existing C# or VB.NET skills. Why not see for yourself how powerful the Wisej.NET platform is? Head over to the website and start your free trial if you haven’t already to follow through this review.
Instead of rewriting: Migrate Windows Applications from the Desktop to the Browser
by and
Built up over many years, desktop applications play a crucial role in providing users with important features and functionalities they have grown accustomed to. Developers know that taking these monolithic-like applications and moving them to the web is no simple task. When the challenge arises, a common approach is to try to break down the system into small, modular components. With enterprise-scale systems this may prove to be a challenge that could take the larger part of a decade. Instead, we suggest an approach that allows web developers to "stand on the shoulders of giants" and reuse the best components of the development work that occurred before them.
For software developers, migration can be a touchy topic. Rather than deal with the existing system, developers may be happy to start from the ground up with a blank slate! There is a strong desire for contemporary technologies, modern tools, and leaving technical debt behind. Reality may hit hard when deadlines start approaching and financial resources are cut short.
The outdated application cannot be redeveloped in just a few months. Opinions differ on the question of how much time is required to move from the Windows desktop to a browser-based cloud application. On the one hand, the existing solution still needs to be maintained. The existing development team may need to split to allocate resources for maintenance and new features in the old application while also working on the side to develop a new platform. This requires know-how (which technology? how does it work?) and experience, because web development works very differently from the familiar Windows world.
Entrusting a second team with the task may not be the best solution. Application-specific knowledge, experience of the industry, and customer expectations can be documented and explained, but the resulting work, if not done by someone who thoroughly understands the application and its users, may not be up to par. It is not without reason that in-house and externally-contracted projects often fail because the programmed result did not meet the client's expectations. Important information may have been missing or the responsible developers were simply not familiarized enough with the specific requirements.
One common challenge is the size and complexity of the existing solution. A mature business application may have hundreds of screens and over a million lines of source code that has been developed, refined, changed and optimized again over years and decades. This can certainly be tidied up and some things can be streamlined, but it requires a good plan, time for implementation and appropriate financial backing.
In 2022, the redevelopment of a complex desktop solution failed after two years. A team of 10 developers were tasked with rewriting a Windows-based ERP solution with around 1,000 screens into a pure web application. Two years later, not even 5% of the application was available in the browser, because a lot of time had already been invested in architecture discussions, feature analysis, class library setup, and other topics. The schedule had tripled from the three years initially forecasted.
The major cloud providers such as Amazon AWS and Microsoft Azure are regularly contacted to evaluate application migrations. However, moving to a virtual desktop or a Citrix-based solution is not a viable long-term option. Now that the Microsoft world with Office 365 and Dynamics is available in the browser and across all platforms, users increasingly want the same experience for their own line-of-business applications. Those who want to remain competitive will have to take this step in order to avoid being overtaken by competitors.
The failed redevelopment project resulted in more realistic time estimates from the major cloud providers. Based on the feature set, with 1,500 on-premise installations in the field, around eight years were projected to redevelop the application for a cloud-native setup, without taking into account team size or other resources.
Do you have eight years? How do you react to all the professional and technical changes that occur in the meantime? How do you ensure that the selected target technology is not already outdated after these eight years?
As an alternative to develop from scratch, Wisej.NET provides a framework for migrating Windows Forms (WinForms) or Windows Presentation Foundation (WPF) applications into native web-based applications. Other source technologies such as Visual Basic 6, Microsoft Access, or Gupta/Centura Team Developer are supported as well.
The project mentioned above was modernized within 13 months with the help of Wisej.NET. The Windows-based client/server solution has become a cloud-based application delivered via Microsoft Azure and Azure App Services. The user interface was updated, having a fresh and modern look and feel today (see box "More than just the runtime").
How does it work? With an application of this size, there are a number of issues that don't just run out-of-the-box in the browser. Web-based applications work in a fundamentally different way to Windows programs based on the client/server principle. The following topics, among others, must be taken into consideration:
Global variables, missing session isolation
File and registry access, Windows-specific functions
State management (browsers work asynchronously per se)
Session management (several users share one application)
Platform independence (deployment on Linux/in the cloud)
Integrations (Microsoft Office Automation is not supported on the server)
Reporting and interfaces to external systems
The biggest question, how WinForms or WPF components can be transferred to an HTML/JavaScript-based interface, is now presented step-by-step in a hands-on example.
Installing Wisej.NET is relatively straightforward from the Visual Studio Marketplace.
Search for "Wisej.NET 3 VS2022" via the "Extensions" menu in Visual Studio and install the extension. Alternatively, visit one of the two following links:
A Wisej.NET license is required after installation. There are two possible license variants available:
The Trial Edition allows the use of Framework & Designer as a test version.
The Community Edition is free of charge for non-commercial purposes and small organizations.
The license can be obtained at
For demonstration purposes, we use an open-source WinForms project that’s available online (). We will convert the Simple Scientific Calculator (see Fig. 1) to a web-based application. The source code and the functions speak for themselves and should not be the focus here. Rather, the focus is on the question of how any application can be migrated to the browser, even if the source code between user interface, business logic and data access is not yet divided into individual layers.
The application has a comparatively simple structure and does not follow a meaningful naming concept. Form1.cs contains the essential logic.
It should also be possible to replicate the steps presented here with other software projects, although the specific requirements and functions will vary depending on code base and programming language. Some initial steps for the migration of different source technologies to Wisej.NET are explained here: .
An essential task is to replace existing UI elements of the Windows-based user interface with web components from the Wisej.NET control collection. References to other areas of the Windows SDK, e.g. Painting (which is largely supported by Wisej.NET), will also work.
The result of the following migration steps is available for download here: . However, to reproduce the steps yourself, you first need the source code of the desktop application. After successfully installing Wisej.NET and opening the Calculator source code in Visual Studio, the procedure is as follows:
There are various procedures for web-enabling an existing application. We decided to add a Wisej.NET project to the existing solution, to copy code from the existing WinForms project into the Web-based solution..
Add a new project in the Solution Explorer by right-clicking on the solution
Select the "Wisej 3 Web Page Application" Project Template
Enter "calcweb" as the project name
The Wisej.NET-specific project settings are confirmed as suggested.
The current Wisej.NET version 3.5 supports both .NET Framework and .NET 7 and 8. Wisej.NET-based applications are already fully .NET (Core) based, i.e. deployment on Linux or other platforms supported by .NET is possible without limitations. For technical reasons, a reference to the .NET Framework 4.8 is still required for the graphical designer in Visual Studio. This dependency will be removed in a future version.
It may seem a little strange at first glance, but in the next step we copy the existing source code from the WinForms project into the Wisej.NET project. The easiest way to do this is to right-click on Form1.cs - "Copy" and "Paste" below calcweb. Form1.resx can then be safely deleted. Compiling the solution is pointless at this point, because we have just inserted WinForms dependencies in a web-based project. We will now resolve these in the next step:
Open Form1.cs in calcweb in source code view.
The namespace "calculator" must be replaced by "calcweb". Better make the adjustments manually in Form1.cs and Form1.Designer.cs.
Within the project, replace System.Windows.Forms with Wisej.Web.
The next step is to remove a property that is not supported in Wisej.NET:
\r\n(\s*)this\.(.*)UseVisualStyleBackColor = true;
is replaced with (nothing), i.e. an empty field, to remove the entire affected line of code (as a regular expression). You can use the Search & Replace functionality in VS to execute this.
At this point, you should already be able to open Form1.cs of the calcweb project with the Wisej.NET Designer in Visual Studio. All you need to do is double-click or use the key combination Shift+F7. If compilation errors occur at this point, closing and restarting Visual Studio may help. In rare cases, switching project types can lead to one-off problems, which will disappear once our conversion work has been completed.
The web-based user interface should now be visible in the Visual Studio Designer. One of the advantages of the Wisej.NET framework is the ability for pixel-perfect design directly in Visual Studio. The various interface elements can be arranged and edited conveniently using the mouse and properties window.
We are close to being able to run the application in the browser. First of all, we have to make sure that the correct page is loaded when the application is started:
Program.cs with its Main()
function must be adapted, i.e. remove the following line: Application.MainPage = new Page1();
add instead use: new Form1().Show();
Our new project, calcweb, should be set as the startup project of the solution, otherwise the WinForms version will continue to open. You can right-click the project in Visual Studio and select "Set as Startup Project".
Run the application
As a result, you should now be able to open the calculator in the browser (see Figure 6).
That was it already. The application runs in the web browser without us having to worry about session management, state handling, communication between client and server or the various technologies (HTML, CSS, JavaScript). Wisej.NET takes care of the nasty details. Things like a browser refresh is also supported: simply enter something and then press <F5> in the browser - the current state is retained.
Many things could be improved at this point. The interface is of course not responsive, as we have only used the source code 1:1. Wisej.NET offers countless options to improve the UI/UX of the calculator application. A TableLayoutPanel that takes over the positioning of the individual buttons would be a good next step.
If the look and feel is still a little too close to the Windows world, you can play with the various integrated themes or the available for Wisej.NET. Themes for Bootstrap, Material, and Graphite are included and can be customized using the Wisej.NET Theme Builder. How it works is explained in detail in the corresponding online documentation ().
A good example of this approach is the TRIO software solution, which was migrated from 16 separate applications (Visual Basic 6) into an integrated suite based on C# with Wisej.NET. Figure 7 shows what is possible in a short period of time, achieving a great new design close to the original application. The application is fully backwards-compatible with existing databases and has been very well received by users.
Apart from that, it makes sense to think about separating logic and UI. Our source application was not exactly a prime example of architectural design and adherence to naming conventions. Wisej.NET can't perform miracles here - it's just a framework that is used specifically to customize the user interface. Nevertheless, the switch from Windows to Web does not mean that the end of the possibilities has been reached. Separating layers, tidying up the source code and paying off technical debt should play an ongoing role in every project.
Wisej.NET-based applications are by no means monolithic. Rather, the question is how a sensible division of the layers can be achieved in complex projects. In principle, all design patterns and architectural approaches are supported. Initial suggestions for the division of layers during new development can be found by following the Wisej.NET NavigationBar Template, which is available via the Visual Studio Marketplace.
This is not the end of the migration, but rather the beginning. In addition to its own component library, Wisej.NET provides integrations with a wide range of external JavaScript controls: from barcodes and QR codes to charting, code editors and much more complex components. Wisej.NET supports the integration of any JavaScript control with a manageable amount of effort. When developing in Visual Studio, the control can be used with the graphical designer with all JavaScript events available from C# or VB.NET.
In addition, Wisej.NET has Premium Extensions that integrate with all major component manufacturers such as Syncfusion, DevExpress, Infragistics and Progress. The demo application at shows some of the controls that are available. Developers can use advanced components such as the Syncfusion Kanban, Grids, and Office-like components for integrating text editors or spreadsheets.
Admittedly, this was a very simplified migration. In reality, software projects are likely to show additional pitfalls in more complex scenarios. For larger applications, it can take days and weeks and months until migrated code can be compiled for the very first time. That’s a matter of complexity. However, the basic procedure is always the same, i.e. the path presented here can be transferred to the migration of large and complex software solutions, with thousands of screens. As an alternative to the do-it-yourself approach, the manufacturer of Wisej.NET and partner companies such as fecher offer a wide range of services, from training to complete outsourcing of application modernization.
by , translated by
Wisej.NET allows you to create a variety of types of Web Applications. There are several templates included with the installation of Wisej. Each of the templates is available for the classic .NET framework 4.8 and for .NET 5/6/7. Here is a brief description of each template:
Creates a web application with a page that fills the entire browser. Controls can be placed directly on the page. Page to page navigation happens simply by creating a new Page object and then invoking the Show() method.
Creates a web application with a window that is displayed within the browser. Controls are placed within the window. The window has the typical characteristics of an OS Desktop window. It contains a title bar with minimize, maximize and close buttons. The architecture of the application- that it consists of windows- is similar to that of a Winforms application. It implements the Multi-document interface (MDI) windows mechanism with a container window for the other windows.
Creates an application that reproduces a customizable desktop container. The look is that of a Windows desktop and its taskbar. You can add items to the taskbar, show floating windows etc. This template is useful when you need to create very complex applications rich in functions, such as applications that encompass the entire user work environment.
Creates a project of Wisej.NET objects (custom controls) that can be used in other Wisej.NET applications as components.
Creates a Web application with a main page similar to the Web Page Application. However, this template adds all the JavaScript parts and the manifest files necessary for the browser to recognize it as a Progressive Web Application (PWA). Some of the advantages of using a PWA include a decrease in loading time after the initial installation due to the caching of resources and an integrated look with the native client device. PWA applications can have a frame around them, so they look more integrated with the client device instead of having the visual look of a typical browser window.
Creates a Web application with a main page similar to the Web Page Application. However, this template includes the MobileIntegration extension. This allows you to write code that utilizes native device functionality such as the camera, device vibration, device orientation, and native UI components. Wisej mobile apps can run on Android and iOS.
Wisej.NET offers a variety of licensing options. The options range from Community up to the Enterprise level. Community is free but with some restrictions, intended for amateur developers or small businesses with up to 5 developers and operating revenues of less than $1 million. The Enterprise level offers direct named support and priority bugfixes. In terms of development, the main difference between community licenses and paid licenses is some integrations with third-party components via Premium Extensions. For a detailed explanation of the options visit:
Installing and configuring the development environment needed to work with Wisej.NET is simple enough. First you need to have Microsoft Visual Studio installed on your development machine. You can use Microsoft Visual Studio 2019 or 2022 in any of the distributed versions (Professional, Enterprise, Community). You will need the versions of .NET for which you want to develop applications.
You can download Visual Studio here:
During the installation of Visual Studio, make sure to install the following under “Workloads”: ASP.NET and web development, .NET desktop development, and Visual Studio extension development. You can re-run the Visual Studio installer if you have already installed Visual Studio and wish to add these without a complete uninstall and reinstall of Visual Studio.
After installing Visual Studio, installing Wisej.NET consists of downloading and installing the Wisej.NET Visual Studio VSIX package. The package can be retrieved from both the Wisej.NET site and from the Visual Studio Marketplace. These are the download links:
Visual Studio Marketplace
After installation of the Wisej.NET VSIX package, you will have the Wisej.NET designer and its project templates installed in Visual Studio.
More information can be obtained here:
Video showing how to install Wisej:
As for including the Wisej.NET library in a Visual Studio project, Wisej.NET is distributed as a NuGet package. This allows you to simplify the management of Visual Studio projects in relation to the various versions of the components and libraries used. It also makes targeting of projects to various versions of .NET and operating system platforms very easy. Here is the link to the various Wisej.NET versions available on NuGet:
The installation of the runtime environment follows the same guidelines as ASP.NET and ASP.NET Core applications, so no special setup or configurations are required other than those specific to the target operating system.
More information can be obtained here:
Once Wisej.NET is installed in your Visual Studio development environment, you will find various templates available. As mentioned earlier, it is possible to create applications for the classic .NET Framework 4.8 and applications for the new cross-platform .NET 5/6/7. The templates distinguish between the types of .NET. The templates that mention (.NET Framework) are templates designed for the classic .NET Framework, while the other templates are for cross-platform versions of .NET. Visually, this is the difference between .NET Framework templates and cross-platform .NET templates:
Depending on the chosen .NET multitargeting we will have a window that guides us through the essential features of our project. In case of multitargeting we will have this dialog window:
In one visual studio solution, you can create projects that target different versions of .NET. Even inside a single project, you can target multiple .NET versions.
Read more information here:
Note: The versions of .NET may vary depending on the ones installed on your computer. It is important to note that the project is always referenced with at least the .NET Framework 4.8 target and optionally with various other installed versions of .NET; this behavior is normal because the current designer of Wisej.NET is made in .NET Framework 4.8. Visual Studio will manage all the compilation commands and component and library references (NuGet or direct) according to the chosen .NET targets.
After you have gone through the "New Project Setup screen, and you click "OK", Visual Studio will open with your project/solution:
Going to the "Solution Explorer" section, and looking under “Dependencies” you can observe how for each .NET target version, the entire set of configurations and references is managed. Note that both the net48 and net7.0 target versions have the Wisej.NET NuGet package.
Wisej.NET uses the NuGet package manager to facilitate its management, allowing for quick changes of referenced versions/builds and updates.
By going to the Visual Studio menu “Tools – NuGet Package Manager – Manage NuGet Packages for Solution” or by right-clicking on the root node of the project (ie "WisejWebApplication1") in the Solution Explorer and selecting the menu item "Manage NuGet Packages" you will be presented with this screen:
As you can see the default source of the packages is the NuGet online repository. If you want to have tighter control of NuGet package sources you can create your own local repositories and force the Visual Studio configuration to use only those repositories. Also note that you must check “Include prerelease” (located in the top middle) if you want to see the NuGet packages for beta versions of Wisej.NET.
When looking at the project properties (right-click on the root node of the project - ie "WisejWebApplication1"- and select "Properties") we observe this:
This project is therefore a "Class Library" targeting .NET 4.8 and .NET 7.
In the "Solution Explorer" section related to the "WisejWebApplication1" project, we can find a series of files that make up the common part of all Wisej.NET Web Applications. These include "Web.Config", "Default.html", "Default.json," "Program.cs" (or "Program.vb" if you use Vb.Net), "Startup.cs" (or "Startup.vb"), and the "Themes" folder. Here follows a description of each file:
Technically, a Wisej.NET application is server-side. Essentially Wisej is a library that works on the ASP.NET framework. Consequently, it shares several features with other ASP.NET applications: namely, being hosted within a Web Server (ie, Kestrel or IIS) and having the same configuration management system, which is based on the Web.Config file. The Web.Config file contains specific configurations for Wisej.NET in terms of Wisej licenses (in the "configuration - appSettings" section) and the various .NET modules and handlers used (in the httpModules, modules, handlers sections).
Default.html is used because a Wisej.NET application is an ASP.NET application. It can be hosted in a process along with other ASP.NET components and share objects such as security contexts, applications, and sessions. Default.html functions as a loader for the Wisej.NET application. It is not mandatory to call it Default.html since it follows the configuration related to the default documents present in the Web.Config.
Let's observe its standard content:
Look at <script src="wisej.wx"></script>
. "wisej.wx" does the actual work of loading the Wisej application. The "wisej.wx" file contains the necessary JavaScript runtime for the client browser. The file is dynamically generated in memory at the first launch of the application.
This file contains the configuration parameters for the Wisej.NET application, including the timeout of the session in seconds ("sessionTimeout") and the default culture ("culture"). For a full list of parameters, see below:
Let's go over what this code snippet does:
This simple example consists of 2 keys ("url" and "startup") and their corresponding values. First, the value of the "url" key indicates that "Default.html" will be used as the default html page. This means that if a request is sent to the server for a URL that doesn't specify a file name, that the file "Default.html" will be sent. Second, the value of the "startup" key indicates what assembly will run once the Wisej application starts. We can break this up into two values (before and after the comma):
WisejWebApplication1.Program.Main
is the fully qualified namespace (Look at the Program.cs
file- this matches up perfectly).
WisejWebApplication1
is the name of the assembly.
The "Startup.cs" file is used for the .NET Core targets and contains the definitions of Wisej.NET .NET Core objects. The Startup.cs file will NOT be used if you run your application in .NET Framework 4.8.
app.UseWisej();
is the part of the code that calls the Wisej middleware, ultimately resulting in the loading of the Wisej application.
The Themes folder contains themes and mixins (theme fragments) created with the Wisej Themes Editor that need to override the default theme indicated in the Web.Config file or in the startup method for application purposes. Themes consist of a JSON file and can be created with the dedicated "Theme Builder" application that can be found at this link:
Wisej.NET is a .NET application and in particular an ASP.NET application in its server-side execution. During development, IISExpress (used by default) or the locally installed IIS server can be used as the web server. In terms of the file system, the application is located in a folder that contains the bootstrap files (Default.html, Default.json) and a "bin" subfolder with referenced .NET assemblies. In the case of our first Web Application, we will have this situation:
The folders "de", "es", "it", etc. present in the "bin" folder contain the Wisej resource files based on the language selected by the user.
When you have .NET multitargeting enabled, the corresponding files can be found in the "bin\Debug" folder. In this case, the project was only built for net7.0.
At runtime, the environment is similar to the development one with some differences:
In Windows environments the full IIS web server is used instead of the IISExpress version.
The Wisej server license key must be specified in the Web.Config file.
The file system structure should include only the bootstrap files (Web.Config, default.html, default.json, the Themes folder, and in the "bin" subfolder only the files necessary for execution).
Prerequisites: Install Visual Studio and the Wisej VSIX package. Then create a new Wisej project using the Wisej Web Application template.
Now let's add some UI and logic. The goal is to add a button to our "Windows1" form that, when pressed (Click event), will display a MessageBox containing the version of .NET with which our Wisej.NET application is running. Here are the steps to follow:
Double-click on the "Window1.cs" file to open it in design view.
Once you open “Window1.cs” in design view, your screen should look like this:
Open the toolbox. You can access the toolbox by going View -> Toolbox, or by clicking on the toolbox icon at the bottom of the design view window.
Select a button object from the Wisej3 control list in the toolbox. You can either click and drag the button into the designer, or you can click once on the button to select it and click again on the designer to place it. If you need to deselect the button, you can hit ”esc”.
4. Click on the button to select it (if it’s not already selected) and look at the “Properties” window. (If you cannot see the properties window, you can open it by clicking on View -> Properties Window.) Scroll down until you see the “Text” property and change the text from “button1” to “Click me!”.
5. Click on the lightning bolt in the properties window to see the events. We are going to handle the Click event of the button. Double-click on the Click event to edit it. (Alternatively, you can use this shortcut: double-click on the button in the designer to handle the Click event.)
6. This will open a new tab in Visual Studio next to the designer view, where you can edit the Window1.cs file. It will create a new function called button1_Click()
, which is called when the button is clicked.
Inside of the button1_Click()
function, add this code:
This will display a MessageBox containing the version of .NET with which our Wisej.NET application is running.
As with all applications developed in Visual Studio, you can launch the compilation and debug execution using the "F5" function key or the start icon present on the menu bar. For client-side execution, you can choose which browser to use: Microsoft Edge, Firefox, Chrome, Microsoft IE 11, Apple Safari, or Opera. Once your Web Page application is compiled and started, it will appear in your browser.
There are two ways to change the running .NET version- you can change the development version, and you can change the version that is used when deploying to IIS.
Change the development version of .NET Click on the down arrow inside of the “Play” button to bring up additional options. Under “Framework”, you can switch between versions of .NET. The options that you see (net48, net7.0, etc) are based on the .csproj file. You can see the .csproj file by double-clicking on the root node of the project (ie "WisejWebApplication1") in the Solution Explorer. This only affects the version of .NET used when running the project in Visual Studio- when the project is compiled, it will be built for all .NET versions listed in the .csproj file.
Change the .NET version used when deploying to IIS Edit the Web.Config file. Choose the version you want to deploy by editing the processPath parameter. This will change the version that is deployed to IIS. Note that this MUST be a .NET Core version; .NET framework 4.8 will not work.
Styling your WiseJ.net app with Theme Builder
by
Implementing features is one thing, making them look good is quite another!
It's important for any framework to have a good story around styling and theming your app, and WiseJ.NET has a dedicated Theme Builder to make this easier.
Let's see how it works.
The first step is to .
The download includes a single zip file, in which you'll find a file called Wisej.ThemeBuilder.exe.
Launch that and you'll find yourself looking at the Theme Builder UI.
At the top of this screen you can see the currently selected theme (Boostrap-4 in this case).
In the top left pane you'll see a detailed list of all the things you can tweak for the current theme.
There you'll find Colors, Images, Fonts, and Appearances.
In the Appearances section you'll find every Wisej.NET component.
Buttons, ComboBoxes, ScrollBars are all available to be styled from this menu.
When you click on an appearance you'll see it's properties in the bottom left pane.
Here I've selected Button and we can see various properties which can be tweaked, including the button's text color, opacity, text alignment.
The preview pane on the right shows, in real time, how your tweaks affect the visible controls.
All the controls in the preview pane can be clicked, which will select the relevant appearance in the pane on the left.
Not all controls are shown in the preview window, but you can get to them if you select the Controls tab in the menu above the preview pane.
Let's start with a really simple tweak to the existing button style.
First we can locate the Button appearance in the Components pane.
Once there we can select one of its states.
We'll explore states in a moment, but for now we'll make our changes to the Default state.
With Button > Default selected we see its specific styles in the Properties pane.
It's then possible to tweak those individual styles/properties.
Here I've changed the backgroundColor style to make buttons yellow by default.
We'll see how to test and use this updated theme in a moment, but first let's take a quick look at component states.
Let's explore states with the help of another component, the MessageBox.
With the Controls tab open in the preview pane you'll see the first few controls are various different types of Message Box.
Click on one of those and it will be automatically selected in the Components pane where we can then see all its properties/styles.
In this context states refer to the different types of Message Box we can show (error, information, stop etc.)
We can see the different background colors specified for each state.
Let's tweak the background color for the information state.
As soon as we do that, we can see from the preview that our Message Box now has a bit of a color clash going on!
The text and icon colors could do with a tweak.
The easiest way to identify which appearance needs changing is to click on the element in the preview pane.
In this case, clicking on the title (the "Message Box" text) highlights the MessageBox > Title appearance.
Now we can clearly see that the property we want to tweak for the information state is properties > textColor.
Let's go ahead and change that.
We can do the same with the Image (exclamation), and the CaptionBar (the bar underneath the title).
By the time we're done we've changed these appearances (all for the information state):
MessageBox > Title
MessageBox > Image
MessageBox > CaptionBar
Now we're close to a style we could run with (albeit we won't be winning any style awards for this one!)
But what about that message text?
If we click the text in the preview pane we'll see MessageBox > Message highlighted in the left-hand pane.
But, unlike the other appearances, this one has no alternative state for Information, just the Default state.
The good news is we can add our own additional state(s).
First by clicking the + icon in the top left corner of the Components pane.
In the resulting window we can select the State tab, then type in the name, which in this case needs to be Information.
Then click OK to add the new State.
Now we can add a new property to this state.
First, select properties in the Properties pane, then click the + icon.
We see the Add Style/Property dialog.
We can select the property name textColor and give it a value (in this case, the color we want).
Click OK and the preview will update to show how the message box looks with our new properties applied.
So far so good, but how to actually use this new style we've defined?
The first step is to save it.
Click the Save As icon (the save icon with a pencil over it) to save your theme somewhere on your machine as a .theme file.
In Visual Studio, in a new or existing Wisej.NET project you'll see a Themes folder.
Right click that, then select Add > Existing Item.
Locate your newly saved theme, and click OK to add it to the Themes folder.
Once done, it should look something like this:
With this we can use this theme when viewing components in the designer.
For example, here's a simple page with a button.
Notice how, with the correct theme selected in the bottom left, the buttons have picked up the new style (yellow background).
This works for development, but if you launch and view your app in the browser it isn't (yet) using the new theme.
For that we need to tweak the application config.
There are a few ways to select which theme your running Wisej.NET app uses.
You can configure it in your application's Default.json.
Another way is via web.config.
Either of these approaches will set the theme for the entire app.
Finally, you can also switch theme programmatically, while your app is running:
Now we know how to create and use themes, there's one thing left to explore; how to define brand new styles from scratch.
But before we do that, there's something we need to understand about appearances.
When working with an appearance, you might notice that it inherits a base class.
For example, taking another look at the Message Box. We can see, if we look at the Components pane that it inherits from window.
This means it will pick up all of window's styles/properties by default.
Of course, you can then override those by defining more specific properties.
Where this comes in particularly handy is if you want to create your own items, based on an existing component.
Let's say you want to create an entirely new kind of button.
So not just a different state for the existing button, but a new type of button you can use when you want to have a completely different appearance.
You can add a new appearance to the existing appearances, by clicking the + icon in the Components pane.
In this case, we want our appearance to look like a button.
So we can set it's inherit
property to button.
Now it will look the same as the existing button
appearance, except for the styles/properties we override.
In this case I've overridden the backgroundColor
style to give it a bold purple background and the textColor
property to give it white text.
Heading back to our WiseJ.NET application we can use this new style, by setting the appearanceKey
for any of our buttons.
By setting appearanceKey
to myButton
we ensure this button picks up our new MyButton appearance.
So there you have it.
You can configure just about any aspect of your Wisej.NET application.
Theme Builder provides a convenient way to see, and tweak the style of any of your Wisej.NET controls.
You can then add the theme to your project and see the results in the designer as you build your components.
Finally, you can specify the theme for your app via config, and/or enable the users to switch theme programmatically as they interact with your application.
One of those common tasks you end up doing with virtually any web application is to show data.
'Data' is a broad brush term. It might be as simple as showing some hardcoded text on the page, or as complicated as retrieving thousands of interconnected rows of data from a database.
A web application's success is often built on how well it presents data to users, so let's explore how we can connect UI to data using Wisej.NET.
The first, and perhaps simplest way to get data to show up in your UI is to bind individual controls to data.
If you've worked with Windows, or WebForms in the past, you'll be familiar with the concept of adding instances of controls to your page.
In Wisej.NET we can add controls, then use them to show data.
For example, we could add a label control to a page, then set its Text
property to the value we want to show:
But what if we want to show data via that label, for example from a database, or some other model that we happen to have access to from this page?
For that we need to drop down to the code for this page.
There, we can interact with the control directly. For example, here's how we might take the values from an instance of a class, and show them in our UI (via two label controls).
UserDetails
is our model in this example.
When this page loads its constructor will be invoked. In there we can set the Text
property for our two labels to the relevant values from the instance of the UserDetails
class.
Once we've bound a control to a property like this the UI will be updated automatically if the value changes.
So far so good, but what if we want to present a lot of data, for example rows from a database or API?
Wisej.NET includes a number of 'container controls' which can be used to show your data:
DataGridView
ListView
TreeView
ComboBox
ListBox
CheckedListBox
These are all useful in different circumstances and the DataGridView
is a particularly handy "go-to" control for displaying structured, tabular data.
To use DataGridView
we can start by pointing it to a data source.
List
Let's say we want to display a table of movies.
Here's a small service to return a list of (hardcoded for the demo) movie details.
The MovieDetails
class looks like this.
For the purposes of the demo Movies.DemoData()
returns a hardcoded list:
Turning our attention to our Wisej.NET page, we can add an instance of the DataGridView
control via the WYSIWYG editor.
Once we have that we can bind it to our movie data:
Now, when we view this in the browser, we get a handy table showing all of our movies:
Each row is bound to a record in the data source (in this case, and instance of MovieDetails
) and each cell is bound to a field in that record (Title
, Summary
, Year
).
Naturally, in most web applications we ultimately want to display data from a database rather than a hardcoded list.
For this we can use ADO.NET or any other ORM/Data Access framework that works with .NET.
Once we go down this road it can be useful to introduce a BindingSource
.
This acts as a sort of 'middle man' between data controls (like our DataGridView
) and the underlying database.
For example, here's the same Movie Example, but using the popular Micro ORM - Dapper.
I've added a BindingSource
to the page (via the WYSIWYG designer) and called it moviesBindingSource
.
I've then used Dapper to query the database, specificaly to retrieve a list of movies.
This list is then assigned as the DataSource
for the BindingSource
.
Functionally this works exactly the same as our earlier example, except the data is now coming from a table in a SQLite database.
Once we've got data on the page we can customise the DataGridView
in various ways, including changing the size of columns, formatting cells, and using different column types.
By default, the DataGridView
automatically shows all the available properties from the underlying data.
There are two ways to control which data is visible in your grid:
By tweaking the underlying model
By modifying the properties of the DataGridView
iteself
Perhaps the simplest option is to to use the model itself to control what gets shown in the grid.
For example, here I've updated the Dapper query to select specific columns from the database (instead of using *
to select all).
Note how this also uses a different model to before, a MovieSummary
class.
Using the model to control which columns appear has the advantage that, if the model changes (for example, we add a property) the grid automatically reflects those changes (including the new property as a column).
Alternatively, we can take control over the columns by modifying the DataGridView
itself.
If we set the AutoGenerateColumns
property to false, we can manually choose which columns we want to display:
in this case I've specified a single column with the name Title
and a DataPropertyName
of Title
.
WiseJ will look for a property called Title
on the model and show its value if it exists.
Finally, whether using autogenerated columns, or specifying them ourselves, we can also customise the appearance of each column via number of handy properties. Here are some of the key ones:
All of the following properties can be set in code, or via the designer when editing columns for the DataGridView
.
We can tweak the width of the column via the Width
property.
Chances are you're going to want to tweak the format of your data at some point. Whether it's showing dates in a specific format, or making sure your currency values include commas and currency symbols in all the right places.
For that we can use the DefaultCellStyle.Format
property for a column.
For example, if our movie data included a full release date (DateTime
) , we could get the release year from it using the Format
property:
You might want to show a different header for a column. We can do that via the HeaderText
property.
You can tweak the style for all cells in a column using DefaultCellStyle
.
For example, here's how to change the background colour:
With this, all cells for the Title
column will have a subtle blue background.
You can make your data visible in your Wisej.NET app using simple binding (for showing individual 'pieces of data') or complex binding, using one of Wisej.NET's numerous 'container' controls.
The DataGridView
is a handy 'go to' option for displaying rows of data.
You can quickly get data into the grid using its DataSource
property.
From there, you can tweak which data is visible by adding/removing properties from the underlying model, or by specifying which columns should be present in the DataGridView
itself.
Finally, you can control the appearance of columns in the grid by explicitly setting column properties, in code, or via the WYSIWYG designer.
by
Wisej.NET ships with a large number of pre-built controls for you to use in your web applications, covering almost every requirement you can think of (taking user input, presenting data etc.)
But what if you want to take it up a notch and show your data in more advanced ways, perhaps offer your users themes and have everything in your web app change based on the chosen theme?
Requirements like these can soon ramp up, and before you know it you're having to make a lot of tweaks to the built-in controls to make them behave the way you want them to.
But there is another option…
Component Libraries have a long history in .NET Development.
These handy libraries give you a significant number of battle-tested, pre-built components which you can drop into your web applications, and the good news is Wisej.NET has support for all the major component vendors.
Let's see how to get up and running with one popular component library - Telerik's Kendo UI.
You can bring Kendo support to your Wisej.NET app via the official NuGet package, via the command line:
Or the NuGet GUI:
In the case that the Kendo controls aren't automatically added to the toolbox after adding the NuGet package, you can follow the instructions below:
Right-click the toolbox and select Add Tab
Call the new tab something useful (like Telerik, or Keno)
Right-click the new tab then select Choose Items
Browse to your project's bin folder and select the dll called Wisej.Web.Ext.Kendo.dll
Click OK to add the items to the toolbox
When you add the NuGet package to your project you should find you now have a file called Wisej.Web.Ext.Kendo.json in your solution.
This controls how your Wisej.NET project interacts with the Kendo library, and includes options for the theme you want to use, plus the source of the components (it defaults to CDN).
For our purposes we can leave the options set to their default values.
But should you want to serve the Kendo packages from your project instead, you can create a folder called Kendo then change the source to Local in this JSON file.
At this point we should be able to drag and drop any of the Kendo components onto a page in our Wisej.NET application.
Let's start with a really simple (if slightly niche!) control, kendoBarcode
.
It might seem like a random choice for learning how Kendo and Wisej.NET work together but the barcode component has the significant benefit of being small and relatively simple, so easy to work with as we explore the various ways we can interact with the controls from a Wisej.NET application.
When you drop a Kendo control onto your Wisej.NET control/page/form, you should see it rendered in the WYSIWYG preview:
So far so good, but how can we interact with the configuration of the control? Let's jump into the code and see what we've got:
The primary mechanism for controlling the Kendo controls is via the dynamic Options
property.
For example, to set the value of this barcode control we can set its Value
via Options
in code, like this:
Now if you run this in the browser it's not immediately obvious whether it's working, mainly because the text for the barcode isn't shown automatically (just the barcode).
Taking a quick look at the Telerik docs for the barcode component API, we can see an option to display the barcode value as text via the text.visible
property.
One thing to watch out for, when setting a property more than one level deep (as in this case, where visible
is not a top level property), you'll need to call Update()
for the changed property to take effect.
Here's how I managed to display the barcode value as text in the barcode
component.
With that, we can now see everything is working (when we view this in the browser).
Interacting with third party controls via their properties is one way to get this working, another is to use methods instead.
Any methods which are exposed via the API for the component can be accessed via the Instance
property.
For example, we can set the barcode's value
using a method, like this:
It's also possible to call that method asynchronously, using the await
keyword. Here for example is how we might await an operation to export the barcode to a PDF:
In this case result
will be a dataUrl for the generated PDF.
Alternatively, you can provide a callback:
Here we're passing a callback which will be invoked when exportPDF
completes.
Effectively this produces the exact same result as using the await
approach above.
So far we've seen how to dsiplay Telerik components on the page and interact with their properties and methods.
But what about reacting to events.
For example, one of the Kendo controls is a rating control.
Logically you'd want to be able to run logic when a rating is selected.
For that we can attach a handler to any events exposed by the control.
To figure out which events are available we can consult the official Telerik documentation.
Here's a link to the API for the Rating Control:
There you can see a number of events listed. We're interested in the change
event.
We can attach to that via the dynamic Instance
object if we prefix the event name (change
) with on
:
This assigns an instance of WidgetEventHandler
(which points to kendoRating1_WidgetEvent
method) to the change
event.
Again we don't have any explicit types to work with here as Instance
is a dynamic object.
e.Data
is also dynamic in this case (Data
on WidgetEventArgs
points to a dynamic object).
As before, we can check the official API docs to see what's available. in this case we should have access to a newValue
property.
With that, when a rating is selected, an AlertBox
pops up which shows the selected rating.
Finally, let's pull all these pieces together with a more realistic requirement.
Let's say we want to show the movie data we've explored in previous articles, but using Telerik's controls to show the list of movies.
Previously we used the built-in dataGridView
to show the list of movies:
Let's try using Telerik's DataGrid
control instead:
The first step is simple enough, to drag and drop an instance of kendoGrid
onto a page. But where to go from there?
If we take a look at the Telerik docs we can see a dataSource
property for binding the grid to data:
We can point that to our existing movie data source:
Just like that, we have a Telerik grid showing movies.
We can now switch on other grid features as we wish. Let's make it sortable:
Now we can click on any of the column headings to sort the data by that column.
One of the big advantages of using a component from a vendor like this is how much quicker it has to handle typical requirements.
For example, we can easily enable filtering by setting filterable
to true.
We can go a step further and enable useful functionality like exporting to PDF too, still just by using the Options
property to toggle specific features on (or off).
Here I've declared an array with a single entry (the string "pdf"). This is enough to make an "Export to PDF" button appear above the grid. When we click that the grid is exported to PDF and the PDF downloaded via the user's browser.
There are significant time-savings to be had if you can use pre-built components to build your Wisej.NET app.
Wisej.NET has support for Telerik's Kendo component library and you can easily drag and drop Kendo components into your Wisej.NET controls/pages.
The Options
property gives you an easy way to set the component's properties. Just remember to call Update
if you set properties more than one level deep.
Finally, Instance
gives you a handy way to invoke methods on the Kendo components, with support for Async operations as well.
Keen to start using Kendo UI controls in your Wisej.NET projects? Find out more about the Kendo premium extension (as well as a number of other premium extensions for other component vendors) over in the .
{
"url": "Default.html",
"startup": "WisejTutorials.Program.Main, WisejTutorials",
"theme": "WisejTheme" // add this
}
<configuration>
<appSettings>
...
<add key="Wisej.DefaultTheme" value="WiseJTheme"/>
</appSettings>
...
</configuration>
private void btnChangeTheme_Click(object sender, EventArgs e)
{
Application.LoadTheme("WisejTheme");
}
namespace WisejTutorials.Panels
{
public partial class SimpleDataBinding : Wisej.Web.UserControl
{
private UserDetails userDetails = new UserDetails { Name = "Nasa", Age = "42" };
public SimpleDataBinding()
{
InitializeComponent();
lblName.Text = userDetails.Name;
lblAge.Text = userDetails.Age;
}
}
public class UserDetails
{
public string Name { get; set; }
public string Age { get; set; }
}
}
public class MovieService
{
public Task<List<MovieDetails>> GetMovies()
{
return Task.FromResult(Movies.DemoData());
}
}
public class MovieDetails
{
public string Title { get; set; }
public string Summary { get; set; }
public int Year { get; set; }
}
public static class Movies
{
public static List<MovieDetails> DemoData()
{
return new List<MovieDetails>
{
new MovieDetails
{
Title = "The Godfather",
Summary =
"The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.",
Year = 1972
},
...
}
}
}
public partial class DataGridViewBinding : Wisej.Web.UserControl
{
...
private void DataGridViewBinding_Load(object sender, EventArgs e)
{
LoadData();
}
private async void LoadData()
{
this.dgMovies.DataSource = await new MovieService().GetMovies();
}
}
public partial class DapperExample : Wisej.Web.UserControl
{
public DapperExample()
{
InitializeComponent();
LoadData();
}
private async void LoadData()
{
using (var connection = new SQLiteConnection(DBConfig.ConnectionString))
{
this.moviesBindingSource.DataSource = await connection
.QueryAsync<MovieDetails>("SELECT * FROM Movie");
this.dgMovies.DataSource = moviesBindingSource;
}
}
}
private async void LoadData()
{
using (var connection = new SQLiteConnection(DBConfig.ConnectionString))
{
this.moviesBindingSource.DataSource = await connection
.QueryAsync<MovieSummary>("SELECT Title, Summary FROM Movie");
this.dgMovies.DataSource = moviesBindingSource;
}
}
public class MovieSummary
{
public string Title { get; set; }
public string Summary { get; set; }
}
this.dgMovies.Columns["Title"].Width = 200;
dgMovies.Columns["ReleaseDate"].DefaultCellStyle.Format = "yyyy";
dgMovies.Columns["Title"].HeaderText = "Movie Title";
dgMovies.Columns["Title"].DefaultCellStyle.BackColor = Color.AliceBlue;
Page myPage = new Page();
myPage.Show();
<!DOCTYPE html>
<html>
<head>
<title>WisejWebApplication1</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge;IE=11" />
<meta http-equiv="Cache-Control" content="no-store" />
<script src="wisej.wx"></script>
</head>
<body>
</body>
</html>
// Default.json
{
"url": "Default.html",
"startup": "WisejWebApplication1.Program.Main, WisejWebApplication1"
}
MessageBox.Show("I'm running on " + System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
dotnet add package Wisej-3-Kendo
public partial class KendoExamples : Wisej.Web.UserControl
{
public KendoExamples()
{
InitializeComponent();
this.kendoBarcode1.Options.Value = "1928374";
}
}
public partial class KendoExamples : Wisej.Web.UserControl
{
public KendoExamples()
{
InitializeComponent();
this.kendoBarcode1.Options.value = "1928374";
this.kendoBarcode1.Options.text = new { visible = true };
this.kendoBarcode1.Update();
}
}
this.kendoBarcode1.Instance.value("567354");
var result = await this.kendoBarcode1.Instance.exportPDFAsync();
kendoBarcode1.Instance.exportPDF((Action<dynamic>)(r =>
{
AlertBox.Show(r);
}));
public partial class KendoExamples : Wisej.Web.UserControl
{
public KendoExamples()
{
InitializeComponent();
this.kendoRating1.Instance.onChange += new WidgetEventHandler(kendoRating1_WidgetEvent);
}
private void kendoRating1_WidgetEvent(object sender, WidgetEventArgs e)
{
AlertBox.Show(e.Data.newValue.ToString());
}
}
public partial class TelerikGrid : Wisej.Web.UserControl
{
public TelerikGrid()
{
InitializeComponent();
}
private async void TelerikGrid_Load(object sender, EventArgs e)
{
this.kendoGrid1.Options.dataSource = await new MovieService().GetMovies();
}
}
private async void TelerikGrid_Load(object sender, EventArgs e)
{
this.kendoGrid1.Options.sortable = true;
this.kendoGrid1.Options.dataSource = await new MovieService().GetMovies();
}
private async void TelerikGrid_Load(object sender, EventArgs e)
{
this.kendoGrid1.Options.sortable = true;
this.kendoGrid1.Options.filterable = true;
this.kendoGrid1.Options.dataSource = await new MovieService().GetMovies();
}
private async void TelerikGrid_Load(object sender, EventArgs e)
{
var toolbar = new[] { "pdf" };
this.kendoGrid1.Options.toolbar = toolbar;
...
this.kendoGrid1.Options.dataSource = await new MovieService().GetMovies();
}
by
Sooner or later you're going to want to enable data input for your web app, so users can add, edit, and delete data.
Whether it's to update their profile, complete a short form, or edit rows in a table, inputting data is a major part of the story for many Line Of Business web applications.
So how does WiseJ handle this requirement…
There are a few options, here are the most common (and useful).
Perhaps the simplest is to programmatically retrieve (or set) the value of an input field at the point you need it.
Here's an example:
We have a couple of labels, a TextBox
and a Button
.
We can wire up an event handler for the button click event, grab the value of the text input, and use it to set a value for our label.
This is nice and simple for quickly taking user input and doing something with it but sometimes you'll want a slightly more 'automatic' approach, where you can keep track of values entered by a user.
For this you can use Data Binding.
You can take a standard input control (like a text box) and bind it to a property on an object.
When the user enters a new value, the object is updated.
Now the text property of txtName
is effectively linked to the Name
property on person
(an instance of the Person
class).
With this we can retrieve the entered value at any time via person.Name
, as we do in the button click event handler.
But what if we want to keep the UI in sync with changes happening in code.
For example, we might want to wire up a Reset button that clears the person's name when clicked, and therefore clears the textbox.
With the binding in place you might expect you could just reset the value of person.Name
and the text input would show the new value (an empty string).
However, as it stands the UI textbox value won't be updated (it will show the previously entered value).
We can solve this by making the underlying Person
class implement INotifyPropertyChanged
.
INotifyPropertyChanged
is a handy mechanism we can use to tell .NET when something has changed.
To implement INotifyPropertyChanged
we need to define a PropertyChanged
event:
With this in place it's then a question of invoking the PropertyChanged
event at the right time (when the Name
property's value is updated).
Now every time we set Person.Name
to a new value, PropertyChanged
will be invoked, and our binding will ensure txtName
is updated to reflect the new value.
But what if we want to go a step further and enable editing of multiple records via something like a data grid? For this we may need to turn to a more advanced form of databinding.
Say, for example, you want to present a number of records in a grid (as we saw when we explored showing data via WiseJ) and enable editing of those records.
One option here is to use a BindingList
.
This is particularly handy when you have any kind of list of objects and want to hook them up to a data control (such as a DataGridView
).
For example, we can create a Binding List of Person
objects:
I've added a DataGridView
(dataGridView1
) via the WYSIWYG editor then hooked that up to the BindingList
via the page's constructor.
With this we get a list of people's names, and can add, edit and delete records safe in the knowledge that the underlying list will be updated accordingly.
This is all well and good for in memory lists, but what if we want to persist changes to a database and perhaps provide our own custom UI for editing?
Let's look at a concrete example where we'll use a combination of the DataGridView
to show a list of movies, and a little bit of Simple Binding to present a custom edit dialog for editing a movie's title.
In the previous article we used Dapper to fetch a list of movies from a database and present it to the user via a DataGridView
.
DapperExample.cs
Now let's suppose we want to enable users to edit the title of any of the movies in this list.
In this case we want to show a custom dialog window which can be used to edit the title.
Here's the flow we're aiming for:
User double-clicks the row for the movie they want to edit
A dialog window opens with a form containing a single text input for the movie's title
The user makes their changes and clicks Save (or OK)
The new values are used to update the relevant record in the database
We can use the grid view's DoubleClick
event to wire up the DoubleClick
event:
DapperExample.cs
We start by locating the currently selected row in the grid view:
With this we know the Id
of the movie we want to edit.
We can then go ahead and use Dapper to fetch the relevant details.
Once we have the details we can instantiate then show our editing window (theEditMovieDialog
dialog), giving it the movie details we just fetched from the database.
Here's the dialog itself:
EditMovieDialog
We use bindings (as we saw earlier) to ensure the value entered into the txtTitle
text input is reflected in the underlying model (UpdatedDetails
).
When the user clicks the OK button the dialog result is set to OK
and the dialog is closed.
Back in our main page we can retrieve the updated details, make a call to update the DB (and then re-fetch the data so we can show it in our grid view).
With this we have a list of movies with a handy option for the user to edit each movie's title.
When the user clicks Save the dialog is closed (with an OK dialog result) and the code in the main DapperExample page calls the database to update the selected movie's title.
There are numerous methods for accepting user input via WiseJ, here we've explored some of the key underlying mechanisms.
You can interact with input controls (like a text box) directly or use binding to ensure you always have access to their current values.
INotifyPropertyChanged
is useful to ensure your UI stays in sync with your models.
Finally, complex binding is useful when you want to interact with records via built-in data presentation controls like a grid view.
public partial class SimpleInput : Wisej.Web.UserControl
{
...
private void button2_Click(object sender, EventArgs e)
{
lblMesage.Text = "Hello " + txtName.Text + "!";
}
}
public partial class SimpleInput : UserControl
{
private Person person = new Person();
public SimpleInput()
{
InitializeComponent();
txtName.DataBindings.Add("Text", person, nameof(person.Name));
}
private void button2_Click(object sender, EventArgs e)
{
lblMesage.Text = "Hello " + person.Name + "!";
}
}
public class Person
{
public string Name { get; set; }
}
lblMesage.Text = "Hello " + person.Name + "!";
private void btnReset_Click(object sender, EventArgs e)
{
person.Name = string.Empty;
}
public class Person : INotifyPropertyChanged
{
public string Name { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class ComplexInput : Wisej.Web.UserControl
{
BindingList<Person> people = new BindingList<Person>();
public ComplexInput()
{
InitializeComponent();
dataGridView1.AllowUserToAddRows = true;
dataGridView1.AllowUserToDeleteRows = true;
dataGridView1.DataSource = people;
}
}
private async void LoadData()
{
using (var connection = new SQLiteConnection(DBConfig.ConnectionString))
{
this.moviesBindingSource.DataSource = await connection
.QueryAsync<MovieSummary>("SELECT Title, Summary FROM Movie");
this.dgMovies.DataSource = moviesBindingSource;
}
}
+...
private async void dgMovies_CellDoubleClick(object sender, Wisej.Web.DataGridViewCellEventArgs e)
{
if(e.RowIndex >= 0)
{
using (var connection = new SQLiteConnection(DBConfig.ConnectionString))
{
// figure out which item was selected in the grid
MovieSummary selectedItem = (MovieSummary)dgMovies.Rows[e.RowIndex].DataBoundItem;
var selectQuery = "SELECT * FROM Movie where Id = @id";
// fetch the details for the specific movie
var details = await connection.QuerySingleOrDefaultAsync<MovieDetails>(
selectQuery, new { selectedItem.Id });
// create a new instance of the dialog window (and pass along the movie details via its constructor)
using (EditMovieDialog editDialog = new EditMovieDialog(details))
{
// show the dialog
var result = editDialog.ShowDialog();
// run this logic when the dialog result is 'OK'
if(result == DialogResult.OK)
{
// save changes
// refetch data
}
}
}
}
}
MovieSummary selectedItem
= (MovieSummary)dgMovies.Rows[e.RowIndex].DataBoundItem;
var selectQuery = "SELECT * FROM Movie where Id = @id";
// fetch the details for the specific movie
var details = await connection.QuerySingleOrDefaultAsync<MovieDetails>(
selectQuery, new { selectedItem.Id });
using (EditMovieDialog editDialog = new EditMovieDialog(details))
{
// show the dialog
var result = editDialog.ShowDialog();
...
}
public partial class EditMovieDialog : Form
{
public Data.MovieDetails UpdatedDetails { get; set; }
public EditMovieDialog(Data.MovieDetails details)
{
InitializeComponent();
this.UpdatedDetails = details;
txtTitle.DataBindings.Add("Text", UpdatedDetails, nameof(UpdatedDetails.Title));
}
private void btnSave_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
}
...
using (EditMovieDialog editDialog = new EditMovieDialog(details))
{
var result = editDialog.ShowDialog();
if(result == DialogResult.OK)
{
var updatedRecord = editDialog.UpdatedDetails;
var dbSaveResult = connection
.Execute("UPDATE Movie set Title = @title where Id = @id",
new { editDialog.UpdatedDetails .Title, selectedItem.Id});
LoadData();
}
}
Learn how Telerik's Kendo UI DataGrid can make working with data in your WiseJ.NET web applications much easier.
by Jon Hilton
If you're building web applications, you're going to spend a lot of your time figuring out how to present data on screen.
Simple tables will get you so far, but it's often only a matter of time until you'll be asked to implement paging, sorting, aggregate columns and more besides.
While you can go ahead and build that sort of functionality yourself, a much quicker option is available, in the form of pre-built datagrids from established component vendors.
Last time out we explored how to start using Telerik's Kendo UI with Wisej.NET.
Now let's take that a step further and see how Kendo UI's DataGrid and Wisej.NET make it possible for your users to view and interact with your data.
Kendo UI's DataGrid makes it quick and simple to present tabular data.
You'll need a data source - one option is to use the dataSource
parameter and provide your grid with an IEnumerable<T>
.
These steps assuming we've got a Wisej.NET UserControl or Page, have added an instance of the kendoGrid
and renamed it to gridSales
.
Let's start with a method to fetch some random sales data (for demo purposes):
KendoDataGridFundamentals.cs
public partial class KendoDataGridFundamentals : Wisej.Web.UserControl
{
...
private IEnumerable<Sale> sales =
Enumerable.Range(1, 100).Select(x => new Sale
{
Placed = DateTime.Now.AddDays(x),
CustomerName = "Customer " + x,
Id = x,
Value = x * 20
});
private class Sale
{
public int Id { get; set; }
public DateTime Placed { get; set; }
public string CustomerName { get; set; }
public decimal Value { get; set; }
}
}
We can wire the grid up to this data using the dataSource
property:
private void KendoDataGridFundamentals_Load(object sender, EventArgs e)
{
this.gridSales.Options.dataSource = sales;
}
With that the DataGrid
component springs into life and shows sales data.
We can set any of the grid's configuration options via the dynamic Options
property.
Here's how we can make the grid sortable:
private void KendoDataGridFundamentals_Load(object sender, EventArgs e)
{
this.gridSales.Options.dataSource = sales;
this.gridSales.Options.sortable = true;
}
With this the column headings becomes clickable - multiple clicks toggle between sorting ascending, descending, or not at all.
We could end up with a large amount of sales to show in the grid and one common way to make large amounts of data more accessible is to use paging.
private void KendoDataGridFundamentals_Load(object sender, EventArgs e)
{
...
this.gridSales.Options.pageable = new { pageSize = 25 };
}
With a pageSize
configured the grid will now show that number of items per page and render UI for navigating between pages.
Moving beyond the basics, the Kendo grid can tackle some more advanced scenarios too.
For example, you may wish to group your data based on specific fields.
Let's extend our dummy sales data to include a Product
.
private IEnumerable<Sale> sales =
Enumerable.Range(1, 100).Select(x => new Sale
{
Placed = DateTime.Now.AddDays(x),
CustomerName = "Customer " + x,
Id = x,
Value = x * 20,
Product = x < 50 ? "iPhone" : "Android Phone" // split sales between two products
});
private class Sale
{
public int Id { get; set; }
public DateTime Placed { get; set; }
public string CustomerName { get; set; }
public decimal Value { get; set; }
public string Product { get; set; }
}
Now half of the sales will be for iPhone, the other for Android.
Again we can use the trusty dynamic Options
property, this time to set the grid's groupable
property.
private void KendoDataGridFundamentals_Load(object sender, EventArgs e)
{
this.gridSales.Options.dataSource = sales;
this.gridSales.Options.sortable = true;
this.gridSales.Options.groupable = true;
}
Things can get a touch confusing if we enable paging and grouping so I've removed the paging for now.
With this we can drag any field up to the top of the grid to group by it.
One thing we've ignored until now is the slightly verbose way the grid is displaying our dates for when the sales orders were places.
We can change this if we take direct control over which columns we show, and how we show them.
private void KendoDataGridFundamentals_Load(object sender, EventArgs e)
{
...
this.gridSales.Options.columns = new[] {
new {
field = "Placed",
title = "Placed",
format = "{0:yyyy-MM-dd}"
}
};
}
One thing to watch out for - the grid will now only show the columns specified (where before it automatically showed all of them).
In this case we probably still want our other columns, so we'll need to explicitly include them too:
private void KendoDataGridFundamentals_Load(object sender, EventArgs e)
{
...
this.gridSales.Options.columns = new[] {
new {
field = "CustomerName",
format = ""
},
new {
field = "Placed",
format = "{0:yyyy-MM-dd}"
},
new {
field = "Value",
format = ""
},
new {
field = "Product",
format = ""
}
};
}
Leaving the values for format
blank for the other fields here means they'll retain their default format.
So far we've used anonymous objects in our C# code to set options for the grid.
In practice this can become a little unwieldy.
Take another look at the code for formatting the Placed
column:
this.gridSales.Options.columns = new[] {
new {
field = "CustomerName",
format = ""
},
new {
field = "Placed",
format = "{0:yyyy-MM-dd}"
},
new {
field = "Value",
format = ""
},
new {
field = "Product",
format = ""
}
};
To keep the compiler happy we've had to declare the format
property for every column, even though we're leaving them as empty strings because. Otherwise we'd get an error:
No best type found for implicitly typed array
But all that boilerplate, just so we can tweak the format for one column, seems unnecessary.
To avoid it we can use one of two alternative methods for configuring the grid.
One option is to create a class to represent column configuration.
namespace WiseJTutorials.Panels.KendoGridModels
{
public class Column
{
public string field { get; set; }
public string format { get; set; }
public bool? groupable { get; set; }
}
}
Now we can use that in place of the anonymous types we were using before.
this.gridSales.Options.columns = new Column[] {
new Column{
field = "CustomerName"
},
new Column{
field = "Placed",
format = "{0:yyyy-MM-dd}"
},
new Column{
field = "Value"
},
new Column{
field = "Product"
}
};
The other option is to head to the designer for the page and directly paste JSON as the value for the Options
property.
This is handy if you want to take examples directly from the Kendo API docs. For the most part you can just paste them in here and it should work.
It also has the advantage of making the configuration visible in the designer. In this case the designer now shows the columns for the grid in the WYSIWYG editor.
One last handy feature of the Kendo grid is its built-in ability to filter data.
If you set the filter mode to row
the column header will transform into a row of textboxes where users can enter values to filter the data.
this.gridSales.Options.filterable = new { mode = "row" };
Alternatively you can set filterable
to true
(without setting a mode) to get a filter icon for each column.
this.gridSales.Options.filterable = true;
Clicking this icon reveals more options for filtering the results.
For some columns you might want to let your users select (by clicking a checkbox) which of the available values they want to filter by.
You can enable this on a column by column basis.
In JSON
the configuration looks like this:
columns: [
{ field: "Product", filterable: { multi: true }},
{ field: "Value" }
]
If you've created a strongly typed object to represent columns you can tweak it to include a filterable
property.
namespace WiseJTutorials.Panels.KendoGridModels
{
public class Column
{
public string field { get; set; }
public string format { get; set; }
public bool? groupable { get; set; }
public Filterable filterable { get; set; }
}
public class Filterable
{
public bool multi { get; set; }
}
}
Then configure which columns you want to be presented using checkboxes.
this.gridSales.Options.columns = new Column[] {
...
new Column{
field = "Product",
filterable = new Filterable{ multi = true }
}
};
Wich results in a handy checkbox list for that column when you view this in the browser.
Kendo Grid is a tried and tested component for presenting and interacting with data.
Wisej.NET's premium extension for Telerik's Kendo UI makes it possible to use the grid in your Wisej.NET applications.
You can use the dynamic Options
property to enable the grid's built-in features such as paging, sorting and grouping.
Configuration via anonymous objects is useful to get started, but strongly-typed objects can cut down on boilerplate and make ongoing development easier.
You can also define the grid's options using JSON if you assign it to the Options
property and the easiest way to do this is via the WYSIWYG designer.
by Gabriele del Giovine, translated by Julie Hirt
Wisej.NET provides a wide range of components suitable for implementing various typical use cases in Line of Business (LOB) application development. Many of these components also offer a design surface that allows for the visual creation of application UIs. Below is a list of the available built-in components:
The ones that interest us at the moment are those that offer a drawing surface that allows for easy creation of a UI (user interface) in a visual way.
This feature is not always present in web development tools, due to their dynamic nature and the need for responsive adaptation of the UI to the type of device/browser used. The dynamism required at runtime would make development unnecessarily burdensome and imprecise through a visual drawing surface.
It should be remembered that Wisej.NET is a tool for developing LOB (Line of Business) applications, applications that by their nature must have precise, consistent UIs that are inherently suited to being designed with precision and ergonomics. It is worth considering that often the time spent by analysts and developers studying and implementing the UI of an LOB application is far greater than the time spent managing the underlying business logic. This means that the use of development tools without a visual drawing surface presents a major obstacle to productivity and manageability of the application over time.
The elements with a drawing surface follow the types of applications available in Wisej.NET and behave in the same way with some small differences. Below is the list of elements with a drawing surface.
Web Page Application
Page
Web Application
Window
Web Desktop Application
Desktop
All
UserControl
Wisej.NET is fully integrated into Microsoft Visual Studio and follows its standards for managing the files that make up the various components of a solution. In the case of the designer, we have the main .cs or .vb file (for example, Page1.cs) and the related designer files (Page1.Designer.cs) and resource files (Page1.resx).
The designer's code management will automatically create and maintain the code present within the designer file (ie Page1.Designer.cs). It is possible to customize this code manually if deemed necessary. If there are errors in the designer file, it cannot be opened in design view until the errors are fixed.
An important element of the designer is the toolbar. It contains functionalities aimed at facilitating interaction with the designer itself and with the objects contained within it.
Shows a welcome page with links to resources, help, and tips
Cycle through the various usable themes in the drawing surface
Change the color used for selection lines and various glyphs.
Change the resolution of the drawing surface selection-alignment grid.
Change the client profile shown in design view. By changing the client profile, the property values related to the responsiveness of various objects are updated.
Create a screenshot of the selected control or the entire drawing surface and save it to the clipboard.
Show or hide controls with the "Visible" property set to false. The designer normally shows all controls present on the drawing surface. When there are multiple controls that overlap or can be hidden or shown at runtime, the view can become too complex or confusing. Using this functionality helps mitigate the problem.
Update the selected controls or the entire drawing surface. This is useful when the rendering of a control is incomplete due to the complexity of the object or errors in the objects themselves.
Show or hide anchor glyphs. These are used to "anchor" a specific side of a control to it's parent, which keeps the relative distance the same as the parent is resized.
Show the Wisej.NET controls toolbox. The toolbox is where you can select Wisej.NET controls to add to the designer.
Show the object hierarchy structure (document outline)
Set the tab order of objects on the drawing surface. Select "Manual" in order to create the tab order yourself by clicking on each object in order. Select "Horizontal" to set the tab order to go from left to right horizontally. Select "Vertical" to set the tab order to go from top to bottom vertically.
Show the automatic layout generation tool for the selected controls on the drawing surface. This allows you to quickly arrange controls in different layouts- ie ordered horizontally, ordered vertically, docked to the left.
Show the properties window of the selected control/object. This window can also be shown by selecting View -> Properties Window or by pressing F4.
Shows the name, position and dimensions of the selected control/object.
Opens a window which contains options to configure the HTML rendering method and to "recycle" the designer, which is useful when a control is not loading.
Show the Wisej.NET license panel.
Both the visual and non-visual approaches can be used to create and position a control on the drawing surface. In the visual approach, the Wisej.NET toolbox is opened, the object is dragged onto the drawing surface and positioned where desired, and the properties/events management window is used to manage the properties and events of the selected control. It is the designer's and Visual Studio IDE's task to generate the various code parts in the files that make up the Page, Window, etc. container. Alternatively, it is possible to create all controls by manually writing the relevant code. The manual approach is useful when controls need to be created dynamically. In all other cases, it is better to use the visual method.
After creating a template (in this example a Web Page Application), and double clicking on the designer file (in this example Page1.cs) we will have the following situation in Visual Studio:
Select the Wisej.NET toolbox icon by clicking on it in the toolbar, or by selecting View -> Toolbox. The controls/objects toolbox for Wisej.NET will be displayed like so:
Now we will add two controls to the design surface, a "TextBox" and a "Button". Simply perform a Drag & Drop operation. The goal of the application is to invite the user to write their name and, after pressing the button button1, display a message box on the page with the text "Hello world! My name is" followed by the name entered by the user.
By clicking either object to select it, you can see the snap points for anchoring, movement and resizing, as well a box with an arrow in the upper-right corner, which opens a menu for quickly editing the main properties.
You can open the document outline by clicking on the corresponding button from the toolbar. Notice that two objects have been inserted, one named "textBox1" and one named "button1".
Select "button1" by clicking on the control or selecting "button1" in the document outline. This will show its properties window. Change the value of the Text property to "Hello World!".
Select "textbox1" and open its properties window. Find the "Label" property and click on the "+" by it. Modify the "Label.Text" property to be "My name is". Note that this is different from the Text property- the Text property controls the text inside of the textbox, whereas the Label.Text property controls the text right outside of the textbox.
In the designer now we will have this situation:
We can align the button "button1" better by selecting it and positioning it where we want or by modifying the "Location" property. As you will notice, the object model follows that of Windows Forms TextBox and Button controls.
Now let's see what the designer has generated in the Page1.cs, Page1.Designer.cs, and Page1.resx files. Essentially, a Page1 class was generated in the "WisejWebApplication" namespace, which derives from the base class Wisej.Web.Page with a partial class declaration (distributed over multiple source files). The code of the Page1 class is contained in the Page1.cs and the Page1.Designer.cs files.
Note that the code from the Page1.cs and Page1.Designer.cs files could have easily been written manually. It is not advised to manually edit the Page1.resx file.
using Wisej.Web;
namespace WisejWebPageApplication1
{
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
}
}
The Page1.cs file contains the declaration of the Page1
class. Inside the Page1
class, there is the constructor method Page1()
which invokes the component initialization method, InitializeComponent()
. InitializeComponent()
is defined in the Page1.Designer.cs file:
namespace WisejWebPageApplication1
{
partial class Page1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Wisej Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new Wisej.Web.TextBox();
this.button1 = new Wisej.Web.Button();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.LabelText = "My name is";
this.textBox1.Location = new System.Drawing.Point(85, 58);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(93, 53);
this.textBox1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(195, 51);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(100, 37);
this.button1.TabIndex = 1;
this.button1.Text = "Hello World!";
//
// Page1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 19F);
this.AutoScaleMode = Wisej.Web.AutoScaleMode.Font;
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Name = "Page1";
this.Size = new System.Drawing.Size(980, 761);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Wisej.Web.TextBox textBox1;
private Wisej.Web.Button button1;
}
}
Page1.Designer.cs contains the definition of InitializeComponent()
. Inside the InitializeComponent()
method, we find the declaration and creation of all objects (textbox1 and button1) and the setting of their properties (ie, Name
and Size
). In addition, we find the Dispose()
destructor method.
The Page1.resx file is an embedded resource. It can contain multiple resources such as strings and binary data. In this case, the Page1.resx file contains a single item called $this.RulerSnapLines
. $this.RulerSnapLines
is a set of helper lines you can define in Visual Studio to help with positioning. When dragging objects across the designer, they will snap to these lines. You can click on the ruler to add new lines or on an existing line to remove it.
Now that we have positioned our UI elements on the page, we need to manage our use case (the message box with the text "Hello world! My name is"). To do this, we need to handle the click event of button1, and verify that the user enters a value other than nothing or just empty spaces. So, we select the button1 object and its property window, making sure to click on the EVENT filter button (as shown here)
Double clicking on the Click event will generate the corresponding handler.
The window tells us that the button1_Click
method has been associated with the Click
event of button1
. Double-clicking on the event allows us to go directly to the generated code in Page1.cs. Let's observe what has been generated in the Page1.cs file:
Let's also observe what has been generated in the Page1.Designer.cs file inside the InitializeComponent()
method:
this.button1.Click += new System.EventHandler(this.button1_Click);
As you can see, the button1_Click
method is defined in the Page1.cs file. button1_Clic
k is attached as a handler of the Click event (using the += operator) in the Page1.Designer.cs file. All of this code could have been written manually instead.
Let's now move on to handling the required use case, which is the issuance of a message (specifically, an AlertBox in the browser) following the press of the button1
button, after checking that the user has entered a value in the textbox1
textbox that is different from null or a string composed only of spaces.
private void button1_Click(object sender, System.EventArgs e)
{
string userName = this.textBox1.Text;
if (userName == null || userName.Trim() == "")
{
AlertBox.Show("Tell me your name!");
} else
{
AlertBox.Show("Hello world, my name is "+userName);
}
}
As you can see, only C# has been used, executed server-side, and there is no JavaScript in the sources, no HTML file other than the default one, no .js file, no .css file, or other files with other markup languages. There are no complex initializations/builders/constructors or page routing mechanisms or code that handles application/page state.
A Wisej.NET session contains the entire state of the application. The state contains all the objects created by the app, including all visual components. Unfortunately, the session is lost if the server goes offline. This issue can be mitigated by storing data outside of the session (ie on disk, shared storage, database, via a tool such as Redis) so that if a session crashes due to the server going offline, in-progress work can be recovered.
MVC is a way of separating an application into three interconnected parts: the data storage (Model), user interface (View), and connection between the view and the model (Controller).
The Model represents the application's data. It provides an interface for storing, accessing and updating the data. The View represents the user interface, displaying data from the model to the user and accepting input. The Controller acts as an intermediary between the View and Model, handling user input and updating the Model and View as needed.
MVC enables a clear separation of concerns which improves code organization, enhances maintainability, and facilitates code reusability. MVC's testability enables focused unit testing of individual components, leading to more robust and bug-free applications. Moreover, the flexibility of having multiple views for the same data supports the development of scalable and adaptable applications across different platforms or devices.
You can find the full code of the sample project here: https://github.com/iceteagroup/wisej-architecture-examples
A simple example of a model would look like this:
public class StudentModel
{
public int Id { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
In this example, the model represents a StudentModel
object with its properties Id
, Email
, Name
and Age
.
The model is responsible for all validation logic that enforces business rules on the data. For example, if you wanted to check that the Email
property had the correct format of local-part@domain, the proper place to put that logic would be in the model. Likewise, if you wanted to check that the Id
property was between 1 and 999, that logic would belong in the model.
Here is the StudentModel
, updated with data annotations to check that the Id
and Email
are formatted correctly. It also contains a method, ValidateData()
, that determines if a given model contains valid data.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace WisejMVC
{
public class StudentModel
{
[Range(1, 999, ErrorMessage = "Id must be between 1 and 999")]
public int Id { get; set; }
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public string Name { get; set; }
public int Age { get; set; }
static public string ValidateData(StudentModel model)
{
string message = "";
ValidationContext context = new ValidationContext(model, null, null);
List<ValidationResult> validationResults = new List<ValidationResult>();
bool valid = Validator.TryValidateObject(model, context, validationResults, true);
if (!valid)
{
foreach (ValidationResult validationResult in
validationResults)
{
message += validationResult.ErrorMessage + " \n";
}
}
else
{
message += "The data is valid!";
}
return message;
}
}
}
Let's break down what this code does.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
System.Collections.Generic
is needed for the List<ValidationResult>
that is created later in the code. The System.ComponentModel.DataAnnotations
namespace provides code for validating data.
[Range(1, 999, ErrorMessage = "Id must be between 1 and 999")]
public int Id { get; set; }
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
Notice that there is a new line of code in brackets above the declaration of the Id
property. This is a validation attribute that comes from the System.ComponentModel.DataAnnotations
namespace. In this case, it indicates that the Id should be between 1 and 999, inclusive, and it provides an error message that can be shown if the Id is not valid.
Likewise, the line of code [EmailAddress(ErrorMessage = "Invalid Email Address")]
indicates that the Email
property must be a valid e-mail, and gives the error message to show if the email is not set to a valid e-mail.
Note that these attributes do not prevent you from making an "invalid" instance of StudentModel
. You can write this line of code, and it will compile and run:
StudentModel model = new StudentModel() { Name = "John", Id = 999999, Age = 30, Email = "bademail" };
So how can we avoid creating an invalid StudentModel
? Simple: we define a function called
ValidateData()
which can determine if a given instance of StudentModel
contains valid data. Let's look at the function in more detail:
string message = "";
Defines a string variable to hold the error message, or lack therof. This variable will be returned at the end of the function.
ValidationContext context = new ValidationContext(model, null, null);
The ValidationContext
class contains information about the object being validated, the validation attributes that are applied to the object's properties, and the current validation state. It provides a way to get the values of the object's properties. In this line of code, we create an instance of the ValidationContext
class, passing in the object being validated (model
) and null values for the service provider and items.
List<ValidationResult> validationResults = new List<ValidationResult>();
The ValidationResult
class represents the result of a validation operation. It contains an error message and a reference to the property that failed validation. If the validation was successful, the ValidationResult
object will not contain any error messages. In this line of code, we create a list called validationResults
that can contain ValidationResult
objects. We will use this list to store a ValidationResult
object for each property that fails validation.
bool valid = Validator.TryValidateObject(model, context, validationResults, true);
We send 4 parameters to the Validator.TryValidateObject()
function: model
(the object to be validated), context
(a ValidationContext
object), validationResults
(a list of ValidationResult
objects- it will be used to store any failed validations) and true
(indicates that all properties will be validated)
The function returns true if model
is valid, false if model
is not valid, and we store this in the variable valid
.
You can read more about this function in the documentation:
if (!valid)
{
foreach (ValidationResult validationResult in
validationResults)
{
message += validationResult.ErrorMessage + " \n";
}
}
else
{
message += "The data is valid!";
}
If valid
is false, meaning that at least one property of model
was not valid, the code loops through the validationResult
list and appends each error message to message
.
If valid
is true, meaning that all properties of model were valid, message
is set to "The data is valid!".
return message;
returns the contents of the variable message
, so that they can be used outside of this function.
The model is also responsible for retrieving data from and storing data in the database.
The GetStudents()
function accesses the database, and returns a list of the items in the database.
// returns a list of StudentModel objects from the database
public static List<StudentModel> GetStudents()
{
string jsonDatabaseFilePath = "database.json";
// Read the JSON file content
string json = File.ReadAllText(jsonDatabaseFilePath);
// Deserialize the JSON into a List<StudentModel>
List<StudentModel> students = JsonConvert.DeserializeObject<List<StudentModel>>(json);
return students;
}
The AddStudent()
function adds the StudentModel to the database. Note the use of the keyword this
. Each instane of the StudentModel class can call AddStudent()
to add itself to the database.
// Adds the model to the database. Returns a string which contains an error message if the model is invalid.
public string AddStudent()
{
string validMessage = "Added new student to the database.";
// ValidateData returns a string with the validation errors. Returns validMessage if the data is valid.
string message = StudentModel.ValidateData(this, validMessage);
// if the data in the model is valid, add the student to the database
if (message == validMessage)
{
string jsonDatabaseFilePath = "database.json";
//add the data to the database
UpdateJsonFile(jsonDatabaseFilePath, this);
}
// If the data is not valid, add a bit to the message informing the user that the student was not added to the database.
else
{
message += " New student was not added to database.";
}
return message;
}
In this sample, the "database" is simply a JSON file. If the database was a SQL server database, you would still have the GetStudents()
and AddStudent()
functions. You would connect to the database via a connection string instead of via the filename of the JSON file.
The View is a Wisej page that displays the list of students in a DataGridView and has an "Add Student" button. Generally, the view will be edited by using drag-and-drop to add controls to the designer. An example of a View would look like this, when seen in the designer:
When the user adds or edits controls in the designer, code is produced in the Designer.cs file (In this case- Page1.Designer.cs) Let's look at the code for the View:
namespace WisejMVC
{
partial class Page1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Wisej Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dataGridView1 = new Wisej.Web.DataGridView();
this.button1 = new Wisej.Web.Button();
this.label1 = new Wisej.Web.Label();
this.txtId = new Wisej.Web.TextBox();
this.txtEmail = new Wisej.Web.TextBox();
this.txtName = new Wisej.Web.TextBox();
this.txtAge = new Wisej.Web.TextBox();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// dataGridView1
//
this.dataGridView1.Location = new System.Drawing.Point(102, 70);
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.Size = new System.Drawing.Size(598, 276);
this.dataGridView1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(740, 298);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(146, 37);
this.button1.TabIndex = 1;
this.button1.Text = "Add Student";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(324, 25);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(129, 18);
this.label1.TabIndex = 2;
this.label1.Text = "Student Management";
//
// txtId
//
this.txtId.LabelText = "Id";
this.txtId.Location = new System.Drawing.Point(740, 53);
this.txtId.Name = "txtId";
this.txtId.Size = new System.Drawing.Size(149, 53);
this.txtId.TabIndex = 3;
//
// txtEmail
//
this.txtEmail.LabelText = "Email";
this.txtEmail.Location = new System.Drawing.Point(740, 112);
this.txtEmail.Name = "txtEmail";
this.txtEmail.Size = new System.Drawing.Size(146, 53);
this.txtEmail.TabIndex = 4;
//
// txtName
//
this.txtName.LabelText = "Name";
this.txtName.Location = new System.Drawing.Point(740, 171);
this.txtName.Name = "txtName";
this.txtName.Size = new System.Drawing.Size(146, 53);
this.txtName.TabIndex = 5;
//
// txtAge
//
this.txtAge.LabelText = "Age";
this.txtAge.Location = new System.Drawing.Point(740, 230);
this.txtAge.Name = "txtAge";
this.txtAge.Size = new System.Drawing.Size(149, 53);
this.txtAge.TabIndex = 6;
//
// Page1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 19F);
this.AutoScaleMode = Wisej.Web.AutoScaleMode.Font;
this.Controls.Add(this.txtAge);
this.Controls.Add(this.txtName);
this.Controls.Add(this.txtEmail);
this.Controls.Add(this.txtId);
this.Controls.Add(this.label1);
this.Controls.Add(this.button1);
this.Controls.Add(this.dataGridView1);
this.Name = "Page1";
this.Size = new System.Drawing.Size(1296, 430);
this.Load += new System.EventHandler(this.Page1_Load);
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Wisej.Web.DataGridView dataGridView1;
private Wisej.Web.Button button1;
private Wisej.Web.Label label1;
private Wisej.Web.TextBox txtId;
private Wisej.Web.TextBox txtEmail;
private Wisej.Web.TextBox txtName;
private Wisej.Web.TextBox txtAge;
}
}
Look between the tags #region Wisej Designer generated code
and #endregion
to find the code that was generated by the designer. You will notice a pattern- there are 3 commented lines, followed by a series of lines of code that set the properties of a Wisej control.
For example, here is how the button was set up:
private Wisej.Web.Button button1;
The variable button1
is declared
this.button1 = new Wisej.Web.Button();
A new button object is created
//
// button1
//
this.button1.Location = new System.Drawing.Point(740, 298);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(146, 37);
this.button1.TabIndex = 1;
this.button1.Text = "Add Student";
this.button1.Click += new System.EventHandler(this.button1_Click);
The button's properties- Location, Name, Size, TabIndex and Text are set. this.button1.Click += new System.EventHandler(this.button1_Click);
assigns an event handler function (this.button1_Click
) to the "Click" event of the button1
control. Thus, whenever the Click event fires, the function button1_Click()
will be called. button1_Click()
can be found in Page1.cs - it is part of the controller.
Page1.cs serves as the controller in this sample.
The "Controller" is merely a bridge between the Model and the View.
If you have to format data to later display it in your View, that should happen in the Controller. For example, if your model stored a string as "Example_Text_Here", and you wanted the View to display it as "Example Text Here", it would be the controller's job to replace the underscores with spaces. In this sample, our data is pretty simple, so we don't format it.
public partial class Page1 : Page
{
List<StudentModel> studentdata = new List<StudentModel>();
public Page1()
{
InitializeComponent();
}
/// Load data from the database on initial page load.
private void Page1_Load(object sender, System.EventArgs e)
{
studentdata = StudentModel.GetStudents();
dataGridView1.DataSource = studentdata;
}
/// Add a new student to the database using the values from the text fields.
private void button1_Click(object sender, System.EventArgs e)
{
//check to make sure that all the text fields have text (ie they are not the empty string)
if (txtId.Text != "" && txtEmail.Text != "" && txtName.Text != "" && txtAge.Text != "")
{
//read the data from the text fields in the view
int id = Int32.Parse(txtId.Text);
string email = txtEmail.Text;
string name = txtName.Text;
int age = Int32.Parse(txtAge.Text);
// create a StudentModel based on the data in the text fields.
StudentModel model = new StudentModel() { Name = name, Id = id, Age = age, Email = email };
// Attempt to add the student to the database- show a sucess or failure message.
string message = model.AddStudent();
AlertBox.Show(message);
//clear the textboxes
txtId.Text = "";
txtEmail.Text = "";
txtName.Text = "";
txtAge.Text = "";
//Get the data from the database and show it in the view so that the new student is seen
studentdata = StudentModel.GetStudents();
dataGridView1.DataSource = studentdata;
}
else //if at least one of the text fields is blank
{
AlertBox.Show("Please enter values for all fields.");
}
}
}
The button1_Click()
event handler contains code for adding a new student to the database. The user enters data in the textboxes, and then clicks this button. A new student is added to the database, based on the values that were entered in the textboxes in the view. This is an excellent example of how the controller connects the View to the Model+database. Let's break it down:
We first check to make sure that all the textboxes have text. This ensures that a value will be sent in for each field of the model.
if (txtId.Text != "" && txtEmail.Text != "" && txtName.Text != "" && txtAge.Text != "")
We then read the data from each textbox, and store it in a variable. Note that the view contains some very generic data validation in order to make this code run without errors. For example, the user is only able to enter numbers in the "id" textbox- they cannot enter words. Validation that is specific to the model- ie checking that the id is between 1 and 999- is done by the model.
int id = Int32.Parse(txtId.Text);
string email = txtEmail.Text;
string name = txtName.Text;
int age = Int32.Parse(txtAge.Text);
Next, a StudentModel
is created based on the data from the text fields. The AddStudent() method is called, which checks that the data meets the validation requirements in the student model. (In this case, the requirements are that the id is between 1 and 999, the id is unique, and that the email address is formatted correctly. A message is shown- it either indicates that the data was sucessfully added to the database, or it gives an explanation of why the data was invalid.
// create a StudentModel based on the data in the text fields.
StudentModel model = new StudentModel() { Name = name, Id = id, Age = age, Email = email };
// Attempt to add the student to the database- show a sucess or failure message.
string message = model.AddStudent();
AlertBox.Show(message);
Finally, the textboxes are cleared.
//clear the textboxes
txtId.Text = "";
txtEmail.Text = "";
txtName.Text = "";
txtAge.Text = "";
We see this code snippet twice- it runs when the page (the View) is initially loaded, and after a new student is added to the database. This code ensures that the View and the database are in sync- the same information that the user sees in the view is what is stored in the database.
studentdata = StudentModel.GetStudents();
dataGridView1.DataSource = studentdata;
In the context of a Wisej.NET application, the View is generally a Page or a Form. The controller is responsible for determining which view will be shown. There can be multiple views for each controller. An application can also have multiple controllers- in this case, each controller would most likely have its own view. Note that this sample only has one view and one controller.
The code-behind is the code that connects the View to the Controller. With a Wisej.NET project, the code-behind would be located in Page1.cs, and you would have a separate Controller class (ie Controller.cs). Ideally, the code-behind is as small as possible- it's job is to call Controller class functions at the appropriate times (ie in event handlers, when switching views). This sample does not have a code-behind because Page1.cs is the controller.
Model -Has all validation logic that enforces business rules on the data -Responsible for retrieving data from and storing data in the database. View -Can be created using drag-and-drop controls in the Wisej designer -The User Interface of the application -In Wisej.NET applications, generally a Page or a Form Controller -Connects the Model and the View -Contains event handlers -Formats data from the model before it is displayed in the View -Responsible for syncing the View and the database
by Gabriele del Giovine, translated by Julie Hirt
In this chapter, we explore the structure of an application for beginners that presents concepts and functional characteristics typical of line-of-business (LOB) applications. The application includes login management, access to databases, and the corresponding presentation of data, as well as user interface management functions with search and reporting mechanisms. "Wisej Pubs Demo App" constitutes a simple starting point in code structure and at the same time introduces the necessary development concepts to tackle the world of web-based LOB. You can find the online demo here: https://wisejdemo.delgiovine.it/WisejPubsDemoApp, while the original source code can be found at https://github.com/gdelgiovine/WisejPubsDemoApp. A forked copy of the source code which has been improved in order to make it easier to run on a new machine can be found here: https://github.com/JulieHirt/WisejPubsDemoApp.
The application implements the management of a slightly modified version of the sample database used in Microsoft SQL Server in its early days, the famous DB Pubs, which is the database of a book distribution organization. The application uses a mix of the Web Application template and the Web Desktop Application template. In terms of the role distribution patterns/methods, such as the popular MVC (Model, View, Controller) pattern, this application, due to the need for conceptual simplicity, uses a much simplified approach that involves a Model and ViewControllers (the classic forms of Windows Forms). In reality, there is nothing preventing you from organizing your projects according to preferred patterns, and Wisej.NET, unlike many typical development environments for LOB applications, does not restrict the developer to rigid architectural models.
Regarding data management, I employ a basic type of object-relational mapping (ORM) that I have written myself, which almost completely hides the programmer from the challenges of database access and management. This ORM, called BasicDAL, integrates with Wisej.NET in data presentation functions, as well as in search and reporting functions by integrating "SAP Crystal Reports for Visual Studio" (required to run the application). BasicDAL and the extensions for Wisej 3.0 are always available at https://github.com/gdelgiovine. I chose to use this ORM precisely because it is extremely concise and relatively simple compared to more powerful tools, which tend to be much more complex.
Below are some screenshots of the running application.
Below is the structure of the Visual Studio project as shown in the "Solution Explorer" section.
The "Default.json" file contains the full name of the method for starting the application- WisejPubsDemoApp.Program.Main
- which indicates a namespace of WisejPubsDemoApp
containing the Program
class and the Main()
method.
{
"url": "Default.html",
"startup": "WisejPubsDemoApp.Program.Main, WisejPubsDemoApp"
}
To see the code that runs when the application starts, we can open the "Program.cs" file, which contains the Main()
method:
In the Main()
method, we find the creation of an object of the frmLogin
class and the invocation of its Show()
method. It's worth noting that the prefix "frm" is used to identify all the Form
objects that will be used in the application to manage user interaction.
frmLogin
objectThe frmLogin
object is an instance of the Wisej.Web.Form
class. It implements the functionality that prompts the user to enter a username and password, verifies their correctness, and, in case of success, passes control of the flow to another object, this time of type Wisej.Web.Desktop
. Let's take a look at the interesting elements of frmLogin
.
When viewed in the designer, you can see that the frmLogin
object is a Form
with two textboxes and a button.
Shown below is the code for the frmLogin
class:
//frmLogin.cs
using System;
using Wisej.Web;
namespace WisejPubsDemoApp
{
public partial class frmLogin : Form
{
public AppConfig appConfig = new AppConfig();
System.Collections.Generic.List<WebAppUser> _WebAppUsers;
public frmLogin()
{
InitializeComponent();
// create users list - this is only for demo purpose NEVER use in a real application
_WebAppUsers = new System.Collections.Generic.List<WebAppUser>();
_WebAppUsers.Add(new WebAppUser () { UserName = "User1" , Password = "Password1" });
_WebAppUsers.Add(new WebAppUser() { UserName = "User2", Password = "Password2" });
_WebAppUsers.Add(new WebAppUser() { UserName = "User3", Password = "Password3" });
}
private void btn_Login_Click(object sender, EventArgs e)
{
if (!IsValidLogin(ref this.appConfig.CurrentWebAppUser))
{
MessageBox.Show("UserName or Password not valid!");
}
else
{
PubsDesktop pubsDesktop = new PubsDesktop();
pubsDesktop.appConfig = this.appConfig;
pubsDesktop.Show();
this.Close();
}
}
private bool IsValidLogin(ref WebAppUser LoggedInWebAppUser)
{
LoggedInWebAppUser = new WebAppUser() ;
WebAppUser UserToCheck = new WebAppUser();
UserToCheck.UserName = this.txt_UserName.Text.Trim().ToLower();
UserToCheck.Password = this.txt_Password.Text;
bool UserOk = false;
foreach (WebAppUser _WebAppUser in _WebAppUsers)
{
if (_WebAppUser.UserName.ToLower() == UserToCheck .UserName)
{
if (_WebAppUser.Password == UserToCheck.Password)
{
LoggedInWebAppUser.UserName = _WebAppUser.UserName;
LoggedInWebAppUser.Password = _WebAppUser.Password ;
this.appConfig.CurrentWebAppUser = LoggedInWebAppUser;
UserOk = true;
break;
}
}
}
return UserOk;
}
}
public class WebAppUser
{
public string UserName;
public string Password;
public WebAppUser()
{
}
}
}
When examining the "frmLogin.cs" code, note the use of a provider class called AppConfig
. This class contains all the configuration objects and operational parameters of the application. Each class that is capable of receiving an instance of AppConfig
has a public attribute called appConfig
.
public AppConfig appConfig = new AppConfig();
AppConfig
is propagated to all classes that need such information- frmLogin
, frmTitles
, frmStores
, frmSales
, frmRoySched
, frmPubsInfo
, frmPublishers
, frmJobs
, frmEmployee
, frmDiscounts
, and PubsDesktop
. Shown below is the code where the instance of AppConfig
is passed from frmLogin
to PubsDesktop
:
//frmLogin.cs
PubsDesktop pubsDesktop = new PubsDesktop();
pubsDesktop.appConfig = this.appConfig;
The AppConfig
class contains an instance of the WebAppUser
class within it, which will hold the username and password of the user who has successfully authenticated through the private method IsValidLogin()
. If the user is authenticated correctly, the instance of AppConfig
is passed to the PubDesktop
object, which represents the Web Desktop that will host all the other forms of the application.
Please note that the authentication section mentioned here is for a demo application, and it's crucial NOT to hardcode usernames and their corresponding passwords in actual programs. Regarding authentication, Wisej.NET can utilize "anonymous" authentication or Windows authentication (in the case of applications developed for the .NET Framework). You can choose the authentication types and enable SSL usage in the Project's Properties.
AppConfig
objectIn the"AppConfig.cs" flie,you will find the class responsible for retrieving some of the configuration parameters from the "Web.Config" file and passing the application's configuration to various components. Let's analyze it in detail.
//AppConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WisejPubsDemoApp
{
public class AppConfig
{
public WebAppUser CurrentWebAppUser = null;
public BasicDAL.DbConfig DbConfig;
public bool IsPublicDemo = false;
public string PublicDemoMessage = "";
public AppConfig()
{
DbConfig = new BasicDAL.DbConfig();
}
public Boolean ReadWebConfigAppConfig()
{
bool _ok = false;
if (System.Configuration.ConfigurationManager.AppSettings.Count > 0)
{
try
{
this.IsPublicDemo = Convert.ToBoolean (System.Configuration.ConfigurationManager
.AppSettings["IsPublicDemo"]);
this.PublicDemoMessage = System.Configuration.ConfigurationManager
.AppSettings["PublicDemoMessage"];
this.DbConfig.ServerName = System.Configuration.ConfigurationManager
.AppSettings["DbConfigServerName"];
this.DbConfig.DataBaseName = System.Configuration.ConfigurationManager
.AppSettings["DbConfigDataBaseName"];
this.DbConfig.UserName = System.Configuration.ConfigurationManager
.AppSettings["DbConfigUserName"];
this.DbConfig.Password = System.Configuration.ConfigurationManager
.AppSettings["DbConfigPassword"];
this.DbConfig.AuthenticationMode = Convert.ToInt32(
System.Configuration.ConfigurationManager
.AppSettings["DbConfigAuthenticationMode"]
);
// AuthenticationMode = 0 (Zero) means DB Native Authentication
// AuthenticationMode = 1 (One) means Windows Authentication
_ok = true;
}
catch (Exception)
{
Wisej.Web.MessageBox.Show("Error reading Web.Config");
_ok = false;
}
}
return _ok;
}
}
}
The AppConfig
class contains two objects: CurrentWebAppUser
of type WebAppUser
and DbConfig
of type BasicDAL.DbConfig
. The latter object is part of the ORM (Object-Relational Mapping) library "BasicDAL" and, specifically, is responsible for managing the connection context to an ADO.NET data source. To function correctly, it requires certain mandatory parameters such as the DB Server name, database name, authentication mechanism used, and optionally the username and password to access the database if our database server doesn't support Windows authentication. All of these parameters are used to generate the database connection string. In our implementation, these parameters are stored in the Web.Config file and are read by the ReadWebConfigAppConfig
function. Below is the relevant snippet from Web.Config.
<appSettings>
<add key="Wisej.LicenseKey" value=""/>
<add key="Wisej.DefaultTheme" value="Bootstrap-4"/>
<add key="IsPublicDemo" value="false"/>
<add key="PublicDemoMessage" value="Sorry! This is a Public Demo. Add or Delete is not allowed!"/>
<add key="DbConfigServerName" value="(local)"/>
<add key="DbConfigDataBaseName" value="pubs"/>
<!--DbConfigAuthenticationMode 0 = Native DB Auth (username/password) 1 = Windows Integrated Auth. -->
<add key="DbConfigAuthenticationMode" value="0"/>
<!-- DON'T DO THIS IN REAL WORLD!!! ALWAYS CRYPT VALUE THAT CONTAINS SENSIBLE DATA-->
<add key="DbConfigUserName" value="WisejPubs"/>
<add key="DbConfigPassword" value="pubs"/>
<!-- DON'T DO THIS IN REAL WORLD-->
</appSettings>
This class serves two fundamental functions:
Completing Configuration Retrieval: It completes the retrieval of configuration settings, such as those related to database access.
Menu Management: It implements the management of the menu that allows the selection of interactive application functions available to the authenticated user.
Scrolling through the class code, we can observe, highlighted in yellow, some aspects that will be subject to further examination
PubDesktop
has a public instance of the AppConfig
object. As mentioned previously, the AppConfig
object has been passed from the Login window (frmLogin
), and it contains the CurrentWebAppUser
object populated with the username and password.
public AppConfig appConfig = new AppConfig();
The LoadAppConfig
method is invoked by the event handler for the Desktop's Load
event.
private void PubsDesktop_Load(object sender, EventArgs e)
{
this.LoadAppConfig();
this.txt_CurrentUser.Text = this.appConfig.CurrentWebAppUser.UserName;
}
The LoadAppConfig
method sets a portion of the BasicDAL.DbConfig
object related to the parameters of the Runtime UI environment in which the BasicDAL ORM will operate, specifically in the case of Wisej.NET.
private void LoadAppConfig()
{
this.appConfig.DbConfig.RuntimeUI = BasicDAL.RuntimeUI.Wisej;
this.appConfig.DbConfig.RedirectErrorsNotificationTo = new BasicDALWisejControls.BasicDALMessageBox();
this.appConfig.DbConfig.Provider = BasicDAL.Providers.SqlServer;
if (!this.appConfig.ReadWebConfigAppConfig())
{
MessageBox.Show("Error reading Web.Config parameters!");
Application.Exit();
}
}
This enables interactive handling of error situations and their corresponding messages, which will be redirected to the client's browser using a native Wisej.NET MessageBox
object passed through the RedirectErrorsNotificationTo
property.
this.appConfig.DbConfig.RedirectErrorsNotificationTo = new BasicDALWisejControls.BasicDALMessageBox();
Additionally, the type of database server being used, Microsoft SQL Server, is set via the Providers
property.
this.appConfig.DbConfig.Provider = BasicDAL.Providers.SqlServer;
The configuration operation continues by reading the data present in the "Web.Config" file using the ReadWebConfigAppConfig
method.
if (!this.appConfig.ReadWebConfigAppConfig())
{
MessageBox.Show("Error reading Web.Config parameters!");
Application.Exit();
}
Once this is completed, the TextBox containing the current user's name is populated, and the application waits for the user to select an item from the mnuBar1
menu. The selection is handled by the ManageMenuBar
method.
ManageMenuBar(e.MenuItem.Name);
ManageMenuBar()
receives the name of the selected menu item and, through a switch block, activates a new instance of the chosen Form. A utility function from the BasicDAL module is invoked, which implements Wisej.NET user controls (BasicDALWisejControls). Then, the configuration is passed, and finally, the Show()
method is called.
private void ManageMenuBar(string MenuItemName)
{
switch (MenuItemName)
{
case "mnuFile_Exit":
break;
case "mnuDiscounts" :
frmDiscounts frmDiscounts = new frmDiscounts();
BasicDALWisejControls.Utilities.SetFixedWindowStyles(frmDiscounts);
frmDiscounts.appConfig = this.appConfig;
frmDiscounts.Show();
break;
case "mnuAuthors" :
frmAuthors frmAuthors = new frmAuthors();
BasicDALWisejControls.Utilities.SetFixedWindowStyles(frmAuthors);
frmAuthors.appConfig = this.appConfig;
frmAuthors.Show();
break;
//more cases- one for each form type
default:
break;
}
}
As mentioned in section 4.2, the application architecture follows a very simple architectural pattern, typical of the world of simple Windows Forms, using a Model-View approach where the View also implements some business logic controller functions. In the real world, this approach is usable for moderately complex applications when data management is delegated to an ORM (Object-Relational Mapping) that can handle almost all aspects of interaction with the database. In our case, the ORM BasicDAL is capable of performing this task, completely abstracting the database management from the developer and allowing the complete avoidance (if desired) of using SQL language within the application.
The following is a simple explanation of BasicDAL's structure in terms of constituent objects and main functionalities. BasicDAL is an ORM that, in addition to ORM functions, also incorporates data binding functions to presentation objects with properties that can be displayed in a UI. Its default operating pattern is that of the Active Record; however, in situations where the memory allocated by the record set is considered excessive or when it is not necessary to keep active records in memory, it is possible to switch to DataReader mode with a forward-only cursor.
The main objects are:
Database Connection Context
BasicDAL.DbConfig
Mapping of Database Objects (Tables, Views, Stored Procedures, Functions, SQL Statements) and Their Interaction (Read, Update, Insert, Delete, Select, Presentation)
BasicDAL.DbObject
Mapping Single Column
BasicDAL.DbColumn
Mapping Single Parameter
BasicDAL.DbParameter
Binding Between Column Values and Presentation Objects
BasicDAL.BoundControls
Creation of Query Language Statements Specific to the Database Provider Used by the Database Connection Context
BasicDAL.DbFilters
Hierarchy of main object compositions:
BasicDAL.DbObject
BasicDAL.DbColumn
BasicDAL.DbColumns
BasicDAL.DbObject
BasicDAL.DbParameter
BasicDAL.DbParameters
BasicDAL.DbObject
BasicDAL.DbFilters
BasicDAL.DbFIltersGroup
BasicDAL.DbColumn
BasicDAL.BoundControl
BasicDAL.BoundControls
In our application, the model is represented by the file "BasicDALPubsModel.cs." The model is made up of a set of objects, derived from the base class BasicDAL.DbObject
:
DbT_dbo_authors
DbT_dbo_discounts
DbT_dbo_employee
DbT_dbo_jobs
DbT_dbo_pub_info
DbT_dbo_publishers
DbT_dbo_roysched
DbT_dbo_salesmaster
DbT_dbo_salesdetails
DbT_dbo_stores
DbT_dbo_titleauthor
DbT_dbo_titles
DbV_dbo_titleview
DbSP_dbo_byroyalty
DbSP_dbo_reptq1
DbSP_dbo_reptq2
DbSP_dbo_reptq3
DbV_dbo_titles_publishers
DbV_dbo_AuthorsFullname
DbV_dbo_pub_info_publisher
The purpose of these objects is to map Database objects (Tables, Views, Stored Procedures, Functions). Let's take a look at the class DbT_dbo_authors
, which is related to a Table.
public class DbT_dbo_authors : BasicDAL.DbObject
{
private BasicDAL.DbColumn _DbC_au_id = new BasicDAL.DbColumn("[au_id]", System.Data.DbType.String, true, "");
private BasicDAL.DbColumn _DbC_au_lname = new BasicDAL.DbColumn("[au_lname]", System.Data.DbType.String, false, "LAST");
private BasicDAL.DbColumn _DbC_au_fname = new BasicDAL.DbColumn("[au_fname]", System.Data.DbType.String, false, "FIRST");
private BasicDAL.DbColumn _DbC_phone = new BasicDAL.DbColumn("[phone]", System.Data.DbType.String, false, "PHONE");
private BasicDAL.DbColumn _DbC_address = new BasicDAL.DbColumn("[address]", System.Data.DbType.String, false, "ADDRESS");
private BasicDAL.DbColumn _DbC_city = new BasicDAL.DbColumn("[city]", System.Data.DbType.String, false, "CITY");
private BasicDAL.DbColumn _DbC_state = new BasicDAL.DbColumn("[state]", System.Data.DbType.String, false, "ST");
private BasicDAL.DbColumn _DbC_zip = new BasicDAL.DbColumn("[zip]", System.Data.DbType.String, false, "ZIP");
private BasicDAL.DbColumn _DbC_contract = new BasicDAL.DbColumn("[contract]", System.Data.DbType.Boolean, false, false);
private BasicDAL.DbColumn _DbC_email = new BasicDAL.DbColumn("[email]", System.Data.DbType.String, false, "EMAIL");
public BasicDAL.DbColumn DbC_au_id
{
get { return _DbC_au_id; }
set { _DbC_au_id = value; }
}
public BasicDAL.DbColumn DbC_au_lname
{
get { return _DbC_au_lname; }
set { _DbC_au_lname = value; }
}
public BasicDAL.DbColumn DbC_au_fname
{
get { return _DbC_au_fname; }
set { _DbC_au_fname = value; }
}
public BasicDAL.DbColumn DbC_phone
{
get { return _DbC_phone; }
set { _DbC_phone = value; }
}
public BasicDAL.DbColumn DbC_address
{
get { return _DbC_address; }
set { _DbC_address = value; }
}
public BasicDAL.DbColumn DbC_city
{
get { return _DbC_city; }
set { _DbC_city = value; }
}
public BasicDAL.DbColumn DbC_state
{
get { return _DbC_state; }
set { _DbC_state = value; }
}
public BasicDAL.DbColumn DbC_zip
{
get { return _DbC_zip; }
set { _DbC_zip = value; }
}
public BasicDAL.DbColumn DbC_contract
{
get { return _DbC_contract; }
set { _DbC_contract = value; }
}
public BasicDAL.DbColumn DbC_email
{
get { return _DbC_email; }
set { _DbC_email = value; }
}
public DbT_dbo_authors()
{
this.InterfaceMode = BasicDAL.InterfaceModeEnum.Private;
this.DbObjectType = BasicDAL.DbObjectTypeEnum.Table;
this.DbTableName = "authors";
}
}
The DbT_dbo_authors
class derives from the base class BasicDAL.DbObject
and maps the "author" table. Each column is mapped with a BasicDAL.DbColumn
class field, which uses a part of the table name as its name. The constructor of BasicDAL.DbColumn
expects the following mandatory parameters: column name, column data type, primary key membership, default value.
In the previous code sample of the DbT_dbo_authors
class, the DbColumn
fields are declared as private and associated with a public property to access the customization of the Get and Set methods. If there is no need to access these methods, the class could be composed as follows:
public class DbT_dbo_authors_public : BasicDAL.DbObject
{
public BasicDAL.DbColumn _DbC_au_id = new BasicDAL.DbColumn("[au_id]", System.Data.DbType.String, true, "");
public BasicDAL.DbColumn _DbC_au_lname = new BasicDAL.DbColumn("[au_lname]", System.Data.DbType.String, false, "LAST");
public BasicDAL.DbColumn _DbC_au_fname = new BasicDAL.DbColumn("[au_fname]", System.Data.DbType.String, false, "FIRST");
public BasicDAL.DbColumn _DbC_phone = new BasicDAL.DbColumn("[phone]", System.Data.DbType.String, false, "PHONE");
public BasicDAL.DbColumn _DbC_address = new BasicDAL.DbColumn("[address]", System.Data.DbType.String, false, "ADDRESS");
public BasicDAL.DbColumn _DbC_city = new BasicDAL.DbColumn("[city]", System.Data.DbType.String, false, "CITY");
public BasicDAL.DbColumn _DbC_state = new BasicDAL.DbColumn("[state]", System.Data.DbType.String, false, "ST");
public BasicDAL.DbColumn _DbC_zip = new BasicDAL.DbColumn("[zip]", System.Data.DbType.String, false, "ZIP");
public BasicDAL.DbColumn _DbC_contract = new BasicDAL.DbColumn("[contract]", System.Data.DbType.Boolean, false, false);
public BasicDAL.DbColumn _DbC_email = new BasicDAL.DbColumn("[email]", System.Data.DbType.String, false, "EMAIL");
public DbT_dbo_authors_public()
{
this.InterfaceMode = BasicDAL.InterfaceModeEnum.Private;
this.DbObjectType = BasicDAL.DbObjectTypeEnum.Table;
this.DbTableName = "authors";
}
}
The BasicDAL.DbObject
and BasicDAL.DbColumn
objects have dozens and dozens of properties, fields, methods, and events. The complete documentation is available on the GitHub website at https://github.com/gdelgiovine.
BasicDAL also provides a tool for generating BasicDAL.DbObject
classes. The tool is called BasicDAL ClassBuilder and is available on the GitHub website.
Wisej.Web.Form
)In the application, forms are characterized by the suffix "frm" in their names and all share the same basic structure, derived from a template. Therefore, we will only analyze some forms that implement interesting functionality. To do this, we will examine frmSales
and frmAuthors
, which offer some interesting insights.
fmSales
In Wisej.NET, forms are preferably designed using a drawing surface (designer) without the need to manually write HTML or CSHTML code, which is necessary for ASP.NET MVC, ASP.NET Core, Angular, or React applications. This is the designer for the frmSales
form:
As seen, the form in the design phase closely resembles its runtime appearance.
The files that make up the form look like this in the "Solution Explorer." Note that this is the same structure that we saw in section 3.4.2 for the "Page1.cs" file.
frmSales
The vast majority of the code found in the two files implementing the frmSales
class, namely "frmSales.cs" and "frmSales.Designer.cs", is located in the latter file. The code in the "frmSale.Designer.cs" file is typically generated and managed automatically by the Wisej.NET designer. The code present in the "frmSales.cs" file is kept to a minimum and primarily consists of declarations of objects related to the BasicDAL ORM and UI object event handlers. Due to the use of a simplified version of the Model-ViewController pattern, the frmSales.cs file also contains fragments of business logic. It's important to emphasize that Wisej.NET does not impose any specific architectural pattern.
Let's begin by looking at the fields declared in frmSales
. We'll talk about what values they are assigned.
//the fields of FrmSales
public partial class frmSales : Form
{
public AppConfig appConfig = new AppConfig();
private BasicDAL.DbConfig DbConfig = new BasicDAL.DbConfig();
private BasicDALWisejControls.BasicDALMessageBox BasicDALMessageBox = new BasicDALWisejControls.BasicDALMessageBox();
private DbT_dbo_salesdetails dbT_Dbo_SalesDetails = new DbT_dbo_salesdetails();
private DbT_dbo_titles dbT_Dbo_Titles = new DbT_dbo_titles();
private DbT_dbo_stores dbT_Dbo_Stores = new DbT_dbo_stores();
private DbT_dbo_salesmaster dbT_Dbo_SalesMaster = new DbT_dbo_salesmaster();
private BasicDAL.DbLookUp dbl_stor_id = new BasicDAL.DbLookUp();
private bool FormInit = false;
private bool DataNavigatorHandlerInitialized = false;
//...
}
Looking specifically at the first two fields in frmSales, appConfig
and DbConfig
, we see the following:
// frmSales.cs
public AppConfig appConfig = new AppConfig();
private BasicDAL.DbConfig DbConfig = new BasicDAL.DbConfig();
appConfig
receives the complete application configuration from the PubDesktop
object:
// PubsDesktop.cs
frmSales frmSales = new frmSales();
//...
frmSales.appConfig = this.appConfig;
frmSales.Show();
DbConfig
is populated with a copy of the BasicDAL.DbConfig database context present in the AppConfig
object:
//code from InitDataContext() in frmSales.cs
this.DbConfig = this.appConfig.DbConfig.Clone();
Next, let's look at the fields dbT_dbo_salesdetails
, dbT_dbo_titles
, dbT_dbo_stores
, and dbT_dbo_salesmaster
:
// frmSales.cs
private BasicDALWisejControls.BasicDALMessageBox BasicDALMessageBox = new BasicDALWisejControls.BasicDALMessageBox();
private DbT_dbo_salesdetails dbT_Dbo_SalesDetails = new DbT_dbo_salesdetails();
private DbT_dbo_titles dbT_Dbo_Titles = new DbT_dbo_titles();
private DbT_dbo_stores dbT_Dbo_Stores = new DbT_dbo_stores();
private DbT_dbo_salesmaster dbT_Dbo_SalesMaster = new DbT_dbo_salesmaster();
The classes DbT_dbo_salesdetails
, DbT_dbo_titles
, DbT_dbo_stores
, and DbT_dbo_salesmaster
are declared in "BasicDALPubsModel.cs" (see section 4.7.2). All of these classes derive from BasicDAL.DbObject
. They are the entities that map the corresponding Table objects in the Pubs database. (For example, DbT_dbo_titles
maps to the 'titles' table).
Next, observe the declaration and instantiation of dbl_stor_id
, which is a BasicDAL.DbLookUp
object:
// frmSales.cs
private BasicDAL.DbLookUp dbl_stor_id = new BasicDAL.DbLookUp();
This object implements a LookUp function towards a BasicDAL.DbObject
by adding BasicDAL.DbFilters
to its BasicDAL.DbFilters
collection and associating UI controls for data binding through its BasicDAL.BoundControls
collection.
// InitDataContext() in frmSales.cs
// Declare of DbLookUp
this.dbl_stor_id.DbObject = this.dbT_Dbo_Stores;
this.dbl_stor_id.Filters.AddBoundControl(this.dbT_Dbo_Stores.DbC_stor_id,
BasicDAL.ComparisionOperator.Equal,
this.txt_stor_id,
"text");
this.dbl_stor_id.BoundControls.Add(this.dbT_Dbo_Stores.DbC_stor_name,this.lbl_stor_name, "text");
The InitDataNavigatorHandler()
method is responsible for creating and associating event handlers for the BasicDALWisejControl.DataNavigator
object used in the form.
//frmSales.cs
private void InitDataNavigatorHandler()
{
// Declare Events Handlers for DataNavigator
if (DataNavigatorHandlerInitialized == true)
{
return;
}
this.dataNavigator1.eAddNew -= new BasicDALWisejControls.DataNavigator.eAddNewEventHandler(this.dataNavigator1_eAddNew);
this.dataNavigator1.eAddNew += new BasicDALWisejControls.DataNavigator.eAddNewEventHandler(this.dataNavigator1_eAddNew);
this.dataNavigator1.ePrint -= new BasicDALWisejControls.DataNavigator.ePrintEventHandler(this.dataNavigator1_ePrint);
this.dataNavigator1.ePrint += new BasicDALWisejControls.DataNavigator.ePrintEventHandler(this.dataNavigator1_ePrint);
this.dataNavigator1.eDelete -= new BasicDALWisejControls.DataNavigator.eDeleteEventHandler(this.dataNavigator1_eDelete);
this.dataNavigator1.eDelete += new BasicDALWisejControls.DataNavigator.eDeleteEventHandler(this.dataNavigator1_eDelete);
this.dataNavigator1.eRefresh -= new BasicDALWisejControls.DataNavigator.eRefreshEventHandler(this.dataNavigator1_eRefresh);
this.dataNavigator1.eRefresh += new BasicDALWisejControls.DataNavigator.eRefreshEventHandler(this.dataNavigator1_eRefresh);
this.dataNavigator1.eClose -= new BasicDALWisejControls.DataNavigator.eCloseEventHandler(this.dataNavigator1_eClose);
this.dataNavigator1.eClose += new BasicDALWisejControls.DataNavigator.eCloseEventHandler(this.dataNavigator1_eClose);
this.dataNavigator1.eFind -= new BasicDALWisejControls.DataNavigator.eFindEventHandler(this.dataNavigator1_eFind);
this.dataNavigator1.eFind += new BasicDALWisejControls.DataNavigator.eFindEventHandler(this.dataNavigator1_eFind);
this.dataNavigator1.eSave -= new BasicDALWisejControls.DataNavigator.eSaveEventHandler(this.dataNavigator1_eSave);
this.dataNavigator1.eSave += new BasicDALWisejControls.DataNavigator.eSaveEventHandler(this.dataNavigator1_eSave);
this.dataNavigator1.eMovePrevious -= new BasicDALWisejControls.DataNavigator.eMovePreviousEventHandler(this.dataNavigator1_eMovePrevious);
this.dataNavigator1.eMovePrevious += new BasicDALWisejControls.DataNavigator.eMovePreviousEventHandler(this.dataNavigator1_eMovePrevious);
this.dataNavigator1.eMoveFirst -= new BasicDALWisejControls.DataNavigator.eMoveFirstEventHandler(this.dataNavigator1_eMoveFirst);
this.dataNavigator1.eMoveFirst += new BasicDALWisejControls.DataNavigator.eMoveFirstEventHandler(this.dataNavigator1_eMoveFirst);
this.dataNavigator1.eMoveLast -= new BasicDALWisejControls.DataNavigator.eMoveLastEventHandler(this.dataNavigator1_eMoveLast);
this.dataNavigator1.eMoveLast += new BasicDALWisejControls.DataNavigator.eMoveLastEventHandler(this.dataNavigator1_eMoveLast);
this.dataNavigator1.eMoveNext -= new BasicDALWisejControls.DataNavigator.eMoveNextEventHandler(this.dataNavigator1_eMoveNext);
this.dataNavigator1.eMoveNext += new BasicDALWisejControls.DataNavigator.eMoveNextEventHandler(this.dataNavigator1_eMoveNext);
this.dataNavigator1.eUndo -= new BasicDALWisejControls.DataNavigator.eUndoEventHandler(this.dataNavigator1_eUndo);
this.dataNavigator1.eUndo += new BasicDALWisejControls.DataNavigator.eUndoEventHandler(this.dataNavigator1_eUndo);
DataNavigatorHandlerInitialized = true;
}
The frmSales_Load()
event invokes the InitDataContext()
method. Therefore, the InitDataContext()
method runs when the frmSales
form is initially loaded.
//frmSales.cs
private void InitDataContext(bool ForceFormInit = false)
{
if (ForceFormInit == true)
{
FormInit = false;
}
if (this.FormInit)
{
return;
}
this.DbConfig = this.appConfig.DbConfig.Clone();
this.DbConfig.RedirectErrorsNotificationTo = this.BasicDALMessageBox;
this.dbT_Dbo_SalesDetails.Init(this.DbConfig);
this.dbT_Dbo_Stores.Init(this.DbConfig);
this.dbT_Dbo_Titles.Init(this.DbConfig);
this.dbT_Dbo_SalesMaster.Init(this.DbConfig);
this.dbT_Dbo_SalesMaster.DataBinding = BasicDAL.DataBoundControlsBehaviour.BasicDALDataBinding;
//binding for BasicDAL DbObject columns into UI objects.
this.dbT_Dbo_SalesMaster.DbC_ord_date.BoundControls.Add(this.dtp_ord_date, "value");
this.dbT_Dbo_SalesMaster.DbC_ord_num.BoundControls.Add(this.txt_ord_num, "text");
this.dbT_Dbo_SalesMaster.DbC_stor_id.BoundControls.Add(this.txt_stor_id, "text");
this.dbT_Dbo_SalesMaster.DbC_stor_ord_date.BoundControls.Add(this.dtp_stor_ord_date, "value");
this.dbT_Dbo_SalesMaster.DbC_stor_ord_num.BoundControls.Add(this.txt_stor_ord_num, "text");
this.dbT_Dbo_SalesMaster.DbC_payterms.BoundControls.Add(this.cmb_payterms, "text");
// Declare of DbLookUp
this.dbl_stor_id.DbObject = this.dbT_Dbo_Stores;
this.dbl_stor_id.Filters.AddBoundControl(this.dbT_Dbo_Stores.DbC_stor_id,
BasicDAL.ComparisionOperator.Equal,
this.txt_stor_id,
"text");
this.dbl_stor_id.BoundControls.Add(this.dbT_Dbo_Stores.DbC_stor_name,this.lbl_stor_name, "text");
this.dataNavigator1.DbObject = this.dbT_Dbo_SalesMaster;
this.dataNavigator1.ManageNavigation = false;
this.dataNavigator1.DataGrid = this.dgv_SalesDetails;
this.dataNavigator1.DataGridActive = false;
this.dataNavigator1.DataGridListView = this.dgvList;
this.dataNavigator1.ListViewColumns.Add(this.dbT_Dbo_SalesMaster.DbC_ord_num , "Order Number", "");
this.dataNavigator1.ListViewColumns.Add(this.dbT_Dbo_SalesMaster.DbC_ord_date, "Order Date", "dd/MM/yyyy");
this.dataNavigator1.ListViewColumns.Add(this.dbT_Dbo_SalesMaster.DbC_stor_id , "Store Id", "");
this.dataNavigator1.DataNavigatorInit(true);
}
The connection context to the local database DbConfig
is cloned from the one present in the received configuration class. (this.DbConfig = this.appConfig.DbConfig.Clone();
)This cloning operation allows for having a context separate from the one received, enabling independent transaction handling within your code without affecting operations on other objects not involved in the transaction. The use of connection context cloning is an additional option available to the developer.
Next, the message window used by the ORM is then redirected to report any unhandled exceptions to the user.
By using the .Init() method of the various DbObject entities that map database objects, they are associated with the specific BasicDAL.DbConfig connection context of the form. All BasicDAL.DbObject entities associated with the context are available in the BasicDAL.DbConfig.ManagedDbObjects collection.
By default, BasicDAL.DbObject objects do not perform data binding for the Value property of BasicDAL.DbColumn objects that map database columns. The binding mode is set/read through the DataBinding
property, which can have these values:
// BasicDAL.cs
public enum DataBoundControlsBehaviour
{
WindowsFormsDataBinding = 2,
BasicDALDataBinding = 1,
NoDataBinding = 0
}
In our frmSales
form, the only BasicDAL.DbObject object that requires data binding to multiple user interface controls is the instance dbT_Dbo_SalesMaster
of the DbT_dbo_salesmaster
class, which represents the sales master table. For each of the BasicDAL.DbColumn objects, their corresponding UI controls are added to their own BasicDAL.BoundControls collection with an indication of whether they are meant to be used for setting or reading the user-entered values. Data binding can be set to write-only to the control, read from the control, read from the control only during the creation of a new data row, or a combination of these values. The direction of data binding is managed by the BindingBehaviour property of the BoundControl object.
Here's an example:
this.dbT_Dbo_SalesMaster.DbC_ord_num.BoundControls.Add(this.txt_ord_num, "text");
this.txt_ord_num
refers to the control that contains the order number in the frmSales
form. In this context, "text" is the name of the property from which values are read (using the control's property's getter to update DbColumn.Value
) or to which values are written (using the control's property's setter with the value of the DbColumn.Value
property).
Next, we proceed to populate the dbl_stor_id
instance by associating it with the dbT_Dbo_Stores
object, which is the DbObject representing the database object that we will use as a data source for performing a lookup. It is necessary to provide the value or values for filtering that will be entered by the user in various controls. This is done by creating a filter based on a BoundControl
. In the BoundControl
, you specify the BasicDAL.DbColumn of the table for the lookup query, the comparison operator, the UI control, and the name of the property of this control from which to retrieve the value.
this.dbl_stor_id.Filters.AddBoundControl(this.dbT_Dbo_Stores.DbC_stor_id,
BasicDAL.ComparisionOperator.Equal,
this.txt_stor_id,
"text");
by Jon Hilton
In this article, we get up to speed with Wisej.NET and explore how it addresses the specific challenges of building Line-of-Business, Enterprise Web Applications.
Blazor is a modern, component-based framework for building websites and applications using .NET, and for many projects, it's a great choice.
But it's not the only option.
Alongside Blazor (including Microsoft's other options like MVC and Razor Pages), we have alternatives, one of which is Wisej.NET.
Wisej.NET is a web framework designed to make it faster and easier to build business web applications. If you’re new to Wisej.NET, you can download the community version here.
It promises a flat learning curve, WYSIWYG designer, full integration with Visual Studio, and an option to bring in external components (from vendors such as SyncFusion and Telerik) so you can focus on building your app and meeting your customers’ requirements.
At first glance, you might imagine Wisej.NET is a straight-up Blazor alternative, solving similar problems and enabling you to build your web applications using .NET.
But in practice, Wisej.NET addresses a particular challenge, building large enterprise web applications, and does so in a way that merits a closer look.
So join me as I get up to speed with Wisej.NET and explore how it addresses the specific challenges of building Line-of-Business, Enterprise Web Applications.
Before we get started, it's probably worth defining what Enterprise Web Applications are in this context and what they need to be able to do.
For this comparison, I'm assuming LOB Enterprise applications manage large amounts of data, where data needs to be entered, reviewed, and visualized.
If you're thinking forms (for data entry) and screens displaying lots of data in various formats, you're probably on the right track!
The key factors we'll explore in this comparison are:
How the two frameworks scale (specifically, how they can handle thousands or even millions of rows of data)
How do the frameworks handle lost connections and browser refreshes (is state preserved?)
The steps involved in handling standard LOB application requirements, such as a modal dialog updating a field on the page
If you've done any work with Blazor, you'll have seen the standard 'new project' boilerplate UI (the one with the Counter and Fetch Data examples)
Let's start by attempting to recreate those examples using Wisej.NET, beginning with the Weather Data example.
After running through an installation process to add Wisej.NET to Visual Studio, the first step is to create a new project.
There are a several options here, but I opted to stick with Wisej.NET 3 Web Page Application as this seemed the closest to a standard Blazor web application (with the notion of pages and the ability to add components to those pages).
Once the project has been initialized, you are presented with a rather blank project, including a file named Page1.cs.
Opening that brings up the Wisej.NET WYSIWYG designer. If you've ever worked on a WebForms or XAML application, some aspects of this are going to feel very familiar.
To build the UI, we can drag and drop controls from the Toolbox onto the page.
For this demo, I'll bring in a Navigation Bar and Flex Layout Panel.
The Navigation Bar will go on the left, and I'll put the Flex Layout Panel on the right.
I've also added a single item to the Navigation Bar (which will link to the Fetch Data/Weather Forecasts component).
Note the WeatherForecastPanel
Tag value. We'll need that in a moment.
From here, we need a way to show different 'components' (or panels) when one of the items in the Navigation Bar is clicked.
I'll implement that by wiring up an event handler to the Navigation Bar's SelectedItemChanged
event, which takes the Tag name from the selected item and uses it to locate (and activate) the relevant 'panel.'
Here's the relevant event for the designer.
Now we need some code to locate the relevant 'panel' and activate it (so it shows up in the Flex Layout Panel I added earlier).
private void navigationBar_SelectedItemChanged(object sender, System.EventArgs e)
{
var panelName = navigationBar1.SelectedItem.Tag;
this.flexLayoutPanel1.Controls.Clear();
var panel = (Control)Activator.CreateInstance(Type.GetType($"Wisej.NETWebPageApplication1.Panels.{panelName}"));
panel.Dock = DockStyle.Fill;
panel.Parent = this.flexLayoutPanel1;
}
Although we're only just getting started, this simple demo is beginning to highlight some of the key differences between Blazor and Wisej.NET.
For comparison, the standard Blazor new project template includes an HTML div
with HTML anchor elements for navigating to the various 'page' in the app.
Blazor operates as a SPA (Single Page Application), so it will intercept those clicks (when you attempt to navigate between pages), locate the component for the page you're attempting to access, then load that component and render it in the browser.
We're doing something similar with Wisej.NET, but there's a key philosophical difference.
Wisej.NET is designed for building web applications (not websites). The focus is on maintaining a consistent state and context around the user, what they're working on, what data they've entered, and which stage they've reached as they work through repeatable business processes.
Therefore the focus is less on navigating between pages on a site but rather on showing the user the suitable screens at the right time while keeping track of the context around how they ended up there.
This makes sense in the context of a web application. You probably don't want your users jumping directly to a part of the UI just because they happen to have the URL for it. In a LOB app, you likely need them to follow the correct process to arrive at that screen!
Back to our demo, we need to create that WeatherForecastPanel we referenced earlier (see earlier note about remembering the tag name).
For this we can add a User Control (via the standard Add > New Item command in Visual Studio).
At this point we could jump off and create more 'panels' but let's have a go at implementing this Weather Data demo first.
I'll start by adding a DataGridView
to the designer.
Then I can use the WeatherForecastService
and WeatherForecast
classes from the Blazor demo for inspiration and implement slightly modified versions for Wisej.NET.
Wisej.NET\WeatherForecastService
using System;
using System.ComponentModel;
using System.Threading.Tasks;
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public Task<BindingList<WeatherForecast>> GetForecastAsync(DateTime startDate, int count = 5)
{
var rand = new Random();
var list = new BindingList<WeatherForecast>();
for (var i = 0; i < count; i++)
{
list.Add(new WeatherForecast
{
Date = startDate.AddDays(i),
TemperatureC = rand.Next(-20, 55),
Summary = Summaries[rand.Next(Summaries.Length)]
});
}
return Task.FromResult(list);
}
}
This code generates random weather data (so we've got something to show in our UI).
Note that the GetForecastAsync
method is modified from the original Blazor version to return an instance of BindingList
, which comes from the System.ComponentModel
namespace provides some 'out of the box' functionality for notifying UI controls of changes to the data (so we can easily bind controls such as data grids).
WeatherForecast
itself is a plain old C# class:
public class WeatherForecast
{
[DisplayName("Date")]
public DateTime Date { get; set; }
[DisplayName("Temp. (C)")]
public int TemperatureC { get; set; }
[DisplayName("Temp. (F)")]
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
[DisplayName("Summary")]
public string Summary { get; set; }
[DisplayName("Temp. (K)")]
public double TemperatureK => TemperatureC + 273.15;
}
With these classes in place, we can set them to drive our datagrid component.
Wisej.NET\WeatherForecastPanel.cs
public class WeatherForecast
{
[DisplayName("Date")]
public DateTime Date { get; set; }
[DisplayName("Temp. (C)")]
public int TemperatureC { get; set; }
[DisplayName("Temp. (F)")]
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
[DisplayName("Summary")]
public string Summary { get; set; }
[DisplayName("Temp. (K)")]
public double TemperatureK => TemperatureC + 273.15;
}
WeatherForecastPanel_Load
is wired up to this user control's Load
event (so it will attempt to load this data when the WeatherForecastPanel is activated).
All that's left is to invoke WeatherForecastService.GetForecastAsync
to load 25 records (for now) and bind them to the data grid's data source property.
At this point, some differences start to emerge between the two frameworks (Wisej.NET vs Blazor).
First, we've discovered that Wisej.NET requires less manual work to create this sort of UI (a grid showing data).
Creating the equivalent UI Blazor requires us to either:
Handcraft the HTML and CSS to render our weather data and navigation bar
Use components from a third-party vendor
Choosing frameworks naturally involves comparing trade-offs (and what makes sense for your specific situation). The trade-offs here seem to be:
With Wisej.NET, you give up direct control over what gets rendered in the browser (because you're not handcrafting the HTML and will end up with whatever HTML Wisej.NET creates for you)
You gain development speed (it's quicker to bind data to the data grid than it is to write the HTML and CSS from scratch)
You get basic grid functionality (like sorting, selecting rows) 'out of the box' (you'd have to implement that yourself with Blazor)
Now, what if we need more than 25 records, say 1000 or even 1,000,000?
To test this, we can add some UI to select the number of records to load. For this, I'll add a Combobox to my Wisej.NET example, with some hardcoded values to choose from and two buttons: one to load the weather data and one to clear it.
In code, I can read the selected value and use it to load the requested number of records when the Load Weather button is clicked.
Wisej.NET\WeatherForecastPanel.cs
C#
private async void LoadData()
{
var count = 25;
int.TryParse(comboBox1.Text, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out count);
this.dataGridView1.DataSource = await new WeatherForecastService().GetForecastAsync(DateTime.Now, count);
this.dataGridView1.Columns["Date"].DefaultCellStyle.Format = "d";
this.dataGridView1.Columns["TemperatureK"].DefaultCellStyle.Format = "N3";
}
private void button1_Click(object sender, EventArgs e)
{
LoadData();
}
When we run this now, Wisej.NET handles the larger numbers of records with minimal fuss.
Switching between 1,000 and 100,000 records is pretty instant, and loading in 1,000,000 records is rapid too.
The Wisej.NET DataGrid provides sort functionality (toggled by clicking the column names) and the ability to select specific rows.
You can also toggle the visibility of specific columns.
Switching to Blazor, we can use a @foreach
loop to iterate over the data, rendering table rows for each record.
Blazor\FetchData.razor
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
We can then implement UI to select the number of records using an HTML select
element.
Blazor\FetchData.razor
<!-- existing markup -->
<select @onchange="CountChanged">
<option>10</option>
<option>10,000</option>
<option>100,000</option>
<option>1,000,000</option>
</select>
@code {
protected async Task CountChanged(ChangeEventArgs e)
{
int.TryParse(e.Value.ToString(),
System.Globalization.NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out var count);
await LoadData(count);
}
protected async Task LoadData(int count = 0)
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now, count);
}
}
Now, if we try to load 1,000,000 records and display them using this foreach
loop, we'll run into trouble.
While testing, I found the browser eventually gives up and asks if you want to keep waiting for the page to respond.
The Blazor way around this problem is to switch to the dedicated Virtualize
component instead.
Using Virtualize
, we ensure that only the visible records render, which enables the page to remain responsive as we throw more records at the table.
We should probably fix the height of the table, too (so we can scroll through records in the table without scrolling the entire page).
Here's the markup and CSS we can use to employ virtualization and also fix the table’s height.
Blazor\FetchData.razor
<div class="weatherPanel">
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
<Virtualize Items="@forecasts" Context="forecast">
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
</Virtualize>
</tbody>
</table>
</div>
.table {
height: 100%;
}
.weatherPanel {
height: 32em;
display: block;
overflow: scroll;
}
Here it is in action:
One thing to notice here is the visible 'lag' as we scroll through the records, as Blazor re-renders the new data in the existing HTML elements.
From here, and to better match the Wisej.NET version, we'd need to implement sorting, the ability to select a row by clicking it, and fix the table headers (so they don't disappear when you scroll down), and implement the ability to show and hide columns.
In effect, the Wisej.NET version is ready to go (thanks to the built-in functionality of the DataGrid).
We'd need to spend more time working on the Blazor equivalent to bring it in line (if we wanted additional functionality like sorting and highlighting selected records).
In any web scenario, there's always the risk of losing connection or users accidentally refreshing (or navigating away from) the current page.
This can be a significant issue, especially if your users are in the middle of a multi-step process (think multi-stage wizards or forms with multiple 'pages'), and end up losing their progress because they lost connection or inadvertently navigated away from the application.
This is one area where the two frameworks diverge quite notably.
Starting with our Wisej.NET weather data page, if I select the option to display 1 million records, let them appear in the Data Grid, then hit refresh, the page loads again exactly as it was before.
We're still on the same screen, with 1,000,000 selected in the combobox and the same one million weather records loaded in the data grid.
Before and after refreshing the page (Wisej.NET):
Indeed, this holds true if we've sorted the grid or toggled the visibility of specific columns, with all that 'state' preserved on refresh.
While Blazor Server does attempt to preserve some application state (more on this shortly), in practice, if we hit refresh in our Blazor equivalent, we stay on the same page (with the same components), but our selections are lost, and the table lies bare.
Before Refresh
After Refresh
In this regard, Wisej.NET carries an advantage for LOB applications, automatically providing a stateful 'session' for your users without requiring you to implement state tracking (persistence and the ability to restore that state) yourself or via third-party libraries.
This proves particularly useful in applications where you want to provide a 'desktop like' experience, with your users opening up multiple 'windows' or forms, which can be moved around, maximized, minimized, closed, etc.
Wisej.NET makes this possible while also remembering the state of the UI as your users navigate around the application.
Here's another example: I've added a few panels to the screen at design time and marked them as movable (so the users can move them within the app itself).
After Initial Load
With the app running in the browser it's possible to drag those windows around.
After moving the panels around, the panels keep their (new) positions when we refresh the page.
Before and After Refresh (after I moved the panels in the Browser)
This gets more interesting when we consider common LOB tasks like filling in forms, with a partially completed form state being preserved by Wisej.NET if you refresh the app or navigate away from the form.
The reason Wisej.NET can restore the UI on refresh lies in the way it handles sessions.
Not to be confused with authentication or user sessions, Wisej.NET sessions are designed to provide a desktop app-like experience but via the web.
Think of it as akin to running an instance of a desktop application. From the minute you launch the app to the moment you close it (or reboot your machine), that running instance of the application, can keep track of your actions, which windows you have visible (or hidden), which parts of the UI you're currently focusing on.
Wisej.NET sessions adopt a similar approach but run via the web. When you access the application a new session is started. At this point, you can go away, come back, refresh, and that session remains active.
The sessions keep track of the current state of the application, including the UI model (which windows are visible/open, data that's been entered via the user, which models are currently awaiting a response, etc.)
To understand how Blazor handles the 'session' state, we need to differentiate between Blazor's two hosting models.
Blazor WASM, which runs entirely in the browser, is akin to a JavaScript Single Page Application (think React or Angular). With Blazor WASM, there is no built-in session state persistence.
Say, for example, a user is part way through completing a multi-page form in a Blazor WASM application. Should their connection be interrupted (they accidentally navigate away from the page or finish work for the day and want to return to the form tomorrow), Blazor WASM won't attempt to persist in any state relating to their progress.
Blazor Server is slightly different. It uses circuits to attempt to keep track of the app's current state (for each user/connection).
Component state (which components are visible, what data is currently being displayed) is stored on the server. As you interact with a Blazor server app, those interactions are sent to the server. Blazor then sends messages to the browser (via a socket connection) instructing it to update the UI (DOM) in response to the changing component state.
In general, Blazor Server will attempt to keep a user connected to the same circuit.
The idea being, if the connection is temporarily interrupted, the user will end up reconnected to the same circuit (so they don't lose any information they've entered so far).
In practice, this is not 100% reliable; Blazor may connect you to a different circuit for a number of reasons, including:
The server kills the original circuit because it finds itself under memory pressure
Your app is served via multiple servers in a load-balanced configuration. If an individual server fails (or is removed when no longer required), the original server used for a user's connection may become unavailable when the user attempts to reconnect
If you refresh, not only will you connect to a new circuit, but you'll also lose any state held in the browser's memory (for example, if you've set values via JavaScript interop).
It is technically possible to persist state across circuits, but you are required to develop your own solution to preserve the application state somewhere other than the server's memory (a database, for example).
It's also worth noting that you aren't able to persist the UI state in this scenario.
For example, whilst you could write code to store the values a user has entered into a form, if they were halfway through a form and had been presented with a modal, you wouldn't be able to put them back on that form, with the modal open, in the event they ended up switching to another circuit.
Conversely, Wisej.NET would keep track of that state,and put you back on the same 'screen' with the same open modal.
This seems like a good time to explore Wisej.NET's approach to managing the connection between client and server.
When you launch your app and access it via the browser, Wisej.NET maintains two representations of your application's UI model: one on the client (browser) and one on the server.
This is seamless from the user's perspective but opens up some interesting possibilities for development as you can write JavaScript code that interacts with your application's controls in the browser, and Wisej.NET will simultaneously keep the related server-side counterparts in sync.
The upshot is that you can use JavaScript to traverse through your application's UI model (starting with the top-level App
) and interact with elements directly.
Let's take that multi-panel example from earlier.
We can traverse to the first panel (the imaginatively named 'Panel1') and invoke the hide
method to make the control disappear.
When we invoke this method (via JavaScript), Wisej.NET will hide the panel in the browser and ensure the server-side instance of the panel is updated accordingly.
To test this, I wired up an event handler to the panel's Disappear
event (in the C# code):
MultiWindow.cs
private void panel1_Disappear(object sender, EventArgs e)
{
// this method was invoked
}
Then ran the following command (via the JavaScript console):
App.Page1.flexLayoutPanel1.MultiWindow.panel1.hide()
The panel disappeared and the corresponding panel1_Disappear
event was invoked on the server:
This is a useful feature, not least because it means you can write JavaScript code without fear of the server-side model drifting out of sync, and/or inconsistent business rules being applied (any interactions triggered in the browser will be handled, correctly, on the server).
There's no direct equivalent in Blazor. The closest we have is the ability to interact with JavaScript via JS Interop. This requires manual 'plumbing' and implementing specific methods (in the Blazor components) to make them invokable via JavaScript.
Before we tackle another common business application requirement (how to show modal dialogs), let's take a step back and consider the humble Counter demo.
Counter is likely the first demo you see when you spin up a Blazor project:
When you click the button the counter increments.
To recreate this using Wisej.NET, we can create a simple page (or form, user control, etc.) with a button, a label, and a little code to update the value of the counter when the button is clicked.
public partial class Counter : FlexLayoutPanel
{
int value = 0;
public Counter()
{
InitializeComponent();
this.label1.Text = value.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
value++;
this.label1.Text = value.ToString();
}
}
In the UI, I have two labels (one to show the value and another to show the text next to the value), as well as the button.
This demo, while simple, demonstrates a key difference between the two frameworks: Blazor tends to adopt a declarative approach, whereas Wisej.NET (in general) skews towards an imperative approach (depending on the specific use case).
Here's the Blazor counter equivalent:
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Note how this relies on binding (for example, where we're displaying the current value of @currentCount
in our markup). If the bound value is updated the UI element reacts accordingly (and the new value is displayed).
With Wisej.NET, we're more likely to take an imperative approach, whereby we issue commands to update the UI directly.
this.label1.Text = value.ToString();
It is possible to bind certain Wisej.NET controls to data (so that, if the data source changes, the control reflects the updated data). In fact, we saw an example of that in the weather data demo. But in general, your Wisej.NET forms will take a more imperative approach.
There are trade-offs to consider with either approach:
Imperative code can be easier to write, especially when modeling business logic that is logically sequential (show a dialog, prompt the user for extra data, validate the provided data, etc)
The declarative approach makes it easier to identify which fields affect the UI when inspecting the UI (because the bindings are visible in the markup)
The inherent decoupling of a declarative approach (the state is changed in one place, and the UI reacts automatically) can make it harder to track down where important logic is located, especially if the changes are made in code that is located some distance away from the UI
You may be more used to one style over the other (in which case you're likely to be more productive using the one with which you're already familiar)
One common requirement in LOB/Enterprise web applications is to present or request information via a Modal.
For certain business processes, it's important to block any further progress until a Modal is acknowledged and/or required information provided.
Wisej.NET has built-in support for suspending execution on the server whilst awaiting a response from the user (when presented with a Modal dialog).
For example, a delete button (say, to delete a customer).
It's possible to show a message box in response to that button isclicked, which shows a confirmation dialog and prevents any other code from being executed until the user has made their choice.
private void btnDelete_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
{
this.btnDelete.Enabled = false;
this.btnDelete.Text = "Deleted";
}
}
When a user clicks that Delete button, they'll have to choose Yes or No to proceed.
But what if we want to request more information before proceeding?
For that. we can use a custom form and display it as a modal.
Here, EnterPassword
is a form
(a type of Wisej.NET container which you can add to your application).
Wisej.NET/EnterPassword (Form)
public partial class EnterPassword : Form
{
public string Password { get; set; }
public EnterPassword()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.Password = txtPassword.Text;
Close();
}
private void button2_Click(object sender, EventArgs e)
{
Close();
}
}
Its job is to request a password from the user and store it in the Password
property.
It's not visible in the code, but I also set the DialogResult
for each button.
This ensures that when we show this 'form' as a dialog (modal), we'll get a dialog result (which we can then use to drive the relevant business logic).
From here, we're able to show this form as a dialog (modal), take the entered password, and use it to drive business logic.
Wisej.NET/ModalPage.cs
private void btnDelete_Click(object sender, EventArgs e)
{
using (var dialog = new EnterPassword())
{
// show the `EnterPassword` form as a modal (dialog)
var result = dialog.ShowDialog();
if (result == DialogResult.OK)
{
// grab the entered password
var password = dialog.Password;
// here we could check the password,
// and delete the user if password OK
this.btnDelete.Enabled = false;
this.btnDelete.Text = "Deleted";
}
}
}
In this case, we display the modal, then access the entered password (via the public Password
property).
When we run this in the browser, clicking the Delete button shows the EnterPassword
form as a dialog (modal), at which point we are required to close the dialog before proceeding (either by entering a password and clicking Continue or by clicking the Cancel button).
Refresh Friendly
Because Wisej.NET preserves the current UI state of the application (for any given 'session'), the state of the modal is also restored on refresh.
If we've clicked a button and a modal has appeared, server execution is paused.
We can then refresh the page, and the modal will still be open.
We're able to continue (by clicking one of the buttons), at which point execution will resume and, the relevant business logic will be executed.
This 'state persistence' means we could implement complex business processes, including multiple modals (potentially showing different options based on user input), safe in the knowledge that progress through those processes would be preserved, even if the connection was temporarily lost.
Blazor doesn't ship with any built-in Modal functionality, so our options here are to:
Implement a Modal popup ourselves (by creating an overlay to sit in front of the page, and an HTML element to act as the modal window)
Use a third-party library such as Blazored Modal
If we're going to match the Wisej.NET functionality in Blazor (using Blazored.Modal) we need to:
Add a reference to the Blazored.Modal Nuget package
Reference the Blazored.Modal JavaScript and CSS files
Register the IModalService
Set up something called a Cascading Parameter to make the IModalService available to our components
Once that (one-time) setup completes, we can set about displaying modals in our Blazor components.
Here's the Blazored Modal equivalent of our Delete Confirmation example.
<button @onclick="@(()=>DeleteCustomer(customer.Id))">Delete</button>
@code {
private Customer customer = new Customer { Id = 1 };
[CascadingParameter] public IModalService Modal { get; set; }
async Task DeleteCustomer(int customerId)
{
var modal = Modal.Show<ConfirmPassword>("Enter Password to proceed");
var result = await modal.Result;
if (result.Cancelled)
{
// Modal was cancelled
}
else
{
// Modal was closed
var password = result.Data;
}
}
}
We're making use of the IModalService
cascading parameter to display another component (EnterPassword
) and await the result.
ConfirmPassword
case is another Blazor component.
ConfirmPassword.razor
<label>
Enter your password to continue
<input type="password" @bind-value="password" @bind-value:event="oninput"/>
</label>
<button @onclick="Continue">Continue</button>
@code {
[CascadingParameter] BlazoredModalInstance ModalInstance { get; set; }
string password;
private async Task Continue()
{
await ModalInstance.CloseAsync(ModalResult.Ok(password));
}
}
Note in this example, ConfirmPassword
requires the BlazoredModalInstance
cascading parameter to directly close the model.
Unlike the Wisej.NET example. If you refresh whilst the modal is open, any existing 'progress' will be lost, and you'll have to start over.
Some observations about the two approaches:
As with the data grid example, you have more direct control of the appearance of your modals with Blazor (because you are writing the HTML and CSS yourself)
On the flip side, with Blazor, you need to write that HTML and CSS yourself, which consumes more development time (and you're somewhat on you or your team's CSS skills)
Blazor depends on third-party packages to support modals (or you can implement your own)
Wisej.NET has built-in Modal functionality (no need to turn to a third party) and gives you the ability to style your modals as you see fit (via the WYSIWYG editor and the ability to set various properties which control appearance)
Wisej.NET has built-in state tracking, which can withstand lost (or 'flaky') connections
Wisej.NET's approach makes it possible to implement complex business logic where (multiple) Modals are required as part of the process. You can implement a multi-step process, where modals are shown at various points, and the server will remember where you are up to in that process, even on a refresh of the application
On that last point, here's an (admittedly contrived) example of a multi-stage process using Wisej.NET's ability to show message boxes with modals at various stages.
private void btnDelete_Click(object sender, EventArgs e)
{
using (var dialog = new EnterPassword())
{
var result = dialog.ShowDialog();
if (result == DialogResult.OK)
{
// grab the entered password
var password = dialog.Password;
// here we could check the password,
// and only continue if it's OK
if (MessageBox.Show("Are you sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
{
if (MessageBox.Show("Are you really sure?", buttons: MessageBoxButtons.YesNo) == DialogResult.Yes)
{
// delete the customer then…
this.btnDelete.Enabled = false;
this.btnDelete.Text = "Deleted";
}
}
}
}
}
Clearly there's room for some refactoring here but note how you could be part way through this process, refresh, and seamlessly resume from where you left off.
Finally, on our whistle-stop tour of Wisej.NET, let's look at implementing a dynamic UI, whereby we can add controls to the UI at runtime.
Let's say we want to implement some sort of dynamic dashboard with widgets that our users can add or remove.
With Wisej.NET, we can add any control to a container programmatically.
For example, here, I'm adding the simple Counter
demo (see above) to a FlowLayoutPanel
.
container.Controls.Add(new Counter());
The Wisej.NET FlowLayoutPanel
(and other panels in Wisej.NET) have a super handy Tools collection, whereby you can add icons to the panel's toolbar.
Here's an example:
To test this further, I've added an 'Add' button to this FlowLayoutPanel
.
From there, we can write a little code to handle a tool being clicked, then take action accordingly.
private void panel1_ToolClick(object sender, ToolClickEventArgs e)
{
var container = (Panel)sender;
switch (e.Tool.Name)
{
case "Add":
container.Controls.Add(new Counter());
break;
default:
break;
}
}
Now, when we click that button, we get new instances of Counter
being added to the panel.
Each instance is independent, meaning we can increment each counter separately.
As with everything we've seen so far this state is preserved on refresh (so you can add multiple counters, increment them all separately, refresh and they'll still be there, showing their respective counts).
We can also pass data to the control. For example, let's say we want to pass an initial count to our counters. We can use a standard C# approach here, creating a constructor which accepts an initial count:
Counter.cs
public Counter(int initialCount = 0)
{
InitializeComponent();
this.value = initialCount;
this.label1.Text = value.ToString();
}
Then use that constructor when we add a counter to our panel.
container.Controls.Add(new Counter(100));
Blazor (as of .NET 6) has a specialized DynamicComponent
which you can use to render other components.
<DynamicComponent Type="Counter"/>
This declarative approach means we can also render a number of dynamic components by looping over a list.
@code {
readonly List<Type> _components = new List<Type>
{
typeof(WidgetA),
typeof(WidgetB)
};
}
@foreach(var component in _components){
<DynamicComponent Type="@component"/>
}
In this example, we've created a list of component types, then looped through each, using DynamicComponent
to render the relevant component.
To add a new component to the screen, we'd need to add them to the _components
list, at which point Blazor would re-render and display the newly added component.
We can pass data to a dynamically rendered Blazor component using a dictionary (of string, object).
private Dictionary<string, object> exampleParameters
= new Dictionary<string, object>
{
["InitialCount"] = 100
};
Which we can then forward to the dynamic component as Parameters
:
<DynamicComponent Type="Counter" Parameters="exampleParameters"/>
Overall, there's a little more abstraction with the Blazor approach (compared to Wisej.NET, where we can more directly create controls, including parameters/data, and add them to a container).
This was my first time exploring Wisej.NET, and my overall feeling is that it tackles some common LOB/Enterprise application requirements in a clean, scalable way.
If you're building an application where state matters (such as the current visibility of controls, partially completed form data, business processes which may run over a period of time) Wisej.NET offers a lot of functionality out of the box.
We've only really scratched the surface in this article, but for me, the notable features of Wisej.NET are:
Built-in state (application and UI) tracking
Pre-built controls (for common LOB requirements like showing data via grids)
Scalable architecture (can handle thousands of connections)
A unique client/server approach where controls are instantiated in the browser, and on the server, with interactions in either place reflected in the other
An accessible migration path for existing WebForms/WinForms applications
There are other factors we didn't get to, including theme management and the ability to use third-party controls from component vendors, both of which Wisej.NET provides.
Having worked on some WebForms (and indeed WinForms) web applications in the past, the approach to building 'forms' with Wisej.NET is very familiar. If you're currently maintaining a WebForms (or WinForms) application, I can see how Wisej.NET would be a very natural migration.
Switching to Blazor, I think Blazor works well for websites and applications where you want (or need) precise control over the HTML and CSS. The UI model for Blazor is based on standards-compliant HTML and CSS. Of course, this also means you have to write everything yourself or turn to third-party vendors.
Blazor is largely unopinionated (with respect to UI controls), so you'll generally need to invest time/effort into building the UI for common tasks like displaying modals or turn to a third-party solution.
This is especially true for some of the common LOB/Enterprise application requirements we've explored here. In practice, Blazor is a general-purpose web application framework with many potential use cases. As a result, you're likely to spend more time on plumbing and creating bespoke solutions (or using third-party libraries) with Blazor for these common requirements.
Ultimately any technology/framework choice comes down to trade-offs and choosing the best tool for the job.
If you're looking to build (or migrate) a LOB app, I can certainly recommend giving Wisej.NET a go, if only to establish for yourself whether it solves the key challenges you're looking to address.
You can download the free community version of Wisej.NET here.