Example for a machine with Microsoft Silverlight for Windows Embedded

A new item in Windows Embedded CE 6.0 R3 is Silverlight for Windows Embedded. With this new technology, user interfaces of CE devices can now be written in XAML and designed with tools such as Microsoft Expression Blend. On the basis of the machine example, the creation of a Silverlight for Windows Embedded application with integration of the ADS components is described here.

Target platform

- Windows CE 6 R3

Implementation

- C++

Required software

- Microsoft Visual Studio 2008
- Microsoft Expression Blend 2 SP1
- TwinCAT 2.11
- Beckhoff HMI 600 SDK

Required hardware

- Windows CE 6.0 R3 device (e.g. CX1020)

First steps...

1. Creating a new Silverlight 2 project:

The design of a Silverlight for Windows Embedded application is described in XAML. To this end a Silverlight 2 project is created with Microsoft Expression Blend 2 SP1 via 'File - > New Project'. The Visual Studio Solution thereby created is not needed in this sample. In addition, the selection of the programming language (Visual C# or Visual Basic) can be ignored. Silverlight for Windows Embedded supports only Visual C++, which is not integrated in Expression Blend. It is therefore also not possible to use the source code generated by this tool. Disable Visual Studio integration in Expression Blend to avoid the unnecessary automatic generation of Visual C# and Visual Basic code. To do this, select: 'Options->Event handlers' 'Clipboard only'.

Example for a machine with Microsoft Silverlight for Windows Embedded 1:

2. Creating a user interface
The user interface can now be created in Expression Blend.

Example for a machine with Microsoft Silverlight for Windows Embedded 2:

In the upper left area, the two outputs can be seen that are also output to the bus terminals. The bottom left shows the variable for counting the workpieces. The speed can be set on the right. The 'Steps' display corresponds to the number of cycles. In addition, a button for ending the program is created at the top right.

<UserControlxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="319" Height="255"><!-- Timelines --><UserControl.Resources><!-- Timeline Device Down --><Storyboardx:Name="timelineDeviceDown"><ColorAnimationUsingKeyFramesBeginTime="00:00:00"
            Storyboard.TargetName="txtDeviceDown" 
            Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"><SplineColorKeyFrameKeyTime="00:00:00.4000000" Value="#FFFF0000"/></ColorAnimationUsingKeyFrames></Storyboard><!-- Timeline Device Up --><Storyboardx:Name="timelineDeviceUp"><ColorAnimationUsingKeyFramesBeginTime="00:00:00" 
            Storyboard.TargetName="txtDeviceUp" 
            Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"><SplineColorKeyFrameKeyTime="00:00:00.4000000" Value="#FFFF0000"/></ColorAnimationUsingKeyFrames></Storyboard><!-- Timeline Engine --><Storyboardx:Name="timelineEngine"/></UserControl.Resources><!-- Beginn der Layout Beschreibung --><Gridx:Name="LayoutRoot" Background="#FF595959"><!-- Title Machine --><TextBlockText="Machine" Margin="80,8.438,80,0" VerticalAlignment="Top" 
        FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="34"/><!-- Device Up / Device Down --><GridMargin="15,60,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" 
        Height="53" Width="132.532"><RectangleFill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2" 
            RadiusX="6" RadiusY="6"/><TextBlockText="Device Up" Margin="28.823,5.396,-8.823,0" 
            VerticalAlignment="Top" Foreground="#FFFFFFFF" FontSize="16"/><TextBlockText="Device Down" Margin="29.002,0,-9.002,4.994" 
            VerticalAlignment="Bottom" Foreground="#FFFFFFFF" FontSize="16"/><TextBlockx:Name="txtDeviceDown" Text="ê" Margin="7.517,0,0,4.998" 
            HorizontalAlignment="Left" VerticalAlignment="Bottom" FontFamily="Wingdings" 
            FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="16"/><TextBlockx:Name="txtDeviceUp" Text="é" Margin="7.517,5.497,0,0" 
            HorizontalAlignment="Left" VerticalAlignment="Top" FontFamily="Wingdings" 
            FontWeight="Bold" Foreground="#FFFFFFFF" FontSize="16"/></Grid><!-- Counter --><GridMargin="15,0,0,71" HorizontalAlignment="Left" VerticalAlignment="Bottom" 
        Height="53" Width="133"><RectangleFill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2" 
            RadiusX="6" RadiusY="6"/><StackPanelMargin="12.991,16,0,16" HorizontalAlignment="Left" 
            Orientation="Horizontal" Width="113"><TextBlockText="Count:" Width="54.824" Foreground="#FFFFFFFF"FontSize="16"/><TextBlockx:Name="txtCount" Margin="2,0,0,0" Foreground="#FFFFFFFF" 
            FontSize="16"/></StackPanel></Grid><!-- Speed --><GridMargin="0,0,15,71" Height="53" HorizontalAlignment="Right" 
        VerticalAlignment="Bottom" Width="132.532"><RectangleFill="{x:Null}" Stroke="#FFFFFFFF" StrokeThickness="2" 
            RadiusX="6" RadiusY="6"/><StackPanelMargin="12.988,0,3.012,0" Orientation="Horizontal"><RadioButtonx:Name="radSpeedSlow" Content="slow" Margin="0,0,6,0" 
            Foreground="#FFFFFFFF" FontSize="16" Height="19.496"/><RadioButtonx:Name="radSpeedFast" Content="fast" Foreground="#FFFFFFFF" 
            FontSize="16" Height="19.496"/></StackPanel></Grid><!-- Steps --><GridMargin="15,0,15,5" VerticalAlignment="Bottom" Height="57"><ProgressBarx:Name="prgSteps" Margin="0,18,0,18" Maximum="25"/><TextBlockText="Steps:" HorizontalAlignment="Left" 
            VerticalAlignment="Top" Foreground="#FFFFFFFF"/><TextBlockText="0%" HorizontalAlignment="Left" 
            VerticalAlignment="Bottom" Foreground="#FFFFFFFF"/><TextBlockText="100%" HorizontalAlignment="Right" 
            VerticalAlignment="Bottom" Foreground="#FFFFFFFF"/></Grid><!-- Close Button --><Buttonx:Name="butClose" Content="X" HorizontalAlignment="Right" VerticalAlignment="Top" 
        Height="20" Width="20"/><!-- Beckhoff Logo --><ImageSource="beckhoff_logo_white.jpg" Margin="0,64.502,15,0" HorizontalAlignment="Right" 
        VerticalAlignment="Top" Height="43.151" Width="133.745" Stretch="Fill"/></Grid></UserControl>


3. Creating a new Win32 Smart Device project

A new Win32 Smart Device project must now be created in Visual Studio 2008.

Example for a machine with Microsoft Silverlight for Windows Embedded 3:

Beckhoff HMI 600 SDK installed?

If the Beckhoff HMI 600 SDK is not yet installed on the computer, install it before creating a new Visual Studio project.

Example for a machine with Microsoft Silverlight for Windows Embedded 4:

The Platform SDK of this project is the Beckhoff HMI 600 SDK.

Example for a machine with Microsoft Silverlight for Windows Embedded 5:

Executable (EXE) is selected as the server type.

Example for a machine with Microsoft Silverlight for Windows Embedded 6:

4. Integrating the XAML file as a resource

The user interface designed with Expression Blend can be integrated in the new project. To do this, open the resource file (.rc). A right mouse click on the resource in the Resource View tab and the selection of 'Add - > Resource…' opens a dialog box via which the XAML file can be integrated.

Example for a machine with Microsoft Silverlight for Windows Embedded 7:

The XAML file can be imported into the project with the aid of the dialog.

Example for a machine with Microsoft Silverlight for Windows Embedded 8:

Specify XAML as the resource type.

Example for a machine with Microsoft Silverlight for Windows Embedded 9:

The standard resource ID (IDR_XAML1) can be retained in this example. In your own projects, however, it makes sense to rename them.

Example for a machine with Microsoft Silverlight for Windows Embedded 10:

5. Creating an AdsHelper Class

The largest part of the Ads communication can be stored in a separate class, called AdsHelper here.

AdsHelper.h

The TcAds headers are integrated in the AdsHelper headers. Care must be taken to specify the correct path. In addition, the headers depend on the processor type. In the sample code the X86 headers are attached.

#include"..\_AdditionalFiles\TcpAdsApiCe\include\TcAdsDef.h"#include"..\_AdditionalFiles\TcpAdsApiCe\include\TcAdsAPI.h"


Furthermore, the declarations are made in the header.

typedef enum E_NOTIFICATION_IDENT
{
    engine = 0,
    deviceUp = 1,
    deviceDown = 2,
    steps,
    count,
    switchSpeed    
};

long AdsGetVarHandle(AmsAddr* pServerAddr, const char* szVarname, long* pHandle);
long AdsFreeVarHandle(AmsAddr* pServerAddr, long handle);
long AdsGerVarHandleEx(long port, AmsAddr* pServerAddr, const char* szVarname, long* pHandle);
long AdsFreeVarHandleEx(long port, AmsAddr* pServerAddr, long handle);

long SpeedSlowEx(long port, AmsAddr* pServerAddr);
long SpeedFastEx(long port, AmsAddr* pServerAddr);

long connect(long port, AmsAddr* pServerAddr);
long disconnect(long port, AmsAddr* pServerAddr);


AdsHelper.cpp

The headers StdAfx.h and AdsHelper.h must be integrated in the AdsHelper.cpp,

#include"StdAfx.h"#include"AdsHelper.h"

and afterwards the global variables defined.

long hEngine, hDeviceUp, hDeviceDown, hSteps, hCount, hSwitch;
unsigned long hEngineNotification, hDeviceUpNotification, hDeviceDownNotification, 
          hStepsNotification, hCountNotification, hSwitchNotification;

The methods AdsGetVarHandle and AdsGetVarHandleEx serve to create handles for PLC variables

long AdsGetVarHandle(AmsAddr* pServerAddr, const char* szVarname, long* pHandle)
{
    if (pHandle == NULL || pServerAddr == NULL)
    return E_POINTER;
    
    unsigned long read = 0;
    long nErr = 
    AdsSyncReadWriteReqEx(pServerAddrm, ADSIGRP_SYM_HNDBYNAME, 0x0,
    sizeof(long), pHandle,strlen(szVarname), (char*)szVarname, &read);
    
    return nErr;
}

long AdsGetVarHandleEx(long port, AmsAddr* pServerAddr, const char* szVarname, long* pHandle)
{
    if(pHandle == NULL || pServerAddr == NULL)
    return E_POINTER;
    
    unsigned long read = 0;
    
    long nErr = 
    AdsSyncReadWriteReyEx2(port, pServerAddr, ADSIGRP_SYM_HNDBYNAME, 0x0,
    sizeof(long), pHandle, strlen(szVarname), (char*)szVarname, &read);
    
    return nErr;
}


Handles for PLC variables are released again with AdsFreeVarHandle and AdsFreeVarHandleEx.

long AdsFreeVarHandle(AmsAddr* pServerAddr, long handle)
{
    return AdsSyncWriteReq(pServerAddr, ADSIGRP_SYM_RELEASEHND, 0, 
               sizeof(handle), &handle);
}

long AdsFreeVarHandleEx(long port, AmsAddr* pServerAddr, long* pHandle)
{
    return AdsSyncWriteReqEx(port, pServerAddr, ADSIGRP_SYM_REALEASEHND, 0, 
                 sizeof(pHandle), &pHandle);
}


The following two methods write the PLC variable ".switch" and thereby set the speed to slow or fast.

long SpeedSlowEx(long port, AmsAddr* pServerAddr)
{
    // Handle der SPS-Variable ".switch" erstellen.long handleSpeedSlow = 0;
    long adserror = AdsGetVarHandleEx(port, pServerAddr, ".switch", &handleSpeedSlow);    
    
    // Die SPS-Variable ".switch" auf FALSE setzenbool datafalse = false;
    adserror = AdsSyncWriteReqEx(port, pServerAddr, ADSIGRP_SYM_VALBYHND, 
                 handleSpeedSlow, 0x1, &datafalse);
    
    // Handle der SPS-Variable ".switch" freigeben
    adserror = AdsFreeVarHandleEx(port, pServerAddr, handleSpeedSlow);
    return adserror;
}

long SpeedFastEx(long port, AmsAddr* pServerAddr)
{    
    // Handle der SPS-Variable ".switch" erstellen.long handleSpeedFast = 0;
    long adserror = AdsGetVarHandleEx(port, pServerAddr, ".switch", &handleSpeedFast);    
    
    // Die SPS-Variable ".switch" auf TRUE setzenbool datatrue = true;
    adserror = AdsSyncWriteReqEx(port, pServerAddr, ADSIGRP_SYM_VALBYHND, 
                 handleSpeedFast, 0x1, &datatrue);    
    
    // Handle der SPS-Variable ".switch" freigeben
    adserror = AdsFreeVarHandleEx(port, pServerAddr, handleSpeedFast);
    return adserror;
}


In the connect method, a connection to the variables is created in the PLC.

long connect (long port, AmsAddr* addr, PAdsNotificationFuncEx Callback)
{
    // Attribute der Notification festlegen
    AdsNotificationAttrib attr;
    attr.cbLength = 2;
    attr.nTransMode = ADSTRANS_SERVERCYCLE;
    attr.nMaxDelay = 100000000; // = 1 sec
    attr.nCycleTime = 100000; // = 0,5 sec// Handles der SPS-Variablen holenlong adserr = AdsGetVarHandleEx(port, addr, ".engine", &hEngine);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".deviceUp", &hDeviceUp);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".deviceDown", &hDeviceDown);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".steps", &hSteps);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".count", &hCount);
    
    if(adserr == 0)
    adserr = AdsGetVarHandleEx(port, addr, ".switch", &hSwitch);
    
    
    // Überwachung der SPS-Variablen initialisierenif(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hEngine, 
                           &attr, Callback, engine, &hEngineNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hDeviceUp, 
                           &attr, Callback, deviceUp, &hDeviceUpNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hDeviceDown, 
                           &attr, Callback, deviceDown, &hDeviceDownNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hSteps, 
                           &attr, Callback, steps, &hStepsNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 2;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hCount, 
                           &attr, Callback, count, &hCountNotification);
    }
    if(adserr == 0)
    {
    attr.cbLength = 1;
    adserr = AdsSyncAddDeviceNotificationReqEx(port, addr, ADSIGRP_SYM_VALBYHND, hSwitch, 
                           &attr, Callback, switchSpeed, &hSwitchNotification);
    }
    
    return adserr;
}


