Wtl guide for MFC programmers: Part VIII-property page and Wizard

Source: Internet
Author: User

Wtl guide for MFC programmers: Part VIII-property page and Wizard

Original:Michael Dunn[Original ENGLISH]
Translation: Orbit [www.winmsg.com]

Download DEMO code

Content of this Chapter

  • Introduction
  • Wtl Attribute Table class
    • Cpropertysheetimpl Method
  • Wtl property page class
    • Cpropertypagewindow Method
    • Cpropertypageimpl Method
    • Process notification messages
  • Create an Attribute Table
    • Simplest Attribute Table
    • Create a useful property page
    • Create a better Attribute Table class
  • Create a wizard style Attribute Table
    • Add more property pages and use DDV
  • Other interface considerations
    • Set an Attribute Table
    • Add an icon to the properties page
  • Continue
  • Modify record

Introduction

Even before becoming a universal control of Windows 95, using attribute tables to represent some options has become a very popular method. Wizard-mode attribute tables are usually used to guide users to install software or complete other complex work. Wtl provides good support for the attribute tables in both modes. You can use the features related to the dialog box described earlier, such as DDX and DDV. In this chapter, I will demonstrate how to create a basic property table and Wizard and handle notification messages and events sent on the property page.

Wtl Attribute Table class

To implement an Attribute Table, you must use the cpropertysheetwindow and cpropertysheetimpl classes in combination. They are all defined in the atldlgs. h header file. The cpropertysheetwindow class is a window interface class (that is, a cwindow derived class). cpropertysheetimpl has a complete message ing chain and window implementation, which is similar to the basic window class of ATL, it must be used in combination with cwindow and cwindow wimpl.

The cpropertysheetwindow class encapsulates the processing of various SMS _ * messages. For example, setactivepagebyid () encapsulates psm_setcurselid messages. The cpropertysheetimpl class manages a propsheetheader structure and an array of the hpropsheetpage type. The cpropertysheetimpl class also provides
Some methods are used to fill in the propsheetheader structure and add or delete attribute pages. You can also use the m_psh member variable to directly operate the propsheetheader structure.

Finally, the cpropertysheet class is a special case of the cpropertysheetimpl class. You can use it directly without the need to customize the entire Attribute Table.

Cpropertysheetimpl Method

Below are some important methods of the cpropertysheetimpl class. Because many methods only encapsulate window messages, they are not listed here. You can view the complete function list in atldlgs. h.

CPropertySheetImpl(_U_STRINGorID title = (LPCTSTR) NULL,                   UINT uStartPage = 0, HWND hWndParent = NULL)

The constructor of the cpropertysheetimpl class allows you to use common attributes (default value), so you do not need to set them in other methods. Title specifies the text displayed in the title bar of the Attribute Table. _ u_stringorid is a wtl tool class that can automatically convert the title string and resource ID. For example, the following two lines of code are correct:

  CPropertySheetImpl mySheet ( IDS_SHEET_TITLE );  CPropertySheetImpl mySheet ( _T("My prop sheet") );

Ids_sheet_title is the string ID. Ustartpage is an attribute page activated when a property table is started. It is an index starting from 0. Hwndparent is the handle of the parent window of the Attribute Table.

Bool addpage (hpropsheetpage hpage)
Bool addpage (lpcpropsheetpage ppage)

Add an attribute page. If this property page has been created, you can use the first overload function and use the property page handle (hpropsheetpage) as the parameter. Generally, the second overload function is used. To use this overload function, you only need to set a propsheetpage data structure (which will work together with cpropertypageimpl ), cpropertysheetimpl will create and manage this property page for you.

Bool removepage (hpropsheetpage hpage)
Bool removepage (INT npageindex)

To remove an attribute page, you can use the handle or index of the property page.

Bool setactivepage (hpropsheetpage hpage)
Bool setactivepage (INT npageindex)

Set the activity page of the Attribute Table. You can use the handle or index of the property page. You can use this method to dynamically set an active property page before creating or displaying an Attribute Table.

Void settitle (lpctstr lpsztext, uint nstyle = 0)

The title text of the Attribute Table window. Nstyle can be 0 or psh_proptitle. If it is psh_proptitle, the attribute table will have the psh_proptitle style. In this way, the system will add the string "properties for" before the window title specified by the lpsztext parameter ".

