MFC C++, производный от CEdit и производный от GetWindowText

Я исхожу из CEdit, чтобы создать собственный элемент управления. Было бы неплохо, если бы, как и в элементах управления MFC Feature Pack (Mask, Browsable), я мог бы изменить GetWindowText, чтобы фактически сообщать не то, что обычно отображается в элементе управления (например, преобразовать данные между шестнадцатеричным и десятичным, а затем вернуть обратно это нить).

Возможно ли это в производном CEdit?


person Mary Ellen Bench    schedule 26.10.2017    source источник
comment
Это может быть возможно, но это действительно не очень хорошая идея. Просто предоставьте дополнительную функцию для выполнения преобразования. Кроме того, я должен спросить, почему вы пишете код MFC в 2017 году?   -  person    schedule 27.10.2017
comment
Устаревший код. У меня большая кодовая база, и я хотел бы добавить и заменить некоторые из моих элементов управления редактированием другим, где мне не нужно изменять какой-либо управляющий код, а просто добавить производный код, который позволяет пользователю редактировать как десятичный или шестнадцатеричный, но все равно возвращает исходный способ. Как это сделать?   -  person Mary Ellen Bench    schedule 27.10.2017
comment
Вы можете украсть код у CMFCMaskedEdit или вместо этого вы можете управлять классом у CMFCMaskedEdit. CMFCMaskedEdit происходит от CEdit   -  person Barmak Shemirani    schedule 27.10.2017
comment
@NeilButterworth: похоже, вы знаете о другой поддерживаемой среде для разработки собственных настольных приложений в Windows. Который из них?   -  person IInspectable    schedule 27.10.2017
comment
@IInspectable Qt? .Сеть? Дельфы? Многие другие.   -  person    schedule 27.10.2017
comment
@NeilButterworth: .NET не совсем родной. Delphi не совсем C++. И Qt, да ладно, серьезно, я не просил какой-то сломанный инструментарий гетто, который нельзя заставить работать в Windows. Что-нибудь еще?   -  person IInspectable    schedule 27.10.2017
comment
@IInspectable Очевидно, что если вы не считаете .Net нативным, нет смысла в дальнейшем общении, даже если бы я хотел с вами дальше общаться, чего я не хочу.   -  person    schedule 27.10.2017
comment
@NeilButterworth: UWP является родным. .NET нет. Несмотря на то, что существует .NET Native, эта модель программирования и среда выполнения сильно отличаются от настоящего нативного кода. И это тоже не совсем С++.   -  person IInspectable    schedule 27.10.2017
comment
@IInspectable ATL+WTL? Это также в значительной степени динозавр, но у него меньше накладных расходов (здесь мы говорим в основном о двоичном размере), и вы можете обновляться с MFC более или менее постепенно.   -  person ivanmoskalev    schedule 27.10.2017
comment
@Barmak Shemirani Как им удалось управлять маской в ​​пакете функций. Должно быть возможно. Но это правда, может быть, из этого.   -  person Mary Ellen Bench    schedule 27.10.2017
comment
Некоторые фреймворки MVC основаны на Win32 API, что также лучше, чем использование MFC. @MaryEllenBench Я зажгу за тебя свечку.   -  person zzxyz    schedule 27.10.2017
comment
Исходный код для классов MFC должен быть доступен в "VS-path\VC\atlmfc\src\mfc", расположение зависит от версии VS. CMFCMaskedEdit перехватывает ввод и проверяет по маске.   -  person Barmak Shemirani    schedule 27.10.2017
comment
Бармак прав. Делайте то, что делает CMFCMaskedEdit (использует карту сообщений для перехвата WM_GETTEXT), и вы поймаете все случаи   -  person Joe    schedule 27.10.2017
comment
@ivanmoskalev: WTL не имеет официальной поддержки. ATL подходит для COM, но его недостаточно в качестве универсальной среды приложений. В любом случае, этот вопрос касается MFC. Предложение не использовать MFC не является ни ответом, ни полезным комментарием.   -  person IInspectable    schedule 27.10.2017
comment
@Мэри Эллен Бенч. Перезаписать WM_GETTEXT будет немного сложно. Нет собственного обработчика OnPaint/WM_PAINT. Таким образом, элемент управления использует сам WM_GETTEXT, чтобы получить текст для рисования. Возможно, было бы проще создать свой собственный шаблон/интерфейс редактирования для управления редактированием, который использует виртуальную функцию GetValue, возвращающую интерпретируемое значение. Вот как мы это делаем.   -  person xMRi    schedule 27.10.2017
comment
@xMRi Элемент управления не вызывает WM_GETTEXT внутри. Вместо этого он читает непосредственно из своего внутреннего строкового буфера. В противном случае CMFCMaskedEdit не сработало бы. Вы также можете проверить это, установив точку останова в переопределенном обработчике OnGetText(). Он никогда не сработает, если вы не вызовете GetWindowText() или не отправите WM_GETTEXT явно.   -  person zett42    schedule 27.10.2017
comment
@zett42. Это верно, но подпрограмма OnPaint сделает и это. Вот что я хочу сказать.   -  person xMRi    schedule 27.10.2017


Ответы (2)


Добавьте записи карты сообщений для WM_GETTEXT и WM_GETTEXTLENGTH в производный класс CEdit:

BEGIN_MESSAGE_MAP( CMyEdit, CEdit )
    ON_WM_GETTEXT()
    ON_WM_GETTEXTLENGTH()