When disconnecting the Ads connection, the handles of the PLC variables must be released and the port closed.

long disconnect(long port, AmsAddr* addr)
{
    // Handles der SPS-Variablen freigeben
    AdsFreeVarHandleEx(port, addr, hEngine);
    AdsFreeVarHandleEx(port, addr, hDeviceUp);
    AdsFreeVarHandleEx(port, addr, hDeviceDown);
    AdsFreeVarHandleEx(port, addr, hSteps);
    AdsFreeVarHandleEx(port, addr, hCount);
    AdsFreeVarHandleEx(port, addr, hSwitch);
    
    // Notifications löschen
    AdsSyncDelDeviceNotificationReqEx(port, addr, hEngineNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hDeviceUpNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hDeviceDownNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hStepsNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hCountNotification);
    AdsSyncDelDeviceNotificationReqEx(port, addr, hSwitchNotification);
    
    // Kommunikationsport schließen
    AdsPortCloseEx(port);
    
    return 0;
}


6. Processing the source code

The headers are integrated first in the SilverlightForEmbeddedMachineSample.cpp file.

#include"pwinuser.h"#include"xamlruntime.h"#include"xrdelegate.h"#include"xrptr.h"    #include"resource.h"

The declaration of the variables follows.

IXRDelegate<XRMouseButtonEventArgs>* clickdelegate;

