C++ WinApi отделить UI View от кода

173
02 декабря 2018, 21:00

Можно ли сделать что-то похожее на XAML в C# (WPF)? Отделить UI от кода?

Answer 1

На самом деле можно, это называется Ресурсы диалоговых окон. Несмотря на название, их область использования куда шире, чем диалоговые окна в традиционном смысле. Например, ничто не мешает:

  • Сделать главное окно приложение диалоговым, если оно состоит исключительно из элементов управления и не содержит сложной логики отрисовки.

  • Создать диалоговое окно со стилем WS_CHILD и вкладывать его в другие окна (аналог User Control).

Пример. Нарисуем с помощью редактора ресурсов Visual Studio такое диалоговое окно:

Редактор ресурсов не содержится в студии Express-версий, поэтому при их использовании нужно будет найти сторонний редактор или редактировать определения ресурсов вручную (см. ниже).

В результате получим файл ресурсов и заголовочный файл. Откроем их в текстовом редакторе и удалим лишний текст, сгенерированный IDE, получим в результате:

Resource.h (идентификаторы элементов управления, используются для связи кода с UI)

#define IDD_CALC                        103
#define IDC_INPUT1                      1000 
#define IDC_INPUT2                      1001
#define IDC_PLUS                        1002
#define IDC_MINUS                       1003
#define IDC_MULTI                       1004
#define IDC_DIV                         1005

Resource.rc (определение UI, аналог XAML)

#include "resource.h"
#include "windows.h"
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_CALC DIALOGEX 0, 0, 217, 89
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Супер калькулятор"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "Выход",IDOK,160,68,50,14,WS_GROUP
    EDITTEXT        IDC_INPUT1,15,14,47,15,ES_AUTOHSCROLL | ES_NUMBER
    EDITTEXT        IDC_INPUT2,15,36,47,15,ES_AUTOHSCROLL | ES_NUMBER
    PUSHBUTTON      "+",IDC_PLUS,77,15,18,12
    PUSHBUTTON      "-",IDC_MINUS,105,15,18,12
    PUSHBUTTON      "*",IDC_MULTI,77,36,18,12
    PUSHBUTTON      "/",IDC_DIV,105,36,18,12
END

Далее добавим код:

Model.cpp

#include <windows.h>
#include "resource.h"
//Логика вычислений
BOOL Calculate(UINT idAction, int arg1, int arg2, int* pOutput) {
    if (pOutput == NULL) return FALSE;
    if (idAction == IDC_DIV && arg2 == 0) return FALSE;
    switch (idAction) {
    case IDC_PLUS:  *pOutput = arg1 + arg2; return TRUE;
    case IDC_MINUS: *pOutput = arg1 - arg2; return TRUE;
    case IDC_MULTI: *pOutput = arg1 * arg2; return TRUE;
    case IDC_DIV:   *pOutput = arg1 / arg2; return TRUE;
    }
    return FALSE;
}

ViewModel.cpp

#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#include "resource.h"
//объявления функций
BOOL                InitInstance(HINSTANCE, int);
INT_PTR CALLBACK    DlgCalc_Callback(HWND, UINT, WPARAM, LPARAM);
BOOL Calculate(UINT idAction, int arg1, int arg2, int* pOutput);
// Глобальные переменные
HINSTANCE hInst;                                
HWND hWnd = NULL;
// Точка входа
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);        
    // Выполнить инициализацию приложения
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    MSG msg;
    // Цикл основного сообщения
    while (GetMessage(&msg, nullptr, 0, 0))
    {        
          TranslateMessage(&msg);
          DispatchMessage(&msg);        
    }
    return (int) msg.wParam;
}
//Инициализация приложения
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; 
   //создать окно
   CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CALC), NULL, DlgCalc_Callback);
   if (!hWnd)
   {
      return FALSE;
   }
   //отобразить окно
   ShowWindow(hWnd, nCmdShow);   
   return TRUE;
}

//Логика обработки команд в окне
INT_PTR ProcessCommand(HWND hDlg, UINT idAction) {
    BOOL success = FALSE;
    int x, y, res;
    if (idAction == IDOK || idAction == IDCANCEL)
    {
        //закрыть окно
        DestroyWindow(hDlg);        
        return (INT_PTR)TRUE;
    }
    if (idAction >= IDC_PLUS && idAction <= IDC_DIV) {
        //получение входных данных
        x = GetDlgItemInt(hDlg, IDC_INPUT1, &success, TRUE);
        if (success == FALSE) {
            MessageBox(hDlg, L"Введенное число некорректно", NULL, MB_OK | MB_ICONERROR);
            return (INT_PTR)TRUE;
        }
        y = GetDlgItemInt(hDlg, IDC_INPUT2, &success, TRUE);
        if (success == FALSE) {
            MessageBox(hDlg, L"Введенное число некорректно", NULL, MB_OK | MB_ICONERROR);
            return (INT_PTR)TRUE;
        }
        //выполнение команды
        success = Calculate(idAction, x, y, &res);
        if (success == FALSE) {
            MessageBox(hDlg, L"При вычислении произошла ошибка", NULL, MB_OK | MB_ICONERROR);
            return (INT_PTR)TRUE;
        }
        //вывод результата
        SetDlgItemText(hDlg, IDC_INPUT2, L"");
        SetDlgItemInt(hDlg, IDC_INPUT1, res, TRUE);
        return (INT_PTR)TRUE;
    }
    return (INT_PTR)FALSE;
}

INT_PTR CALLBACK DlgCalc_Callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    UINT idAction;
    switch (message)
    {
    case WM_INITDIALOG:     
        hWnd = hDlg;
        return (INT_PTR)TRUE;
    case WM_COMMAND:    
        idAction = LOWORD(wParam);        
        return ProcessCommand(hDlg, idAction);
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    }
    return (INT_PTR)FALSE;
}

В итоге получаем приложение с разделением на декларативное определение UI, логику UI и логику вычислений, хотя по возможностям это и далеко от WPF/XAML.

READ ALSO
Qt, аналог fread()

Qt, аналог fread()

Мне необходимо считать из файла данные в двоичном виде - сначала несколько int, потом uint8_t, затем еще кое-что

136
ошибка при открытии терминала в netbeans: unable to start pty process

ошибка при открытии терминала в netbeans: unable to start pty process

Раньше в netbeans терминал Cygwin работал даже без открытия java проекта, но теперь после обновления пишет сообщение "unable to start pty process": Прошу тока не предлагать...

236
Xalan Java и русские буквы в имени файла

Xalan Java и русские буквы в имени файла

Хочу использовать Xalan-J для XSLT-преобразования (Java 8)Скачал библиотеку Xalan-J 2

162
Android выбор реализации Date format

Android выбор реализации Date format

У меня recyclerView с кучей элементов, каждый элемент должен отформатировать и отобразить Date, паттерн для всех одинаковый

173