Step 2 Creating A Main Function: #Include Int Int Return
Step 2 Creating A Main Function: #Include Int Int Return
We start by opening Visual Studio 2015 and select FileàNewàProject from the menu. We want an empty C++
project, so in the templates tree on the left, we select Visual C++àGeneral and select Empty Project in the right
pane; see Figure 1. We will name our project “Tutorial”.
Figure 1
When we click OK you’ll see that we’ve created an empty project! Well almost empty; In the Solution Explorer
you can still see three useless filters (Header Files, Resource Files and Source Files), go ahead and delete those.
Now let’s fill it with a standard WinMain function from Snippet 1. Finally press F5 and we should be able to
compile and run.
Snippet 1
#include <Windows.h>
Notice that the compiler says we’re compiling in Win32 mode. That’s not right; Applications in Philips are built in
x64, so we will need to change the configuration.
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 2 of 20
We have two platforms; Win32 and x64, but we only want one. Click on the combobox and click x64.
Now if we press F5 we should see that the compiler is compiling for x64 instead of Win32.
First let’s make includes work. Change the main.cpp so that it looks like Snippet 2;
Snippet 2
#include <Windows.h>
#include <Sense.h>
#include <DLS.h>
You’ll see that we’ve added the includes for Sense.h and DLS.h. We’ll discuss why there are two separate
includes later on, for now we just want it to compile; when you press F5 you’ll see that not surprisingly the
compiler can’t find the includes.
To fix this we’ll add the Sense and DLS packages from our nuget server in Best.
First navigate to the nuget package manager; in Visual Studio we can find this by going to Tools -> NuGet
Package Manager -> Manage NuGet Packages for Solution…
Next we need to add the NuGet server; click the settings icon in the top right corner. Under NuGet Package
Manager -> Package Sources, click the add button on the top right. Name is “Best Server” and put
“https://artifactory-ehv.ta.philips.com/artifactory/api/nuget/IGT-AppEngine” for the source. When done click
“Update” then “Ok”.
Now set your package source to your new server and navigate to browse.
Install Philips.Sense.DLS for your project, this will install both the DLS and Sense packages.
Next we will also make sure we can link Sense and DLS. Right-click your project and navigate to properties. We
need to change the runtime libraries used for both our Debug and Release configurations to ensure that we can
link to Sense successfully. Navigate to C/C++ -> Code Generation -> runtime Library. For the debug configuration
set this option to “Multi-threaded Debug (/MTd), and for release configuration set it to “Multi-threaded (/MT)”
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 3 of 20
Add the code Sense::Gpu(0).show(); to your WinMain function and try to compile. This time, your linker should
tell you that symbols are missing, which is correct of course.
When you now compile, everything should go fine. To finish up, remove the ugly Sense::Gpu(0).show(); line from
your code again (its purpose was only to test if you’re linking the Sense library correctly) and save your project.
We’re done!
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 4 of 20
#include <Windows.h>
#include <Sense.h>
#include <DLS.h>
Notice the two separate libraries; Sense and DLS. The difference between these libraries is that the Sense library
contains system-level components and behavior, whereas the DLS library contains only visualizations for
Controls, such as buttons. Were it needed to create a Vechian or other visual style, there would also be a
Vechian library.
Luckily the previous chapter already made sure we include the libraries we need, so we only need to declare
that we use their namespaces (which saves us a lot of typing). See Snippet 4 for the changes we made;
Snippet 4
#include <Windows.h>
#include <Sense.h>
#include <DLS.h>
l Gpu; as the name implies, the Gpu class controls a single Gpu. If you have more than one Gpu in your
system, you can also define more gpu instances in your application.
l Canvas; this is where you build your UI upon; like a painting, the canvas is the area that is drawable.
l Output; an output is a screen, a window or an offscreen buffer, connected to a specific Gpu. Outputs
usually show the whole canvas, but this is not required; outputs show a rectangular area of the canvas.
l User; User classes enable input for your application. The default user class is UserMouseKeyboard;
allowing input from the mouse and keyboard to be processed by Sense.
So let’s grab these components and make an application; see Snippet 5 for our new implementation of
WinMain;
Snippet 5
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
Scheduler scheduler;
Gpu gpu(0);
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 5 of 20
UserMouseKeyboard user(canvas);
gpu.show();
return scheduler.run();
}
First, notice that we give argument “0” to both the gpu and the output instances; gpus and screens are
numbered from zero. So if you have three Gpus, you will be able to create Gpu 0, 1 and 2. Likewise each Gpu has
a number of screens connected. For each Gpu the screen identifiers start at zero. So a system with two gpu’s
and 4 screens (evenly divided over the Gpus) will have Gpu:Screen 0:0, 0:1, 1:0 and 1:1. Thus, the code in
Snippet 5 says “create an Gpu-instance for the first gpu, and an OutputScreen instance for the first screen of
that gpu.
Second, notice that the output and user instances require a Canvas in their constructor. As said before, a canvas
can have multiple outputs (and users).
Third, notice that we set the size of the canvas to the same size as the output. By default, the output is
positioned at Point(0,0) so this way the dimensions of the canvas and output are matched. size is a property of
the canvas. This acts as a public variable of the class that can be set by instances of other classes. We’ll come
back to properties later on. For now all you need to know is that as a rule, properties can be set by users to
change the behavior of Sense objects.
Finally, you can see that we end with gpu.show() and immediately return after scheduler.run(). The show()
function causes the gpu instance to initialize the graphical resources and render its outputs for the first time.
You’ll usually put this command at the end, so that you have a fully build-up UI shown from the start. However,
to process animations and user input, we require the scheduler to begin its run-loop run. Hence the last call; it
will not return until the scheduler is exited.
You can run this application; you should see a black screen. However, since we haven’t defined a way to exist
the application we will need to kill it from the debugger (use alt-tab).
UserMouseKeyboard user(canvas);
Dialog dialog(canvas);
Button button(dialog);
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 6 of 20
dialog is placed upon the canvas, and the button is placed upon the dialog.
Finally, notice the logical naming of controls; a button has a Button as class, a dialog a Dialog etc. Most if not all
controls in Sense/DLS have very clear names.
If you try to run this, you’ll notice that you still see nothing. The reason for this is that we haven’t positioned our
dialog and button yet, and we’ve also failed to give them a size. By default, position and size of all controls is
zero. We fix this in Snippet 7;
Snippet 7
Dialog dialog(canvas);
dialog.size = Size(500,200);
dialog.position = Point(100,100);
Button button(dialog);
button.size = Size(120,46);
button.position = Point(190,60);
We’re almost there; we only need to set a few properties of the button and dialog and we’re done.
We can add unicode text to the button and dialog’s text and tooltip properties; see Snippet 8. Note that we use
an icon from the iconset of DLS; all icons in DLS are available through Icons, the same is true for Cursors and of
course the aforementioned Palette.
Snippet 8
dialog.text = L"Hello World!";
button.text = L"Exit";
button.image = Icons::Exit;
button.tooltip = L"Exits the application";
Now if you run the application everything looks good, but we still don’t have any functionality; our buttonclicks
don’t do anything.
To remedy this, we add a small lambda function that calls Scheduler::exit(), using the event of Button;
Snippet 9
button.eventClicked = [] { Scheduler::exit(); };
Events are function-pointers that are called when a certain action is performed (in this case when the button is
clicked). You can hook your own function to an event, and this is where lambda functions of C++ really shine; In
most cases where you have a class you can simply use [this] { callMyFunction(); } to call a normal function of
your class. The lambda function automatically takes vare that the right object is called.
We’re done; your application should like something like Figure 5 and your code like Snippet 10;
Figure 5
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 7 of 20
Snippet 10
#include <Windows.h>
#include <Sense.h>
#include <DLS.h>
output.color = Palette::Gray40;
canvas.size = output.getSize();
UserMouseKeyboard user(canvas);
Dialog dialog(canvas);
dialog.size = Size (500, 200);
dialog.position = Point(100, 100);
dialog.text = L"Hello World!";
Button button(dialog);
button.size = Size (120, 46);
button.position = Point(190, 60);
button.text = L"Exit";
button.image = Icons::Abort;
button.tooltip = L"Exits the application";
gpu.show();
return scheduler.run();
}
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 8 of 20
Step 0; strategy
First we need to think about the best way to create our UI. Sense is built around the idea that controls are
positioned in their parent control, so we will start with the largest container controls (those controls that
contain a lot of other controls) and then move our way in. This also makes it easy to program, because we get
direct visual feedback; we will see our UI build up step by step, in the right positions and the right size, instead of
being focused on small details.
First we need to find out which controls we will need. That’s where the Sense Gallery is for; go ahead and open
it from Philips.Sense.DLS.(VersionNumber)\tools\ Gallery-DLS.exe. It looks like Figure 6;
Figure 6
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 9 of 20
The Gallery allows users to look at and play with the available controls; you can see all the available controls and
their various states (enabled, hover, mouse-down etc.). You’ll also notice the four buttons at the top; the first
button toggles the enabled state of all controls. The second button shows the bounds of the control (more on
that later). The third button shows how a control is drawn and the final button is a handy color lookup tool; you
can click-and-drag the button across the screen and it will show the color-code and nearest Palette color from
DLS.
Try to find the major container controls that we’re going to need by clicking through the various tabs of the
Gallery and comparing it to our mockup picture.
You should end up with a Panel as our largest container, then an Expander, a Dialog and finally a Toolbar. When
you look at the mockup, the dialog is obviously a temporary one (for information purposes, hence the large “i"
icon), so we’ll ignore that one for now.
Open op the Tutorial project from our last chapter. First add a new class; create an ImageViewer.cpp and
ImageViewer.h in our project.
We’ll let ImageViewer subclass Control. Your subclass should look like Snippet 11;
Snippet 11
#pragma once
#include <Sense.h>
#include <DLS.h>
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 10 of 20
namespace Tutorial
{
Notice that we deleted the copy and move constructors/operators. We do this because controls cannot be
copied/moved by design; allowing copyable/moveable controls would make the implementation very difficult
and error prone, and would not serve any purpose.
ImageViewer::~ImageViewer() = default;
Now, we need to change our WinMain so that we use our new ImageViewer. We also want to render to a
window instead of a screen. Do the following;
1. Include ImageViewer.h,
2. Declare that we’re using namespace Tutorial
3. We want to render to a window instead of a screen, so change OutputScreen to OutputWindow. The “0”
argument can be removed (an OutputWindow uses a optional Rect instead of a screen identifier; if you’ll
leave it out it will use a default positioning strategy).
4. Remove the line setting the output-color; it’s not needed anymore.
5. Remove the code about Dialog and Button, and declare our ImageViewer viewer control (with canvas as
parent of course).
6. Set the size of viewer to the output size.
7. Add output.show() below gpu.show(); because OutputWindow is a normal Windows window, it can be
shown and hidden like all other windows. That’s why you need to explicitly tell Sense to show it.
If you run the application, you’ll notice that when we resize the window, the panel isn’t resized with it. Let’s fix
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 11 of 20
this; we need to do something with our resize behavior. Luckily, OutputWindow has an eventResized that we can
use, so we’ll hook a lambda function to it that resizes the canvas and panel to the new size. Tour code should
look like Snippet 13 now;
Snippet 13
#include <Windows.h>
#include <Sense.h>
#include <DLS.h>
#include "ImageViewer.h"
canvas.size = output.getSize();
UserMouseKeyboard user(canvas);
ImageViewer viewer(canvas);
viewer.size = output.getSize();
output .show();
gpu .show();
return scheduler .run();
}
Now when you run the application, you’ll see that the panel will resize with the window. Great!
Snippet 14
#pragma once
#include <Sense.h>
#include <DLS.h>
namespace Tutorial
{
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 12 of 20
protected:
Expander expander;
Panel toolbar;
};
}
Now if you try to compile, your compiler will complain our newly added controls don’t have default
constructors. So go ahead and add them to the initialization list in the constructor of ImageViewer. For their
parent, we want *this, since we want the expander and toolbar inside our panel.
So now we just have to set the position and size of the expander and toolbar. But how do we do that when the
panel can be resized; we want our shiny new controls to resize as well.
To do this, we will override the onResize method. This method is always called when a control is resized, so
subclasses of Control can utilize this to reposition their member controls, custom graphics etc.
Now that we’ve been properly scared, let’s get on with the actual positioning.
First, we define the implementation of our onResize function and call the onResize of the superclass (Panel).
Now, we need to know where we can place our controls. For positioning we use a Rect, a Point and/or a Size.
These classes have many useful methods and operators that make them easy and intuitive to use. Take a look in
the headers of Sense.
To determine how large the bounds of our controls are, we could use the size property of our parent, but this is
not such a good idea. In Sense, each control has a space (which determines its bounds amongst others) and a
client-space (the bounds for all its children). These two spaces often differ; take for example a Dialog, Expander
or GroupBox. These controls all have a title-bar, and normally, you want buttons etc. below the titlebar. That’s
[1]
[1]
why Sense
parent. allows controls
Everything outside to
thedefine a client-space
client-area is clipped. . In Sense, each control is rendered in the client-area of its
Next, we want to position and size of the toolbar and the expander. As you can see in the mockup, the toolbar is
at the top of the panel, and is about 60 pixels high. The expander is at the left side of the panel, and is 500 pixels
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 13 of 20
wide.
We can position the controls in two ways; using the setBounds(Rect) method, or setting the position and size
properties individually. Either way works, but from a code perspective the setBounds method leads to nicer and
more understandable code. See Snippet 16;
Snippet 16
expander.setBounds(Rect(bounds.left, bounds.top, bounds.left + 500, bounds.bottom ));
toolbar .setBounds(Rect(bounds.left, bounds.top, bounds.right, bounds.top + 60));
When you run your application now, you’ll see that the Expander and Toolbar are indeed at their expected
locations, but something is wrong; the toolbar is in front of the expander. That’s not really what we had in mind.
The reason for this is that in the class declaration of ImageViewer, we have declared the expander first, and then
the toolbar. This matters; Each Control will render itself, and then the first declared child control on top, then
the second child below the first, and so on and so forth. Note that the order of instantiation is determined by
the class declaration, not the initializers in the class constructor!
The fix is of course easy; swap the declarations of expander and toolbar, and now your application should look
like Figure 7. Note also that you can resize your window, and the expander and toolbar are resized too!
Figure 7
You can also change the order in which controls are rendered using the bringToFront and sendToBack methods.
Use these only when absolutely needed however; it is much easier to debug an application that behaves like the
way it is declared. We sometimes see users using bringToFront() to hide program errors; for example because
the user has accidently placed a transparent control in front of a dialog that eats mouse-clicks. The user will see
the dialog, but can’t click it. Using bringToFront on the dialog removes the symptoms, but doesn’t solve the issue
(and the code becomes harder to understand to boot).
Anyway, back to our application. There’s still something not entirely right; the toolbar shouldn’t be behind the
expander at all. In the toolbar.setBounds call, replace bounds.left with expander.getBounds().right.
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 14 of 20
When we now run the application, we see the border of the toolbar nicely aligned to the border of the
expander. However, when we collapse the expander, the toolbar isn’t compensated.
So we need to check for an event that occurs when the expander is collapsing or expanding. As we can see,
Expander has eventResize, which according to the documentation is called whenever the expander changes size.
We’ll add the following line to the constructor of ImageViewer;
Snippet 17
expander.eventResize = [this] { onExpanderResized(); };
Now, onExpanderResized is called whenever the expander changes shape. Declare onExpanderResized in the
class declaration. To finish up, provide the implementation for onExpanderResized and modify onResize as well;
we move the toolbar.setBounds call to onExpanderResized and provide getCurrentBounds instead of getBounds
of expander. See Snippet 18;
Snippet 18
void ImageViewer::onResize()
{
Panel::onResize();
void ImageViewer::onResizeExpander()
{
Rect bounds = getClientSpace().bounds;
toolbar .setBounds(Rect(expander.getCurrentBounds().right, bounds.top, bounds.right, bounds.top + 60));
}
Try running your application; if you’ve indeed declared the buttons and
separators above the toolbar, your application will now crash! This is because
C++ will initialize class members in the order of declaration; because we give
these controls toolbar as parent we crash in the constructor because toolbar is
given as an argument while it hasn’t been constructed yet. We should have
declared the buttons and separators below the toolbar declaration. Fix this, and
you’ll see that your application will run again.
Now we set the properties of the controls. First we’ll set the icons for the controls. Lookup the icons used in the
mockup in the Sense Gallery and set the image property to them. Set all the imageSize properties to 42.0 (a
double can be cast to a Size; it will use the same dimensions for width and height).
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 15 of 20
Now we need to position the controls. We’ll do this in the onResize function (we could set the size in the
constructor, but it’s nicer to do this in the onResize method if we have one; were we ever required to
dynamically change the bounds, everything is already in that method).
We’ll first need the bounds of the toolbar client space, so we’ll reuse the bounds Rect and set it to the toolbar
client-space.
Next we’ll declare bounding Rects for our buttons and separators. We’ll declare Rect b for the buttons, and Rect
s for the separators. We’ll use bounds.height() for the size of b, and Size(8, bounds.height()) for the size of s.
your code should look like Snippet 19;
Snippet 19
bounds = toolbar.getClientSpace().bounds;
Rect b = Rect(0,0,bounds.height(), bounds.height() );
Rect s = Rect(0,4,8, bounds.height() - 8 );
Now we’ll move b to the top-left position of bounds, by adding bounds.topLeft() to it (using the += operator).
Finally the actual positioning; we’ll use <button>.setBounds(b); b += Point(b.width(), 0); to position all the
buttons; see Snippet 20;
Snippet 20
b += bounds.topLeft();
As you can see, this code is now very easy to understand, and looks very simple. If you’d tried doing the same
using the position and size properties, the code would look a lot more complex.
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 16 of 20
Notice the dialog that’s being designed. The Composer saves directly to header files, so it is easy to use in your
project. Look at Snippet 22; this is the output of the screenshot above, saved to file test.ui.h.
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 17 of 20
Snippet 22
/*----------------------------------------------------
| This file was generated by Sense Composer 2.0 |
| Do not modify the Sense Composer Generated Code |
----------------------------------------------------*/
#pragma once
#include <Sense.h>
#include <DLS.h>
Test(Control &parent)
:
Control(parent)
#pragma region Sense Composer (do not modify)
, dialog (*this)
, label (dialog)
, buttonCustom (*this)
#pragma endregion
{
#pragma region Sense Composer (do not modify)
layoutUpdateBegin();
name = L"Test";
size = Size(856, 640);
dialog.position = Point(96, 128);
dialog.size = Size(696, 336);
label.position = Point(96, 0);
label.size = Size(504, 152);
label.font = Font(L"CentraleSansBook", 50, true, false, false, false, Font::Antialias::High);
label.text = L"This makes sense!";
label.horizontalAlign = HAlign::Center;
label.verticalAlign = VAlign::Middle;
buttonCustom.position = Point(336, 376);
buttonCustom.size = Size(216, 56);
buttonCustom.checked = true;
buttonCustom.text = L"OK";
layoutUpdateEnd();
#pragma endregion
}
private:
Test(const Test&);
Test& operator=(const Test&);
};
You can change the code directly and load it again into the Composer. However, code not related to controls, or
flexible code (like an if-statement) is not understood by the Composer and will be thrown out. Hence the
warning at the top of the file.
Let’s change our toolbar, so that we use the Sense Composer to design it rather than create it ourselves. Open
the Composer from Philips.Sense.DLS.(VersionNumber)/tools/Composer-DLS. In the middle is of course our UI
that we are designing. On the left of Composer are the controls listed, ordered by type. You can drag and drop
controls on the UI you’re designing. At the top is a toolbar with common tasks (New, Open, Save, Cut, Copy,
Paste, Bring to front, Send to back and Show wireframe). Finally, to the right the properties of the currently
selected control(s) is/are displayed.
Click “New…” from the toolbar.In the dialog that follows, select Panel as control type and click OK. You should
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 18 of 20
1. Select a control with the same parent, press Ctrl-A and move all controls around until you get all
controls back in the parent (you can also try resizing the parent, but that’ll only work if the missing
control is beneath or to the right of the parent control).
2. Discard your changes and start again.
3. Save the file and manually remove the control from the saved header file.
Right now, everything looks messy. Let’s order things and add some icons to the controls. First select all controls
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 19 of 20
and move them a bit to the right, so we can order everything from left to right. Now move one of the buttons to
the top-left position. This will be our pan button. When you look at the properties of this button you can see we
can set the image; set the correct icon. Next, we need to set the correct image-size. This should be 42,42; but all
images should have the same image-size. So press Ctrl-A again and change the image-size to 42,42. Now, when
we only select our pan-button again, we can see in the properties that its name is Generated. This means that
the Composer will figure out a name automatically. There is some logic behind the naming that will result in a
button with the text “OK” will be named buttonOK, but controls with the same text (or no text property) will be
named button, button2, button3 etc. However, we can of course explicitly set the name by replacing Generated
with a name of ourselves. This is a good idea if you plan to add logic (use events) to your control. Set the name
to “pan”.
Now position the other buttons, name them correctly, add their icons and place the separators at the right
position. You should end up with something like Figure 11;
Figure 11
Save the file in your project folder as tools.ui.h, and go back to Visual Studio; time to clean up the ImageViewer
class.
First add a new filter to our project; right-click Tutorial in the Solution Explorer and click AddàNew Filter… name
our filter “UI Headers”. Now right-click our new filter and click AddàExisting Item… and add tools.ui.h.
Next, we open the ImageViewer class declaration, and remove every member except for expander. Include the
tools.ui.h header file, and add declare Tools toolbar above the expander declaration.
Now, go to the class implementation. Here you can revert everything back to the implementation we had before
step 4! (Sorry for letting you write all that code). Cleans up, doesn’t it?
Now, compile and run; your application will look exactly the same as in Step 4, but with most of our UI code now
visually created with the Composer.
We don’t want to show the dialog all the time; it should only be displayed when the “i”-button is pressed on the
toolbar, and disappear again when the OK-button is clicked. To do so, do the following in the ImageViewer
constructor;
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021
Page 20 of 20
When you have positioned the control, compile and run your application. You’ll see that you your dialog shows
up and goes away as intended. However, you’ll also notice that you can still click on controls beneath the dialog
when it is shown. We don’t want that; in the ImageViewer constructor, set the modal property of information to
true. This causes the information control to receive all input-events of the canvas when it is visible, and no one
else.
We’re done; your application should look like Figure 12.
Figure 12
In this chapter you’ve learned a lot about designing a UI and placement of controls. For custom positioning,
remember that the Rect, Point and Size classes contain many useful functions and constructors that can help you
place your controls easily and efficiently.
Remember our warnings well:
1. Always add the override keyword.
mhtml:mk:@MSITStore:D:\Dev\puma\root\Externals\Philips.Sense\doc\Sense.chm::/Se... 14-Oct-2021