Prior to Web 2.0, the following concept worked:
CheckBoxMTT → CheckboxCommon → Checkbox
CheckboxMTT → CheckboxCommon → WebCheckbox
to accomplish this, there were two files both called CheckboxCommon in two separate locations. One file called CheckboxMTT that would share code that could be used in both platforms.
The separate CheckboxCommon classes could be used to implement target specific items, and things in CheckboxMTT would do functionality that we wanted to implement across our projects and other things.
This no longer works on Web 2.0. When the second scenario is set up, the first one breaks and vice-versa. Note I had to manually add CheckboxMTT in the second project, it would not let me drag the file in.
So, all this being said, is there a way I can make this work or am I forced to duplicate code in both places and therefore not be able to share common code all the way up the chain?
This is the technique I use to make my apps cross platform, using the example of a WinLogin page for Desktop, Web and Mobile:
- Create three projects for the same app, but add ‘Web’ at the end of the web version and ‘Mobile’ at the end of the Mobile version.
- Create a window in each app called ‘WinLogin’. Add a button on each window named ‘OKButton’
- Create a Class called ‘ClassCommonWinLogin’ in one project. Make it external and copy the link to the other projects.
- In each WinLogin window create a Property called CommonWinLogin of type ClassCommonWinLogin
- In each WinLogin add an Opening Event that has the following code:
CommonWinLogin = New ClassCommonWinLogin
CommonWinLogin.myWinLogin = Self
- In the class ClassCommonWinLogin create a Method called doSetup. We’ll come back to this later.
- In the class ClassCommonWinLogin create a Property called myWinLogin of type WinLogin (to hold a reference to WinLogin). Then right-click on it and choose ‘Convert to Method Pair’. This will rename your Property as ‘mmyWinLogin’ and add a Method item for your window reference. I call it myWinLogin so I can still reference WinLogin directly if needed.
- In the class ClassCommonWinLogin create a Property called OKButton of type DesktopButton (to hold a reference to OKButton). Then right-click on it and choose ‘Convert to Method Pair’. This will rename your Property as ‘mOKButton’ and add a Method item for your button reference.
- Now rename mOKButton as mOKButtonDesktop. then duplicate it twice as mOKButtonWeb and mOKButtonMobile. Set the each to be only referenced by Desktop, Web or Mobile respectively, otherwise they’ll give a compile error.
- In the OKButton Method above, duplicate the Desktop version twice, but keep the same name OKButton. So you should have a control with one as Return mOKButtonDeskop and type DesktopButton, another as Return mOKButtonWeb and type WebButton and the third as Return mOKButtonMobile and type MobileButton. Again, set them to only apply to their respective OS’s.
- In the Assigns item of the OKButton Method, set the parameters to ‘Assigns Value As Object’. This allows us to return the correct Property of the three. Set the code in this Method to:
#If TargetDesktop Then
mOKButtonDesktop = DesktopButton(Value)
#ElseIf TargetWeb Then
mOKButtonWeb = WebButton(Value)
#ElseIf TargetiOS Then
mOKButtonMobile = MobileButton(Value)
- When doSetup runs, the Property myWinLogin should already be set, so to set the OKButton Property and its Event, we add the following code:
OKButton = myWinLogin.OKButton
AddHandler OKButton.Pressed, AddressOf OKButtonPressed
- Create a Method called OKButtonPressed with parameter ‘myObject as Object’ to receive the button reference as an Object when it is called. You can recast myObject in the Method if required, but it is usually unnecessary.
- Now this is done, add all your other objects to each window or containers and set them in doSetup. They will all be available everywhere in this cross-platform Class as if your code was running in the original windows and containers. You can create as many Methods as you need in this Class and just refer to each object by its name. You can create links to other window’s objects too.
Note: since setting the Properties and their respective environments is a real pain, I tend to duplicate existing Property/Method as sets to reduce errors. I have a set for every type of Object (buttons, textfields, popupmenus, etc) along with their common Event Methods.
I know this sounds complex, but it means 99.9% of all my code is in the one place per window or container, and it MUCH simpler in the long run.
I hope this helps!
Would Interfaces be simpler? I haven’t delved into this, but it allows your common methods to not have to know what kind of control they are. Control specific code goes into each subclass, but the bulk of your common code remains the same.
Not really, it’s just kind of insane, not complex.
I stopped reading it around bullet 5.
Anyone using any popular language simply does not get why someone would need something like that just to say “include shared/my_module_whatever.sourcecode” and use what’s defined there.
Try it yourself. Create an application interface across all three environments that execute the same logic and you’ll find that either you’re duplicating lots of code/events/properties or having lots of #If TargetDesktop/TargetWeb/TargetiOS statements at the top of every Method.
My technique has much more work up front since it moves all definitions, logic, properties, Methods and Events into the Class, but it means there is only one place to look for changes and bugs. I now make each change/fix once only, not three times. It has vastly decreased the size of my code base too.
Please, if you, or someone else, can come up with a better technique, I am all ears, but so far I have not seen one and I am ignorant of the use of Interfaces as suggested by @Tim_Hare in case someone else wants to show how this might work. I am happy to give an example of my technique so people can decide which they prefer.
Interface is designed to handle this specific problem. It allows you to address vastly different controls as if they were the same type. The code in your shared module doesn’t need to know the class of control, you pass it a parameter of
theControl as myInterface and it can call any method defined by that interface without needing to know the actual control type. That shields your business logic from the controls themselves. If your subclass of DesktopButton, WebButton and MobileButtton all implement myInterface, then the code in your module can be shared with all 3 project types without need for
#if Target anything.
Can you share the actual project with me if you have it available? Thanks.
Sure, here is a sample login window using this technique (for Desktop, Web, iOS and Android):
Thanks for the input. This are reasonable processes for new projects. However, we have a lot of existing projects that share controls outlined above that would take a lot of rework to get to run on Desktop and Web 2.0.
Here is a post from another forum entry:
[quote=416465:@Thom McGrath]Technically, you can by editing the project file. The fact that you cant is merely a UI thing. I believe the setting will be preserved, until you make changes from within the IDE. It just looks like
CompatibilityFlags = (TargetConsole and (Target32Bit or Target64Bit)) or (TargetWeb and (Target32Bit or Target64Bit)) or (TargetDesktop and (Target32Bit or Target64Bit)). So you could inject platform constants into it.
The only reason it wasnt included in the inspector in the first place was UI complexity.[/quote]
Understand though that editing the project files in this way is not supported, so if it screws up your project, it may not be retrievable.
(From Greg O’Lone)
So it is not recommended. However, it exactly corrects my problem, assuming I don’t try to modify it. It will compile and run without issues (at least that I have found so far) on both Web and Desktop. So is there a right way to accomplish the same thing?