UINT exitcode;    

IXRVisualHostPtr   vhost;
IXRButtonBasePtr   butClose;
IXRRadioButtonPtr  radSpeedSlow;
IXRRadioButtonPtr  radSpeedFast;
IXRTextBlockPtr    txtDeviceDown;
IXRTextBlockPtr    txtDeviceUp;
IXRTextBlockPtr    txtCount;
IXRProgressBarPtr  prgSteps;

IXRStoryboardPtr   timelineDeviceDown;
IXRStoryboardPtr   timelineDeviceUp;
IXRStoryboardPtr   timelineEngine;

long port;
AmsAddr addr;

unsigned long TimerID;
DWORD EventID;
CRITICAL_SECTION cs;
long event_cnt;
long event_cntold;

void __stdcall Callback(AmsAddr* addr, AdsNotificationHeader* handler, unsigned long User);



The timer callback function checks the Ads connection and restores the connection if necessary.

VOID CALLBACK MyTimerProc(
    HWND hwnd,     // handle to window for timer messages
    UINT message,  // WM_TIMER message
    UINT idTimer,  // timer identifier
    DWORD dwTimer) // current system time
{
    if (event_cnt = event_cntold)
    {
    // Handels werden freigegeben und der Port geschlossen
    disconnect(port, &addr);
    
    // Kommunikationsport auf dem ADS Router öffnen
    port = AdsPortOpenEx();
        
    addr.port = 0x321;
    long adserror = -1;
    
    // Neue Verbindung zur SPS herstellen.while(adserror != 0)
    {
        adserror = connect(port, &addr, Callback);
        Sleep(1000);
    }
    }
    
    event_cntold = event_cnt;
}