void SetWizardMode()

Set the psh_wizard style and rename the Attribute Table to the wizard mode. This function must be called before the display of the Attribute Table.

Void enablehelp ()

Set the psh_hashelp style. The Help button is added to the Attribute Table. Note that you must make the Help button available and provide help on each property page to make it take effect.

Int_ptr domodal (hwnd hwndparent =: getactivewindow ())

A pattern Attribute Table is created and displayed. If a positive value is returned, the operation is successful. The help documentation on propertysheet () API provides a detailed explanation of the returned value. If an error occurs, the attribute table cannot be created, domodal () returns-1.

Hwnd create (hwnd hwndparent = NULL)

Create and display a schema-free Attribute Table. The returned value is the window handle. If an error occurs, the attribute table cannot be created, and create () returns NULL.

Wtl property page class

The encapsulation class of the property page by wtl is similar to that of the Attribute Table. There is a window interface class cpropertypagewindow and an implementation class cpropertypageimpl. Cpropertypagewindow is very small and contains the most common methods that need to be called in the attribute table as the parent window.

Cpropertypageimpl is derived from cdialogimplbaset. Because the property page is created from the dialog box resources, this means that all wtl features that can be used in the dialog box can be used in the property page, such as DDX and DDV. Cpropertypageimpl has two main functions: to manage a propsheetpage data structure (stored in the member variable m_psp), to process all notification messages starting with PSN. You can directly use the cpropertypage class for simple attribute pages. This class is only suitable for Attribute pages that do not interact with users, such as the "about" page or the introduction page in the Wizard.

You can also create an attribute page containing ActiveX controls. First, add the atlhost. h file to the stdafx. h file, and use caxpropertypageimpl instead of cpropertypageimpl. For simple pages, you can use caxpropertypage instead of cpropertypage.

Cpropertypageimpl Method

Cpropertypageimpl manages a propsheetpage structure, that is, the Public Member m_psp. Cpropertypageimpl also loads the propsheetpage * operator, so you can pass cpropertypageimpl to a method that requires parameters of the lppropsheetpage or the lpcpropsheetpage type, such as cpropertysheetimpl: addpage ().

The cpropertypageimpl constructor allows you to set the page title. The title is usually displayed on the tab of the page:

CPropertyPageImpl(_U_STRINGorID title = (LPCTSTR) NULL)

If you do not want the property table to create the property page but want to create the page manually, you can call create ():

HPROPSHEETPAGE Create()

Create () only calls createpropertysheetpage () using m_psp as the parameter (). If you add a property page to a created property table or add a property page to another uncontrolled property table (for example, processing the extended property table of the System Shell ), you only need to call the CREATE () function.

The following three methods are used to set the title Text of the property page:

Void settitle (_ u_stringorid title)
Void setheadertitle (lpctstr lpstrheadertitle)
Void setheadersubtitle (lpctstr lpstrheadersubtitle)

The first method is to change the text of the page label, and the other several text at the top of the property page in the Wizard for setting the wizard97 style.

Void enablehelp ()

Set the psp_hashelp flag in m_psp. When this page is activated, the Help button of the attribute table is available.

Process notification messages

Cpropertypageimpl has a message ing processing wm_notify. If the notification code is the value of PSN _ *, onnotify () calls the corresponding notification handler function. This uses the virtual function mechanism in the compilation phase, so that the derived classes can easily reload these processing functions.

Due to the change in the design of wtl 3 and wtl 7, two sets of different notification processing mechanisms exist. In wtl 3, the value returned by the notification handler function is different from that returned by the PSN _ * message. For example, wtl 3 processes psn_wizfinish as follows:

Case psn_wizfinish:
Lresult =! Pt-> onwizardfinish ();
Break;

Onwizardfinish () expects to return true to end the wizard, and false to stop closing the wizard. This method is simple, but the general control of ie5 adds a new explanation for the return value processed by psn_wizfinish. It returns the handle of the window that requires focus. Wtl 3 programs will not be able to use this feature because it performs the same processing on all non-0 return values.

