Porting desktop apps to web apps

To create a web version of an existing desktop app, there are several things to consider. Obviously, you cannot use your project exactly as it is as desktop apps and web apps are different project types. Since you cannot change the type of a project, you will need to create a new web app project in order to create a web app.

But with an appropriate design and understanding of the UI differences, you will find that you can create a web app easily, re-using (or even sharing) a significant amount of non-UI code.

User interface

../../_images/porting_desktop_apps_to_web_apps_eddie's_electronics_desktop_window_layout.png

The user interface for a web app is completely different than the user interface for a desktop app. Not all of the desktop controls have equivalent web controls (DesktopGroupBox, for example) and not all the features of the desktop controls are available in web controls (such as DesktopListBox vs WebListBox). There are also web controls that do not have an equivalent desktop control (for example, WebMapViewer).

In addition, web apps do not have a concept of a Menu Bar, which is something that almost every desktop app uses, so you'll want to re-think how you present this information to the user.

Because of these differences, you are going to need to completely re-implement your existing desktop user interface using web app controls. Keeping your UI and business logic code separate can make this transition easier.

Web pages replace windows

Generally speaking, each window in a desktop app can be designed as a web page in a web app. You use the Show method to display different pages based on user actions, similar to how you might show additional windows. Keep in mind that a web app can only show a single page at one time. If your desktop app relied on having multiple visible windows, then you will need to rethink that design and come up with an alternative. In most cases your layouts should not change much.

Dialogs

../../_images/porting_desktop_apps_to_web_apps_eddie's_electronics_web_page_layout.png

Dialogs in desktop apps can use the MessageDialog class or be modal windows. In web apps, those options are not available. To create a dialog in a web app, you instead add a WebDialog to your project and add your layout to it. Then you add this web dialog to the page or pages on which it should appear and call it using Show where appropriate.

When the dialog is closed, its Dismissed event handler is called where you can determine what action to take.

Unlike desktop app dialogs, Web dialogs are not modal. After you call Show to display the dialog, the rest of the code in your method runs without waiting for the dialog to close.

Shown event handler

In desktop apps, you often use the Open event handler to do initial setup of your controls or windows. In web apps, you should instead use the Shown event handler.

Control appearance

In desktop apps, you can modify the style of a control by changing properties on it for color, font, etc. With web apps, you can do this as well. In some cases the control will have specific properties available and in others you will use the control's Style property. Web projects use Bootstrap themes to provide the overall appearance of controls.

Multiple users

One fundamental difference between desktop and web apps is that desktop apps are designed to be used by a single user at a time while web apps are designed to be used by many users at a time. Sessions and Cookies can help with this.

Latency

With a web app, your Xojo code runs on the server where the web app is installed and not in the user's web browser. This means that there is a delay, slight in some cases and more significant in others, between when an action is taken on the browser, when the Xojo code runs on the server and then when the result of that code appears back on the browser. It's what is called “round trip” and it depends on network traffic, how physically far a user is from your app and among other things, how long it takes for your app to respond. The result is that you 're going to have to think asynchronously when designing.

Sessions

Having to deal with multiple users means you may need to manage global data differently. In desktop apps, public properties and methods on the App object are global to the entire app, which can often be useful.

In a web app, the App is also global to the entire app, which means it is globally available to all users of the web app. This is usually not the behavior you want. Instead you want data to be specific to each user (aka Session) that is connected to the web app.

So you do not want to store global information that is specific to the current user, such as the UserName that was used to log in, in the App object. Instead, web apps have a concept called a WebSession. Each user that connects to your web app gets its own WebSession in the form of a Session object. Use the Session object to manage global information just for the user. For example, saving the UserName to a property on the Session means that it is only visible to the one user.

Cookies

Most apps need to save preferences or settings of some kind. With web apps, you can easily do this using Cookies, a web technology that provides a way for a web browser to save settings that can be later requested by the web app. Cookies are part of the WebSession. This code saves the user name in a Cookie:

Session.Cookies.Set("UserName") = UserNameField.Text

Log in

If you've saved a cookie containing the UserName as shown above you may want your web app to retrieve it so that it can pre-populate the UserName field on a login form. This code (in the Shown event handler for the page) fetches the saved Cookie and uses it to pre-fill a UserName field:

UserNameField.Text = Session.Cookies.Value("UserName")

Databases

Database desktop apps often keep a global reference to the database as a property of the App object. With a web app, you should instead use Session to store a property for the database reference. Each user that connects to the web app will then have their own connection to the database so that transactions work properly and so that you can isolate database access to prevent other users ' data from being visible.

For example, a desktop app may use the App.Open event handler to connect to the database once when the app starts and save the connection to a public DB property that is accessed throughout the app as App.DB. In a web app, you would instead do the connection in the Session.Opening event handler and save the connection to a public DB property that is accessed throughout the session as Session.DB.

For desktop apps, if you want to allow multiple users to access a database (using multiple installations of the desktop app), then you usually want to use a database server.

With web apps, you may find that you do not always need a database server. Because your web app is itself running as a server, SQLite is often more than sufficient for handling light to medium web app loads.

Code sharing

If you tend to keep most of your code in your user interface objects, then you will not be able to share your code between desktop and web projects. But if you instead separate your code out into classes that are called by your user interface objects, then you can start to share code between web and desktop projects.

This is sometimes referred to as Model-View-Controller design. The View is the user interface, either web or desktop. This cannot be shared. The Model is your data. This is shareable. The Controller is the interface between the Model and the View.

Using this design in conjunction with conditional compilation allows you to create shared code that works for both desktop and web apps.

For details on how to set up projects that share code, refer to Sharing Code among multiple projects.

Circular references

You 'll need to be careful not to create circular references up and down the Session structure. For instance, Session hierarchy looks like this:

  • Session

    • Pages

      • Controls

      • Dialogs

        • Controls

It's very important that the lower items don 't refer directly to the higher ones (like having a reference to Session in a WebPage). If you do, the Session and it's contents will never be destroyed, even when a user leaves your app. If you need such functionality, you 'll need to use a WeakRef.