Porting Desktop Apps to Web Apps
From Xojo Documentation
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.
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 (TabPanel, for example) and not all the features of the desktop controls are available in web controls (such as ListBox 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 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.
In desktop apps, you can modify the style of a control by changing properties on it for color, font, etc. With web apps, you instead create a WebStyle that has the settings you want and you apply it to the control using its Style property.
This has the benefit of allowing you to use the same style for controls throughout your web app. If you then need to change something in the style (say a font size), you can do that in one place (the Style itself) and the change will take effect anywhere that the style is used in the web app.
Also keep in mind that you can inherit from web styles allow you to create a new style based on the settings of a parent style.
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.
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.
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.
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:
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:
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.Open 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.
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 UserGuide:Sharing Code.
You’ll need to be careful not to create circular references up and down the Session structure. For instance, Session hierarchy looks like this:
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 WeakRefs.
This simple example uses a class to open a new web page or a new window, depending on the type of app.
Create a new desktop project and add a new class, called Navigator. In it, add a method called ShowScreen2 with this code:
In Window1, add a Button and put this code in its Action event handler:
Now add a new Window (it should default to Window2 as the name). You should give this window a title that says “Window 2” so you know when it has opened. Run the project and click the button on the default window to see that Window2 opens.
Now create a new web project. Add a Button to WebPage1 and add the same code to its Action event handler:
Now copy the Navigator class from the desktop project to this web project.
Lastly, add a second web page, called WebPage2. Give it a title so you know when it displays. Run the project and click the button on the default web page to see that Web Page 2 appears.
You have now created a (very simple) class that can be used in either a desktop or web app. This technique can apply to just about anything. Code that refers to web-specific objects or features should be included in “#If TargetWeb” and code that is for desktop apps should use “#If TargetDesktop”.
Although you are using the same Navigator class, you are not actually sharing the exact same class between the two projects. Changes made to the Navigator in one project do not affect it in the other project. If you want to share the exact same class so that a change in one project is reflected in another, then you need to use an External Project Item as described in UserGuide:Sharing Code.