END_MESSAGE_MAP()

Поскольку мы переопределяем эти сообщения, нам нужен метод получения исходного текста элемента управления редактирования без бесконечной рекурсии. Для этого мы можем напрямую вызвать оконную процедуру по умолчанию, которая называется DefWindowProc:

CStringW CMyEdit::GetTextInternal()
{
    CStringW text;
    LRESULT len = DefWindowProcW( WM_GETTEXTLENGTH, 0, 0 );
    if( len > 0 )
    {
        // WPARAM = len + 1 because the length must include the null terminator.
        len = DefWindowProcW( WM_GETTEXT, len + 1, reinterpret_cast<LPARAM>( text.GetBuffer( len ) ) );
        text.ReleaseBuffer( len );
    }
    return text;
}

Следующий метод получает исходный текст окна и преобразует его. Здесь было бы возможно все, включая пример преобразования между шестнадцатеричным и dec. Для простоты я просто заключаю текст в тире.

CStringW CMyEdit::GetTransformedText()
{
    CStringW text = GetTextInternal();
    return L"--" + text + L"--";
}

Теперь идет фактический обработчик для WM_GETTEXT, который копирует преобразованный текст в выходной буфер.

int CMyEdit::OnGetText( int cchDest, LPWSTR pDest )
{
    // Sanity checks
    if( cchDest <= 0 || ! pDest )
        return 0;

    CStringW text = GetTransformedText();

    // Using StringCchCopyExW() to make sure that we don't write outside of the bounds of the pDest buffer.
    // cchDest defines the maximum number of characters to be copied, including the terminating null character. 
    LPWSTR pDestEnd = nullptr;
    HRESULT hr = StringCchCopyExW( pDest, cchDest, text.GetString(), &pDestEnd, nullptr, 0 );
    // If our text is greater in length than cchDest - 1, the function will truncate the text and
    // return STRSAFE_E_INSUFFICIENT_BUFFER.
    if( SUCCEEDED( hr ) || hr == STRSAFE_E_INSUFFICIENT_BUFFER )
    {
        // The return value is the number of characters copied, not including the terminating null character. 
        return pDestEnd - pDest;
    }
    return 0;
}

Обработчик для WM_GETTEXTLENGTH говорит сам за себя:

UINT CMyEdit::OnGetTextLength()
{
    return GetTransformedText().GetLength();
}
person zett42    schedule 27.10.2017
comment
Это также изменит содержимое элемента управления редактированием на дисплее! Таким образом, он меняет любой вывод... это не сработает. - person xMRi; 27.10.2017
comment
В ПОРЯДКЕ. Интересный. Таким образом, OnPaint в элементе редактирования напрямую использует внутренний буфер. Странно, что в CMFMaskedEdit отображается внутренний перезаписанный буфер (m_str)... но это швы ОТ. - person xMRi; 27.10.2017

Спасибо всем за то, что указали мне правильное направление. Я попробовал OnGetText, но проблема, похоже, заключалась в том, что я не мог получить базовую строку, или она падала при вызове GetWindowText (или просто снова вызывала OnGetText... и не могла найти базовую строку).

Увидев, что они сделали на маскированном контроле, я дал более простой ответ, подобный этому. Есть ли недостатки? Казалось, что это не вызвало никаких проблем или побочных эффектов ...

Получать непосредственно от GetWindowText

void CConvertibleEdit::GetWindowText(CString& strString) const
{
    CEdit::GetWindowText(strString);

    ConvertibleDataType targetDataType;
    if (currentDataType == inputType)
    {

    }
    else
    {
        strString = ConvertEditType(strString, currentDataType, inputType);
    }
}
person Mary Ellen Bench    schedule 27.10.2017
comment
Не удалось получить базовую строку — вызовите DefWindowProc с параметром WM_GETTEXTLENGTH, чтобы получить длину строки, затем еще раз с WM_GETTEXT, чтобы получить базовую строку. Таким образом, вы больше не будете вызывать свой собственный обработчик. Если это все еще неясно, я могу добавить код в свой ответ. - person zett42; 27.10.2017
comment
Кстати, то, что вы здесь сделали, называется маскированием CWnd::GetWindowText(), потому что метод не является виртуальным. Недостатком является то, что ваш метод не будет вызван, если кто-то использует базовый класс для получения текста окна, что очень часто встречается при использовании MFC. Представьте, что кто-то перечисляет все дочерние окна, он получит указатель CWnd для каждого окна. Когда они вызывают CWnd::GetWindowText(), ваш метод не будет вызываться. - person zett42; 27.10.2017
comment
Для справки, тогда эта маскировка была сделана в исходном маскированном элементе управления? Если вы можете добавить больше к своему примеру, чтобы получить базовую строку. Это была главная часть, которой, казалось, не хватало. - person Mary Ellen Bench; 27.10.2017
comment
Я добавил метод GetTextInternal(), который делает это. - person zett42; 27.10.2017
comment
Итак, я предполагаю, что ваш вопрос не имеет ничего общего с CMFCMaskedEdit, который можно использовать, например, для того, чтобы заставить пользователя вводить только целые числа. - person Barmak Shemirani; 27.10.2017
comment
Не совсем, да. Было бы неплохо, если бы Microsoft каким-то образом скомпилировала всю эту функциональность в один элемент управления редактированием с параметрами, чтобы вы могли одновременно использовать маскировку, кнопки и т. д. - person Mary Ellen Bench; 27.10.2017