The following Ads Event handler is called if a PLC variable to which a link exists changes.

// ADS-State Callback-Functionvoid __stdcall Callback(AmsAddr* addr, AdsNotificationHeader* handler, unsigned long User)
{
    event_cnt++;    


The corresponding arrows are colored red or not, depending on whether deviceUp, deviceDown or engine is set to TRUE.

This effect can be improved still further by the use of timelines.

if (User == deviceUp)
    {
    if (*(bool*)handler->data == true)
    {
        timelineDeviceDown->Stop();
        timelineEngine->Stop();
        timelineDeviceUp->Begin();
    }
    }
    else if (User == deviceDown)
    {
     if(*bool*)handler-data == true)
     {
         timelineDeviceUp->Stop();
         timelineEngine->Stop();
         timelineDeviceDown->Begin();
     }
    }
    else if (User == engine)
    {
    if (*(bool*)handler->data == true)
    {
        timelineDeviceDown->Stop();
        timelineDeviceUp->Stop();
        timelineEngine->Begin();
    }
    }


steps indicates the number of cycles. The value is output via the progress bar prgSteps. For this the data must first be converted to a byte, since the associated PLC variable is of the type byte. Since the progress bar can only transfer data of the type float, conversion to the type float subsequently takes place.

else if (User == steps)
    {
    prgSteps->SetValue((float)*((byte*)handler->data));
    }


