由于项目需要,某些输入框需要能限制只输入数字或小数,而且需要限制输入的数字范围。
而开源的Duilib只有 CEditUI可用,它的属性中与之相关的有一个numberonly属性,它并不满足需求。设置了numberonly之后,它只能输入数字,不能输入小数点或者正负号。
整理下当前的需求,大概是这样:
- 能输入小数点,能输入正负号
- 不能输入除了数字,小数点,正负号之外的其他字符
- 当输入的键不满足要求时,停留在上一次的输入
- 可限制输入的范围,例如 1 ~ 50, -1.0 ~ 1.0等
最终,基于CEditUI实现了一个可用的NumberEdit。
1
| class NumberEditUI : public CEditUI
|
它接受min 和 max两个属性,用于限制输入的范围。
重新实现SetAttribute函数,增加min和max。
1 2 3 4 5 6 7 8 9 10 11 12
| virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue) override { if (_tcsicmp(pstrName, _T("min")) == 0) { m_min = _tstof(pstrValue); m_bValidateMin = true; } else if (_tcsicmp(pstrName, _T("max")) == 0) { m_max = _tstof(pstrValue); m_bValidateMax = true; } else { CEditUI::SetAttribute(pstrName, pstrValue); } }
|
然后,监听CEditUI的DUI_MSGTYPE_TEXTCHANGED
消息
1 2 3 4
| NumberEditUI() : CEditUI() { this->OnNotify += DuiLib::MakeDelegate(this, &NumberEditUI::OnTextChanged); }
|
在OnTextChanged
中,对输入的字符串进行正则匹配,若满足,再判断范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| bool OnTextChanged(PVOID param) { TNotifyUI *event = (TNotifyUI *)param; if (event->sType != DUI_MSGTYPE_TEXTCHANGED) return true; CDuiString text = this->GetText(); regex rx(R"((?:^|\s)[+-]?(([[:digit:]]+(?:\.[[:digit:]]*)?))?(?=$|\s))");
if (text.IsEmpty()) goto VALID; if (!std::regex_match(FrameUtils::wstring2string(text.GetData()), rx)) goto INVALID; double current = _tstof(text.GetData()); if (m_bValidateMin && (current < m_min)) goto INVALID; if (m_bValidateMax && (current > m_max)) goto INVALID; VALID: m_oldValue = text; return true; INVALID: this->SetText(m_oldValue); this->SetSel(m_oldValue.GetLength(), m_oldValue.GetLength()); return true; }
|
完整的代码在这里。