“智能”链接滚动条和编辑控件?

Posted

技术标签:

【中文标题】“智能”链接滚动条和编辑控件?【英文标题】:"Smart" Linked Scrollbar and Edit Controls? 【发布时间】:2009-06-16 20:58:10 【问题描述】:

我希望我能很好地解释我的问题,以便有人提供帮助。

基本上,我有一个水平滚动条(范围为 0 到 1000)和一个表示滚动条位置除以 1000 的编辑控件,以便用户可以使用滚动条选择 0 到 1 之间的数字范围最多 3 位小数精度(.001、.002、...、.987 等),或在编辑框中输入自己的数字。当他们滚动滚动条时,编辑控件中的数字会改变以反映新的滚动位置。当输入一个新数字时,滚动条会将自身设置到反映输入数字的新位置。同时,我也会在这个数字发生变化时(通过滚动条或编辑控件)执行一些计算,并在另一个对话框中显示结果。

这是我的问题:当用户在编辑控件中输入数字时,我无法决定使用哪些事件处理程序来产生正确的行为。

我使用一个名为fuelMargin 的双值变量来处理我的编辑控件,并使用一个名为fuelScroll 的CScrollBar 控件变量来处理滚动条。

在我的 HSCROLL 事件中,我将编辑控件设置为滚动位置 / 1000。那里没有问题;当用户滚动滚动条时,编辑框会正确更新。

至于编辑框,我的第一次尝试是 ONCHANGE 事件:

void MarginDlg::OnEnChangeFueledit()

    CEdit* editBox;
    editBox = (CEdit*)GetDlgItem(IDC_FUELEDIT);

    CString editString;
    editBox->GetWindowText(editString);

    if (editString.Compare(".") != 0 && editString.Compare("0.") != 0
        && editString.Compare(".0") != 0 && editString.Compare("0.0") != 0
        && editString.Compare(".00") != 0 && editString.Compare("0.00") != 0)
    
        UpdateData();
        UpdateData(FALSE);

        if (fuelMargin > 1)
        
            UpdateData();
            fuelMargin = 1;
            UpdateData(FALSE);
        
        if (fuelMargin < 0)
        
            UpdateData();
            fuelMargin = 0;
            UpdateData(FALSE);
        
        fuelScroll.SetScrollPos(int(fuelMargin*1000));
    

当用户尝试键入像 .5 或 .05 或 .005 这样的数字时,我需要其中的第一个 if 语句来防止执行 UpdateData()。不过,它确实会产生一些不稳定的行为。当用户尝试输入 .56 之类的内容时,在 .5 执行 UpdateData() 之后,数字变为 0.5,并且光标移动到最左侧,因此如果他们尝试输入 .56,他们会意外地结束输入 60.5 - 即 1,因为我不会让他们输入小于 0 或大于 1 的数字。但是,如果他们输入 0.56,则可以避免这种行为。

对于我的第二次尝试,我注释掉了我的 ONCHANGE 事件并改为放入了 ONKILLFOCUS 事件:

void MarginDlg::OnEnKillfocusFueledit()

    UpdateData();
    UpdateData(FALSE);

    if (fuelMargin > 1)
    
        UpdateData();
        fuelMargin = 1;
        UpdateData(FALSE);
    
    if (fuelMargin < 0)
    
        UpdateData();
        fuelMargin = 0;
        UpdateData(FALSE);
    
    fuelScroll.SetScrollPos(int(fuelMargin*1000));

所以现在用户可以完成输入他们的号码并且一切都很好 - 只要他们点击编辑框。在框失去焦点之前,滚动条不会移动,也不会计算结果。

我希望在输入框中的数字时计算结果;我希望滚动条在输入数字时移动。但我不希望打字被打乱,即框中的实际数字发生变化或光标以任何方式移动。

建议?

谢谢!

【问题讨论】:

【参考方案1】:

使用第一种方法,您似乎就快到了:唯一真正重要的问题是重复调用 UpdateData() 会在用户键入时弄乱光标位置。

鉴于您试图在控件之间进行相当复杂的交互,我建议根本不要在 OnChange() 中进行验证,以便用户在键入时可以键入他想要的内容(这就是大多数数字编辑控件的工作方式)。当用户关闭控件打开的对话框(或单击以某种方式使用数据的按钮)时,应触发验证,并显示适当的错误。

一旦您从 OnChange() 中的验证中解放出来,您可以通过简单地不在 OnChange() 中调用 UpdateData() 来解决“光标移动”问题。相反,只需解析“editString”中的数字,如果它在有效范围内,则更新滚动条。这样,滚动条会随着用户输入而更新,如果他们输入了无效值,滚动条就会保持原样,并且当他们移动到下一阶段时会出现错误。像这样的东西(未测试):

void MarginDlg::OnEnChangeFueledit()

  CString editString;
  GetDlgItem(IDC_FUELEDIT)->GetWindowText(editString);

  double editValue;
  if ((sscanf(editString,"%lf",&editValue) == 1)
  
    if (editValue >= 0.0) && (editValue <= 1.0))
      fuelScroll.SetScrollPos(int(editValue*1000));
  

唯一需要注意的重要问题是,如果用户键入了一些无效的值,或者超出了有效范围的数字,那么编辑控件和滚动条将不同步。最简单的处理方法就是决定编辑控件是“主”值:也就是说,当我们想知道用户输入了什么时,我们总是查看编辑控件,而不是滚动条,并验证数据.

至于您的第二种方法,一种可能的解决方案可能是实现一个计时器消息处理程序:在计时器处理程序中,您可以说相当于“如果用户一秒钟内没有输入任何内容,我会假设他们”重新完成,并解析数字并更新滚动条”。不过,我并不热衷于将其作为解决方案。

【讨论】:

我将这种方法用于我的 onchange 并且对结果非常满意。谢谢!我在 killfocus 中进行最小的错误检查,即确保数字在范围内,如果不是,我将其设置为 0 或 1(并相应地更新滚动条)。我也想在那里更新我的结果,但我遇到了一些问题......所以我目前正在调试它。但是非常感谢您的解决方案。【参考方案2】:

我建议注意 Enter 键,然后执行 UpdateData() 以及 OnKillFocus 和 OnChange。越人性化越好。

接下来,确保您的 UpdateData() 例程仅在您需要的方向上工作:

如果在编辑控件中输入“.5”,则在引发 OnChange 事件时运行 UpdateData() 例程,但请确保仅更新滚动条。在引发 OnKillFocus 之前,不要担心将编辑控件更新为“0.5”。任何反向(到编辑控件)的更新都会弄乱你的光标。如果您的编辑控件以某种方式绑定到此双精度变量并在 var 更改时自动更新,请考虑将双精度控件和您的编辑控件彼此分开,直到引发 OnKillFocus 事件。

同样的概念也适用于另一个方向。当用户滚动时,不要弄乱滚动条。只需更新编辑控件并保留它即可。

我应该补充一点,如果您知道如何正确使用 XAML 的数据绑定功能,它们在这种情况下确实很有帮助。对于我们原生类型的开发人员来说,很不幸的是,仅使用事件处理程序来实现类似的功能是如此困难。

【讨论】:

以上是关于“智能”链接滚动条和编辑控件?的主要内容,如果未能解决你的问题,请参考以下文章

隐藏列表框滚动条和同步滚动

miniui 怎么设置控件visiable

JComboBox自定义滚动条和去掉默认背景

JComboBox自定义滚动条和去掉默认背景

滚动条和滚动事件

修复了带有水平滚动条和垂直滚动条的标题表