In the case of count, as with steps, the data must first be converted to their original data type before they can be converted into a text and transferred to the text block txtCount

else if (User == count)
    {
    WCHAR text[6];
    wsprintf(text, L"%d", *(unsigned short*)handler->data);
    txtCount->SetText(text);
    }


The output of the speed type is done via RadioButtons. The appropriate radio button is marked depending on the speed.

else if (User == switchSpeed)
    {
    if (*(bool*)handler->data == true)
    {
        radSpeedFast->SetIsChecked(XRThreeState_Checked);
    }
    else
    {
        radSpeedSlow->SetIsChecked(XRThreeState_Checked);
    }
    }
}



The OnClick event is triggered by the various instances and the name can be used to distinguish which instance was the trigger.

class BtnEventHandler
{
public:

    HRESULT OnClick(IXRDependencyObject* source, XRMouseButtonEventArgs* args)
    {
    BSTR name;
    HRESULT hr = NULL;
    source->GetName(&name);
    
    short state = 0;
    
    long adserror = 0;
    
    if (wcscmp(name, L"butClose") == 0)
    {
        // Machine Dialog schließen
        vhost->EndDialog(exitcode);
    }
    if (wcsmp(name, L"radSpeedSlow") == 0)
    {
        // Aufruf der Methode SpeedSlowEx um die Geschwindigkeit auf langsam zu setzen.
        adserror = SpeedSlowEx(port, &addr);
    }
    if (wscmp(name, L"radSpeedFast") == 0)
    {
        // Aufruf der Methode SpeedFastEx um die Geschwindigkeit auf schnell zu setzen.
        adserror = SpeedFastEx(port, &addr);
    }
    
    if (adserror != NULL)
    {
        // Die Handels werden freigegeben und der Port geschlossen
        disconnect(port, &addr);
        
        // Der Kommunikationsport auf dem ADS-Router wird geöffnet
        port = AdsPortOpenEx();
        
        addr.port = 0x321;
        
        // Neu Verbindung zur SPS herstellen.
        adserror = connect(port, &addr, Callback);
        
        Sleep(1000);
    }
    
    SysFreeString(name);
    return S_OK;
    }    
};



In the WinMain method, the XMAL runtime must be initialized first. If XamlRuntimeInitialize is successful, then Silverlight for Windows Embedded Runtime is started in the application.