In wtl 7, onnotify () does not change the return value of the PSN _ * message. The processing function returns the valid value and correct behavior specified in any document. Of course, for forward compatibility, wtl 3 still uses the current default working method. To use the Message Processing Method of wtl 7, you must add a line definition before the line of including atldlgs. h:

# DEFINE _ wtl_new_page_policy_handlers

There is no reason to write new code without using the message processing function of wtl 7, so here we will not introduce the Message Processing Method of wtl 3.

Cpropertypageimpl provides the default notification message processing function for all messages. You can reload the message processing function related to your program to complete special operations. The default message processing function and corresponding behavior are as follows:

Int onsetactive ()-allows the page to become active

Bool onkillactive ()-allows the page to become inactive

Int onapply ()-return psnret_noerror, indicating that the Application Operation is successfully completed

Void onreset ()-no corresponding action

Bool onquerycancel ()-cancel Operation allowed

Int onwizardback ()-return to the previous page

Int onwizardnext ()-proceed to the next page

Int_ptr onwizardfinish ()-allow the Wizard to end

Void onhelp ()-no action

Bool ongetobject (lpnmobjectnotify lpobjectnotify)-no corresponding action

Int ontranslateaccelerator (lpmsg)-The returned psnret_noerror indicates that the message is not processed

Hwnd onqueryinitialfocus (hwnd hwndfocus)-If null is returned, the first control in the order of tab order is set to focus.

Create an Attribute Table

All of these classes are explained. Now we need an example program to demonstrate how to use them. The example project in this chapter is a simple SDI program that displays an image in the customer area and fills the background with a total color, you can use an option dialog box (an Attribute Table) to set the image and color, and a wizard (which will be introduced later ).

Simplest Attribute Table

First, use the wtl Wizard to create an SDI project, and then add an Attribute Table for the dialog box. First, change the dialog box style created by the Wizard to use it as an attribute page.

The first step is to remove the OK button because the attribute table does not want the attribute page to be closed by itself. In the style tab, change the style of the dialog box to child, thin border, select title bar, and in the more styles tab, select Disabled.

The second step (and the last step) is to create an Attribute Table in the onappabout () processing function. We use the non-customized cpropertysheet and cpropertypage classes:

LRESULT CMainFrame::OnAppAbout(...){CPropertySheet sheet ( _T("About PSheets") );CPropertyPage<IDD_ABOUTBOX> pgAbout;     sheet.AddPage ( pgAbout );    sheet.DoModal();    return 0;}

The result looks like this:

Create a useful property page

Not every attribute page in each attribute table is as simple as a dialog box. Most attribute pages need to use the derived class of cpropertypageimpl, so let's look at this class now. We created a new property page to set the image displayed in the background of the customer zone. It looks like this:

The style of this dialog box is the same as that of the page. We need a new class to work with this property page. we name it cbackgroundoptspage. This class is derived from the cpropertypageimpl class, which has a cwindataexchange to support DDX.

class CBackgroundOptsPage :    public CPropertyPageImpl<CBackgroundOptsPage>,    public CWinDataExchange<CBackgroundOptsPage>{public:    enum { IDD = IDD_BACKGROUND_OPTS };     // Construction    CBackgroundOptsPage();    ~CBackgroundOptsPage();     // Maps    BEGIN_MSG_MAP(CBackgroundOptsPage)        MSG_WM_INITDIALOG(OnInitDialog)        CHAIN_MSG_MAP(CPropertyPageImpl<CBackgroundOptsPage>)    END_MSG_MAP()     BEGIN_DDX_MAP(CBackgroundOptsPage)        DDX_RADIO(IDC_BLUE, m_nColor)        DDX_RADIO(IDC_ALYSON, m_nPicture)    END_DDX_MAP()     // Message handlers    BOOL OnInitDialog ( HWND hwndFocus, LPARAM lParam );     // Property page notification handlers    int OnApply();     // DDX variables    int m_nColor, m_nPicture;};

Notes for this class:

