当控件被禁用并在不可预测的时间再次启用时,维护选项卡顺序

本文关键字:启用 维护 顺序 选项 时间 控件 不可预测 | 更新日期: 2023-09-27 17:59:06

所以让我警告你;我正在寻求一种方法,使一个全面的黑客工作更好。我承认这是一次黑客攻击,当然我对整个问题的不同看法持开放态度。也就是说,如果我想进行代码截止,并且我们有一个有点激进的发布日期,我需要尽快完成这项工作。

因此,我无法立即进行大的更改,但我可以很容易地为该软件的第一个补丁进行更改。因此,短期和长期解决方案都是受欢迎的,但短期解决方案(如果可能的话)更可取。

好的,问题就在这里;我有一个向机器人硬件设备发送命令的应用程序。发送需要等待的命令(例如,花费未知时间的物理运动)后,UI将进入"忙碌状态"。当这种情况发生时,所有将发出另一个阻止命令的控件都将被禁用,因为机器无法处理它们。

当UI从繁忙状态出来时,所有控件都会再次启用,但由于明显的原因,选项卡顺序没有得到维护。这使得在输入区域之间切换变得不可能,而且,鉴于我自己几乎只使用键盘,这是不可接受的。我目前"解决"这个问题的方案是:

  1. 启动时,为应用程序中感兴趣的每个控件注册GotFocus事件。由于这是一个WPF MVVM应用程序,而且一些控件是动态创建的,所以这变得很困难。尽管如此,我可以搜索视觉和/或逻辑树,并通过
  2. 在GotFocus事件中,保存对控件的引用
  3. 当我们退出繁忙状态时,尝试将焦点设置为已保存的控件

这很有效。。。有点像。眼前的问题(正如我能想到的更多失败场景…)是,如果上下文菜单是打开的,那么这种逻辑会破坏另一个上下文菜单。考虑:

  1. 焦点在文本区域
  2. 用户右键单击另一个控件。该控件不会使获得焦点(即使我试图在鼠标处理程序中设置它)
  3. 当右键单击执行移动时,系统进入繁忙状态
  4. 当忙碌状态结束时,文本区域将成为焦点,上下文菜单将关闭

(现在我意识到,你可能会说,右键单击执行移动并显示上下文菜单是个坏主意,但上下文菜单命令是非阻塞的、启用的,并且它具有方便的特定域用途。)

就是这样。我甚至无法通过右键点击来获得焦点,而且将焦点设置为菜单本身也不起作用。我只是好奇是否有人对这样的事情有更好的计划。我意识到这很尴尬,而且是一个非常狭窄的环境。谢谢你能提前提供的任何帮助,我会再玩一些。。。

当控件被禁用并在不可预测的时间再次启用时,维护选项卡顺序

这是一个有趣的问题,遗憾的是,我想不出立即解决这个问题的办法,如果这是一件经常发生的事情,我会尽量不阻塞应用程序来解决这个问题。

如果机器人一次只能接收一个命令,那么它可能只需要实现一个命令队列,这样接口仍然可以使用,并且在机器人繁忙时,发出的命令会被推迟。就可用性而言,让这个队列非常可见可能是一个好主意,这样很明显命令已经发出并将被考虑在内。

与其实际禁用控件,不如在调用控件时检查它们是否应该在事件处理程序的开头发送命令。例如:

if(!bControlsEnabled) { return; }

当控件应该被"禁用"时,您也可以更改控件样式。

实现一个自定义行为,在控件的启用更改时侦听,保存聚焦元素,并在启用更改为true时重新聚焦该元素:

public class KeyboardFocus
{
    public static bool GetReturnFocusAfterEnable(DependencyObject obj)
    {
        return (bool)obj.GetValue(ReturnFocusAfterEnableProperty);
    }
    public static void SetReturnFocusAfterEnable(DependencyObject obj, bool value)
    {
        obj.SetValue(ReturnFocusAfterEnableProperty, value);
    }
    private static Dictionary<object, IInputElement> values = new Dictionary<object, IInputElement>();
    // Using a DependencyProperty as the backing store for ReturnFocusAfterEnable.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ReturnFocusAfterEnableProperty =
        DependencyProperty.RegisterAttached("ReturnFocusAfterEnable", typeof(bool), typeof(KeyboardFocus), new UIPropertyMetadata(false, PropertyChangedCallback));
    static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue)
        {
            UIElement element = d as UIElement;
            if (element != null)
            {
                element.IsEnabledChanged += (element_IsEnabledChanged);
            }
        }
        else
        {
            UIElement element = d as UIElement;
            if (element != null)
            {
                element.IsEnabledChanged -= (element_IsEnabledChanged);
            }
        }
    }
    static void element_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if ((bool)e.NewValue)
        {
            if (values.ContainsKey(sender))
            {
                Keyboard.Focus(values[sender]);
                values.Remove(sender);
            }
        }
        else
        {
            values[sender] = Keyboard.FocusedElement;
        }            
    }
}

这可以在XAML中实现,如下所示:

<Grid Name="layout" local:KeyboardFocus.ReturnFocusAfterEnable="True">

这在我的测试设置中是有效的,但如果你在禁用你的东西之前按下一个按钮,那么在这个东西被禁用时,这个按钮会有键盘焦点,但这会失败。

这个解决方案独立于您的体系结构,不需要表单中的代码。它有点快,有点脏,但它能完成任务。

解决方法:-不应禁用控件您应该使用XamBusyIndicator作为该控件的父级,并设置IsBusy的属性。如果IsBusy为true,它将禁用子控件,并且TabIndex属性也将以合理的方式运行。例如:-

<igComboEditor:XamBusyIndicator IsBusy="{Binding IsBusy}" Height="22">
                                <igComboEditor:XamBusyIndicator.Animation>
                                    <igComboEditor:CustomBusyAnimation DataTemplate="{StaticResource CustomBusyIndicatorTemplate}" />
                                </igComboEditor:XamBusyIndicator.Animation>
                                <igComboEditor:XamComboEditor Width="125"  VerticalAlignment="Bottom" Style="{StaticResource XamComboEditorStyle}"
                                                          ItemsSource="{Binding DataList}" IsEditable="False" 
                                                          SelectedItem="{Binding SelectedData}">
                            </igComboEditor:XamComboEditor>
                            </igComboEditor:XamBusyIndicator>

我会

OnCommandSentThatRequiresAWait Event Call SaveStateAndDisableUI()
SaveStateAndDisableUI()
{
   Foreach control in controlsCollection
   {
       switch(controlType)
       {
           for each control extract and save all you need. 
           if it's a menu check if it is opened up and so on, 
           does control have focus, 
           where is the caret located in case of textbox etc              
       }
   }
   save position of mouse x,y
   save position of form, state like if it is minimized, maximized
}
Corresponding RestoreState() should restore everything back up.