int WINAPI WinMain(HINSTANCE hInstance,
          HINSTANCE hPrevInstance,
          LPTSTR lpCmdLine,
          int nCmdShow)
{
    // Initialisierung der XAML Runtimeif (!XamlRuntimeInitialize())
    return -1;


Each Silverlight for Windows Embedded application has a single "application" object that can be used to access global properties.
To access this object the GetXRApplicationInstance API is used.

    HRESULT retcode;

    // Load an dinit XAML resource
    IXRApplicationPtr app;

    if (FAILED (retcode=GetXRApplicationInstance(&app)))
    return -1;
    
    if (FAILED (retcode=app->AddResourceModule(hInstance)))
    return -1;


After the initialization of the application object, the main window can be created and the administration of the object can be handed over to Silverlight for Windows Embedded.

    XRWindowCreateParams wp;

    ZeroMemory(&wp, sizeof(XRWindowCreateParams));

    // Set window styles
    wp.Style = WS_BORDER;
    wp.pTitle = L"Silverlight for Windows Embedded Machine Sample";
    wp.Left = 0;
    wp.Top = 0;
    wp.AllowsMultipleThreadAccess = true;

    XRXamlSource xamlsrc;

    xamlsrc.SetResource(hInstance, TEXT("XAML"), MAKEINTRESOURCE(IDR_XAML1));

    if (FAILED(retcode=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))
    return -1;


The object within a Silverlight for Windows Embedded application is organized in object trees. In order to access this object, a pointer to the root element is required.

    IXRFrameworkElementPtr root;

    if (FAILED (retocde=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))
    return -1;


Creating instances of the controls and timelines.

// Get controls by nameif (FAILED(retcode=root->FindName(TEXT("butClose"), &butClose))) 
    return -1;

    if (FAILED(retcode=root->FindName(TEXT("radSpeedSlow", &radSpeedSlow)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("radSpeedFast", &radSpeedFast)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("txtDeviceDown", &txtDeviceDown)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("txtDeviceUp", &txtDeviceUp)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("txtCount", &txtCount)))
    return -1;
    
    if (FAILED(retcode=root->FindName(TEXT("prgSteps", &prgSteps)))
    return -1;
    
    // Get timelines by nameif (FAILED (retcode=root->FindName(TEXT("timelineDeviceDown"), &timelineDeviceDown))) 
    return -1;  
       
    if (FAILED (retcode=root->FindName(TEXT("timelineDeviceUp"), &timelineDeviceUp))) 
    return -1;
    
    if (FAILED (retcode=root->FindName(TEXT("timelineEngine"), &timelineEngine))) 
    return -1;


Creating the "RadioButtonGroup" and assigning the two radio buttons from this group.

    WCHAR groupName[17];
    wsprintf(groupName, L"RadioButtonGroup");
    radSpeedFast->SetGroupName(groupName);
    radSpeedSlow->SetGroupName(groupName);


A redirecting object is needed to link the EventHandler with the buttons.

    BtnEventHandler handler;

    // Set the event handler for the buttonsif (FAILED(retcode=CreateDelegate(&handler, &BtnEventHandler::OnClick, &clickdelegate)))
    return -1;

    if (FAILED(retcode=butClose->btnAddClickEventHandler(clickdelegate)))
    return -1;
    
    if (FAILED(retcode=radSpeedSlow->AddClickEventHandler(clickdelegate)))
    return -1;
    
    if (FAILED(retcode=radSpeedFast->AddClickEventHandler(clickdelegate)))
    return -1;


Integrating the Ads components.

long adserror = -1;
    port = AdsPortOpenEx();

    AdsGetLocalAddressEx(port, &addr);

    // connect to the PLC and register callbacks
    addr.port = 0x321;
    adserror = connect(port, &addr, Callback);
    
    event_cnt = 0;
    event_cntold = -1;
    
    
    // init timer for reconnect
    SetTimer(NULL, NULL, 5000, MyTimerProc);
    
    if (FAILED(retcode=vhost->StartDialoge(&exitcode)))
    return -1;
    
    // cleanup
    disconnect(port, &addr);
    clickdelegate->Release();
    
    return 0;
}

7. Properties

A connection to xamlruntime.lib and TcAdsDllCe.lib must be made in the project properties.

Example for a machine with Microsoft Silverlight for Windows Embedded 11:

Download Silverlight for Windows Embedded sample

Sample Silverlight for Windows Embedded

Example for a machine with Microsoft Silverlight for Windows Embedded 12:

Replacing X86 version with ARM

In the sample the X86 version of the TcAdsDllCe.lib is used. To build the sample for ARM devices, this library must be exchanged for the corresponding ARM version beforehand.