  • There is a public member named "IDD" that associates the dialog box with resources.
  • The message ing chain is similar to cdialogimpl.
  • The message ing chain links messages to cpropertypageimpl, so that we can process notification messages related to the Attribute Table.
  • An onapply () handler saves the user's selection when you click the OK button in the Attribute Table.

Onapply () is very simple. It calls dodataexchange () to update the DDX variable, and then returns a code to identify whether the attribute table can be closed:

int CBackgroundOptsPage::OnApply(){    return DoDataExchange(true) ? PSNRET_NOERROR : PSNRET_INVALID;}

We also add a tool | Options menu in the main window to open the Attribute Table. The processing function of this menu creates an attribute table, but adds a new attribute page cbackgroundoptspage.

void CMainFrame::OnOptions ( UINT uCode, int nID, HWND hwndCtrl ){CPropertySheet sheet ( _T("PSheets Options"), 0 );CBackgroundOptsPage pgBackground;CPropertyPage<IDD_ABOUTBOX> pgAbout;     pgBackground.m_nColor = m_view.m_nColor;    pgBackground.m_nPicture = m_view.m_nPicture;     sheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;     sheet.AddPage ( pgBackground );    sheet.AddPage ( pgAbout );     if ( IDOK == sheet.DoModal() )        m_view.SetBackgroundOptions ( pgBackground.m_nColor,                                      pgBackground.m_nPicture );}

The second parameter of the constructor of the Attribute Table is 0, indicating that the page with the index 0 is visible at the beginning. You can set it to 1, the page is displayed when the Attribute Table is displayed for the first time. Since the Demo code is used, I am a little lazy and use a public variable to associate it with the radio button on the cbackgroundoptspage property page. In the main window, I directly assign an initial value to it, read the attribute table when you click OK.

If the user clicks the OK button and domodal () plays the idok, we will notify the View window to use the new image and background color. Below are several views with different styles displayed on the screen:

Create a better Attribute Table class

It is a good idea to create an Attribute Table in onoptions (), but it is very bad to use a lot of initialization code here, which is not what cmainframe should do. A better way is to derive a new class from cpropertysheetimpl and complete these tasks in this class.

#include "BackgroundOptsPage.h" class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet>{public:    // Construction    CAppPropertySheet ( _U_STRINGorID title = (LPCTSTR) NULL,                         UINT uStartPage = 0, HWND hWndParent = NULL );    // Maps    BEGIN_MSG_MAP(CAppPropertySheet)        CHAIN_MSG_MAP(CPropertySheetImpl<CAppPropertySheet>)    END_MSG_MAP()    // Property pages    CBackgroundOptsPage         m_pgBackground;    CPropertyPage<IDD_ABOUTBOX> m_pgAbout;};

We use this class to encapsulate the details of each attribute page in the Attribute Table, move the initialization code to the internal part of the Attribute Table, complete the page adding by the constructor, and set other required flags:

CAppPropertySheet::CAppPropertySheet ( _U_STRINGorID title, UINT uStartPage,                                        HWND hWndParent ) :    CPropertySheetImpl<CAppPropertySheet> ( title, uStartPage, hWndParent ){    m_psh.dwFlags |= PSH_NOAPPLYNOW;    AddPage ( m_pgBackground );    AddPage ( m_pgAbout );}

In this way, the onoptions () processing function becomes simpler:

void CMainFrame::OnOptions ( UINT uCode, int nID, HWND hwndCtrl ){CAppPropertySheet sheet ( _T("PSheets Options"), 0 );    sheet.m_pgBackground.m_nColor = m_view.m_nColor;    sheet.m_pgBackground.m_nPicture = m_view.m_nPicture;    if ( IDOK == sheet.DoModal() )        m_view.SetBackgroundOptions ( sheet.m_pgBackground.m_nColor,                                      sheet.m_pgBackground.m_nPicture );}

Create a wizard style Attribute Table

Creating a wizard is similar to creating an Attribute Table. You only need to modify the previous and next buttons. Like MFC, You need to reload the onsetactive () function and call setwizardbuttons () to make the corresponding buttons available. Let's start with a simple introduction page. Its ID is idd_wizard_intro:

Note that there is no title bar text on this page, because all pages in the Wizard usually have the same title, and I prefer to set the text in the cpropertysheetimpl constructor, then each page uses this string resource. This is why I only need to change one character string to change the title text of all pages.

The implementation code for this page is in the cwizintropage class:

class CWizIntroPage : public CPropertyPageImpl<CWizIntroPage>{public:    enum { IDD = IDD_WIZARD_INTRO };    // Construction    CWizIntroPage();    // Maps    BEGIN_MSG_MAP(COptionsWizard)        CHAIN_MSG_MAP(CPropertyPageImpl<CWizIntroPage>)    END_MSG_MAP()    // Notification handlers    int OnSetActive();};

The constructor uses (reference) a string resource ID to set the text of the page:

CWizIntroPage::CWizIntroPage() :    CPropertyPageImpl<CWizIntroPage>( IDS_WIZARD_TITLE ){}

When this page is activated, the string ids_wizard_title ("psheets options wizard") will appear in the title bar of the wizard. Onsetactive () only makes the "Next" button available:

int CWizIntroPage::OnSetActive(){    SetWizardButtons ( PSWIZB_NEXT );    return 0;}

To implement a wizard, we need to create a class coptionswizard and add the menu tools | wizard in the main window. The constructor of the coptionswizard class is the same as the constructor of the capppropertysheet class. It only sets the necessary style labels and adds pages.

class COptionsWizard : public CPropertySheetImpl<COptionsWizard>{public:    // Construction    COptionsWizard ( HWND hWndParent = NULL );    // Maps    BEGIN_MSG_MAP(COptionsWizard)        CHAIN_MSG_MAP(CPropertySheetImpl<COptionsWizard>)    END_MSG_MAP()    // Property pages    CWizIntroPage m_pgIntro;};COptionsWizard::COptionsWizard ( HWND hWndParent ) :    CPropertySheetImpl<COptionsWizard> ( 0U, 0, hWndParent ){    SetWizardMode();    AddPage ( m_pgIntro );}

Tools of the cmainframe class | the wizard menu processing function looks like this:

void CMainFrame::OnOptionsWizard ( UINT uCode, int nID, HWND hwndCtrl ){COptionsWizard wizard;    wizard.DoModal();}

This is the result of the Wizard:

Add more property pages and use DDV

To make this wizard useful, we need to add a page for setting the view background color. This page also contains a checkbox to demonstrate how to handle the DDV verification failure and prevent the wizard from going to the next page. The following is a new page with the ID idd_wizard_bkcolor:

The implementation code of this class is in the cwizbkcolorpage class. The following is the relevant code.

class CWizBkColorPage :    public CPropertyPageImpl<CWizBkColorPage>,    public CWinDataExchange<CWizBkColorPage>{public:    // some stuff removed for brevity...    BEGIN_DDX_MAP(CWizBkColorPage)        DDX_RADIO(IDC_BLUE, m_nColor)        DDX_CHECK(IDC_FAIL_DDV, m_bFailDDV)    END_DDX_MAP()    // Notification handlers    int OnSetActive();    BOOL OnKillActive();    // DDX vars    int m_nColor;protected:    int m_bFailDDV;};

Onsetactive () is the same as the previous introduction page, which makes the previous and next buttons available. Onkillactive () is a new processing function that triggers DDV and then checks the m_bfailddv value. If it is true, it indicates that the checkbox is selected. onkillactive () will prevent the wizard from going to the next page.

int CWizBkColorPage::OnSetActive(){    SetWizardButtons ( PSWIZB_BACK | PSWIZB_NEXT );    return 0;}int CWizBkColorPage::OnKillActive(){    if ( !DoDataExchange(true) )        return TRUE;    // prevent deactivation    if ( m_bFailDDV )        {        MessageBox (          _T("Error box checked, wizard will stay on this page."),          _T("PSheets"), MB_ICONERROR );        return TRUE;    // prevent deactivation        }    return FALSE;       // allow deactivation}

It should be noted that the tasks in onkillactive () can also be completed in onwizardnext (), because both of these processing functions can maintain the wizard on the current page. They differ in that onkillactive () is called when the user clicks the previous and next buttons, while onwizardnext () is called only when the user clicks the next button. Onwizardnext () is also used for other purposes. For example, it can direct the Wizard to the specified page rather than the next page in sequence.

The example Project Wizard has two other pages, cwizbkpicturepage and cwizfinishpage. Since they are similar to the previous two pages, I will not introduce them in detail. For details, you can view the source code.

Other interface considerations Set an Attribute Table

The default location of the property page and Wizard appears in the upper left corner of the parent window:

This seems a bit uncomfortable, but there is a way to remedy it. The first method is to overload the cpropertysheetimpl: propsheetcallback () function, in which the Attribute Table is placed. Propsheetcallback () is the callback function of propsheetproc () introduced in msdn. The operating system calls this function when creating an Attribute Table. wtl also uses this time to subclass the Attribute Table window. So our first attempt is:

class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet>{//...    static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam)    {    int nRet = CPropertySheetImpl<CAppPropertySheet>::PropSheetCallback (                                                        hWnd, uMsg, lParam );         if ( PSCB_INITIALIZED == uMsg )            {            // center sheet... somehow?            }         return nRet;    }};

As you can see, we have encountered a difficult problem. Propsheetcallback () is a static method. You cannot use this pointer to access the Attribute Table window. Copy the code from cpropertysheetimpl: propsheetcallback () and add our own method? The method of linking the Code with the wtl of a specific version (this has been proved to be not a good method) should be as follows:

class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet>{//...    static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM)    {        if(uMsg == PSCB_INITIALIZED)        {            // Code copied from WTL and tweaked to use CAppPropertySheet            // instead of T:            ATLASSERT(hWnd != NULL);            CAppPropertySheet* pT = (CAppPropertySheet*)                                        _Module.ExtractCreateWndData();            // subclass the sheet window            pT->SubclassWindow(hWnd);            // remove page handles array            pT->_CleanUpPages();             // Our own code follows:            pT->CenterWindow ( pT->m_psh.hwndParent );        }         return 0;    }};

This is theoretically perfect, but I have tried that the position of the attribute table has not changed. Obviously, the code of the general control changes the position of the Attribute Table window after we call centerwindow.

You must discard this method that encapsulates code into the Attribute Table class, although it is a good solution. I went back to the original solution, that is, using the attribute page window and the Attribute Table window to collaborate with each other is the Attribute Table window setting. I added a user-defined message uwm_center_sheet:

  #define UWM_CENTER_SHEET WM_APP

Capppropertysheet processes the message in its message ing chain:

class CAppPropertySheet : public CPropertySheetImpl<CAppPropertySheet>{//...    BEGIN_MSG_MAP(CAppPropertySheet)        MESSAGE_HANDLER_EX(UWM_CENTER_SHEET, OnPageInit)        CHAIN_MSG_MAP(CPropertySheetImpl<CAppPropertySheet>)    END_MSG_MAP()     // Message handlers    LRESULT OnPageInit ( UINT, WPARAM, LPARAM ); protected:    bool m_bCentered;  // set to false in the ctor}; LRESULT CAppPropertySheet::OnPageInit ( UINT, WPARAM, LPARAM ){    if ( !m_bCentered )        {        m_bCentered = true;        CenterWindow ( m_psh.hwndParent );        }     return 0;}

Then, the oninitdialog () method of each attribute page sends the message to the Attribute Table window:

BOOL CBackgroundOptsPage::OnInitDialog ( HWND hwndFocus, LPARAM lParam ){    GetPropertySheet().SendMessage ( UWM_CENTER_SHEET );     DoDataExchange(false);    return TRUE;}

Add the m_bcentered flag to ensure that the Attribute Table window only responds to the first uwm_center_sheet message received.

Add an icon to the properties page

If you want to use the features of attribute tables and attribute pages that are not encapsulated by member functions, You need to directly access the relevant data structure: propsheetheader type (structure) in the cpropertysheetimpl class) members m_psh and cpropertypageimpl in the propsheetpage type (structure) member m_psp.

For example, to add an icon to the background page of the Option Attribute Table in the example, you need to add a flag and set several members in the propsheetpage structure of the attribute page:

CBackgroundOptsPage::CBackgroundOptsPage(){    m_psp.dwFlags |= PSP_USEICONID;    m_psp.pszIcon = MAKEINTRESOURCE(IDI_TABICON);    m_psp.hInstance = _Module.GetResourceInstance();}

The following figure shows the effects of the Code:

Continue

In chapter 9, I will introduce some tool classes of wtl, as well as the packaging classes of GDI objects and general dialogs.

Modify record

September 13,200 3: the first release of the article.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.