本文介绍了命令按钮的异常禁用行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很抱歉这么啰嗦,但我想把情况说清楚.如果您是 WPF 专家,请看一看.

有两个 CollectionViewSource 绑定到 ItemsControls,它们使用 UserControl 视图来显示 StackPanels 或自定义 >按钮.下面的屏幕截图中显示的每一侧都有一个.我遇到的问题是,当设置了父集合属性时,DataTemplate 视图中的所有按钮都被禁用.即使是上面的 2 个按钮也有同样的问题,即使它们在我最近的编辑之前工作.

There are two CollectionViewSource bound to ItemsControls that use UserControl Views to display either StackPanels or custom Buttons. There is one for each side shown in the screenshot below. The problem I'm encountering is that when the parent collection property is set, all of the buttons in the DataTemplate view are disabled. Even the 2 buttons higher up are having the same problem even though they worked before my recent edits.

如果我单击表单或按任意键,按钮将启用.一旦属性被重置为新编辑和排序的集合,它们就会再次禁用.冲洗并重复.这就是它的样子.第一帧是它的开始方式(使用 StackPanel 显示为灰色),第二帧是单击 RFS 按钮时的样子,第三帧是我单击任意位置或按下某个键时发生的情况.

If I click on the form, or press any key, the buttons enable. As soon as the property is reset to a newly edited and sorted collection, they disable again. Rinse and repeat. This is what it looks like. The first frame is how it starts (gray using StackPanel), the 2nd is what it looks like when the RFS button is clicked, and the 3rd frame is what happens when I click anywhere or press a key.

我一直在兜圈子尝试一些东西.唯一似乎有效的是代码隐藏的解决方法,它将焦点集中在一件事情上,然后再返回.但是,如果用户尝试使用其他仪表板项目之一,则这对用户不利.

I've been going in circles trying out things. The only thing that seems to work is a code-behind workaround that sets focus to one thing and then back. However, that would not be good for the user if they are trying to use one of the other dashboard items.

由于所有这些的 WPF 非常庞大,我将尝试仅包含相关部分.这些是 TabItemControl (UserControl) 上的 ItemsControls.

Since the WPF for all of this is very massive, I'll try to include just the relevant parts. These are the ItemsControls on the TabItemControl (UserControl).

        <!-- BID (SELL) DEPTH -->
        <ItemsControl Grid.Row="0" Grid.Column="0" x:Name="bidDepthList" ItemsSource="{Binding Source={StaticResource BidDepthCollection}}"
                      Visibility="{Binding Path=IsMultilegEnabled, Converter={StaticResource CollapsedConverter}}">
           <ItemsControl.ItemTemplate>
              <DataTemplate DataType="{x:Type viewmodels:DepthLevelViewModel}">
                 <v:DepthLevelRowView x:Name="BidDepthLevelRowViewControl" DataContext="{Binding}" HorizontalAlignment="Center" Margin="1,0,1,3" />
              </DataTemplate>
           </ItemsControl.ItemTemplate>
        </ItemsControl>

        <!-- ASK (BUY) DEPTH -->
        <ItemsControl Grid.Row="0" Grid.Column="1" x:Name="askDepthList" ItemsSource="{Binding Source={StaticResource AskDepthCollection}}"
                      Visibility="{Binding Path=IsMultilegEnabled, Converter={StaticResource CollapsedConverter}}">
           <ItemsControl.ItemTemplate>
              <DataTemplate DataType="{x:Type viewmodels:DepthLevelViewModel}">
                 <v:DepthLevelRowView x:Name="AskDepthLevelRowViewControl" DataContext="{Binding}" HorizontalAlignment="Center" Margin="1,0,1,3" />
              </DataTemplate>
           </ItemsControl.ItemTemplate>
        </ItemsControl>

所使用的视图在网格内有 4 个控件".根据状态(RFS OFF/RFS ON)和它们在哪一侧(Sell/Buy),一次只显示其中一个.其他的都崩溃了.如您所见,这很好.

The view being used has 4 "controls" inside of a grid. Only one of them is displayed at a time depending on the state (RFS OFF/RFS ON) and which side they are on (Sell/Buy). The others are collapsed. As you can see, this is fine.

它们之间唯一的共同因素是它们设置了 Command,顶部的大多数控件都正确禁用/启用.如果采取任何鼠标或键盘操作,按钮正确启用的事实告诉我 CanExecute 处理程序正在工作,只是不是立即.在我进行这些更改后,其他控件开始工作,但随后大按钮开始出现异常深度按钮一直在做.

The only common factor between them is that they have their Command set, as do most of the controls at the top that are disabling/enabling correctly. The fact that the buttons enable correctly if any mouse or keyboard action is taken tells me that the CanExecute handler is working, just not immediately. The other controls started working after I made these changes, but then the big buttons started misbehaving like the depth buttons have been doing.

我在更改集合后尝试使用 CommandManager.InvalidateRequerySuggested();,但这没有帮助.

I've tried using CommandManager.InvalidateRequerySuggested(); after altering the collections, but that didn't help.

注意:即使是这样简单的事情也会发生这种情况:

NOTE: This is also happening even for something as simple as this:

<Button x:Name="TestBuyButton" Command="{x:Static ptcommands:OrderCommands.Buy}" CommandTarget="{Binding RelativeSource={RelativeSource Self}}" Content="Test Buy" />

我知道布尔值设置正确,因为我添加了测试 CheckBoxes 以使用 IsChecked 显示当前值.而且,只要采取任何输入操作,它们都会启用,包括极其基本的 Button.

I know the booleans are set correctly because I added test CheckBoxes to display the current value with IsChecked. And, they all enable, including the extremely basic Button, as soon as any input action is taken.

还有什么我遗漏的地方或我可以采取的不同方法吗?

我最终使用 Dispatcher 从事件线程到 UI 线程调用我的显示更新例程.布尔值已设置,但 WPF 仍然没有重新查询 Command.但是 .. CommandManager.InvalidateRequerySuggested() 调用然后起作用了!我唯一不喜欢的就是这听起来像是广播无效.我不希望重新查询所有命令.我只想重新查询 Buy 和 Sell 命令.我尝试了各种奇怪的方法,试图让那些工作正常工作,但到目前为止,除了全局之外没有任何效果.

I ended up using the Dispatcher to invoke my display update routine from the event thread over to the UI thread. The boolean values get set, but WPF still didn't requery the Command. BUT.. the CommandManager.InvalidateRequerySuggested() call then worked! The only thing I don't like about that is it sounds like a broadcast invalidation. I don't want all commands to be requeried. I just want the Buy and Sell commands to requery. I've tried all sorts of weirdness trying to get just those to work, but nothing has worked so far other than the global.

编辑 2: 看起来好像 InvalidateRequerySuggested 是要走的路.

EDIT 2: It appears as though the InvalidateRequerySuggested is the way to go.

来自 MSDN CommandManager.InvalidateRequerySuggested:

CommandManager 在确定命令目标何时发生变化时只关注某些条件,例如键盘焦点的变化.在 CommandManager 没有充分确定导致命令无法执行的条件变化的情况下,可以调用 InvalidateRequerySuggested 以强制 CommandManager 引发 RequerySuggested 事件.

这将解释为什么焦点改变和按键会导致按钮启用.该页面还显示将呼叫放入计时器.因此,我认为它并不像我想象的那样资源密集.所以,我想这是我的永久解决方案.

This would explain why the focus changes and keypresses would cause the buttons to enable. The page also shows putting the call in a timer. Because of this, I assume it is not as resource intensive as I thought. So, I guess it's my permanent solution.

推荐答案

我必须进行一些更改才能在不更改焦点的情况下启用按钮.

I had to make a few changes to get the buttons to enable without focus changes.

布尔值是在从事件线程调用的方法中设置的.我曾尝试同时调用 CommandManager.InvalidateRequerySuggested,但没有任何反应.最终我认为这可能与线程有关.这就是最终解决它的原因.

The boolean values were being set within a method called from an event thread. I had tried calling CommandManager.InvalidateRequerySuggested along with it, but nothing happened. Eventually I thought it might have something to do with the thread. This is what ended up resolving it.

在选项卡管理器类(包含事件处理程序和其他逻辑)可以访问的父窗体中,我添加了一个调用方法:

In the parent form that the tab manager class (containing the event handlers and other logic) has access to, I added an invoke method:

  Public Sub InvokeOnUiThread(ByRef uiAction As Action, Optional ByRef doAsync As Boolean = False)

     Dim dispatchObject As Dispatcher = orderTicketView.Dispatcher

     If (dispatchObject Is Nothing OrElse dispatchObject.CheckAccess()) Then
        uiAction()
     Else
        If doAsync Then
           dispatchObject.BeginInvoke(uiAction, DispatcherPriority.Normal)
        Else
           dispatchObject.Invoke(uiAction, DispatcherPriority.Normal)
        End If
     End If

  End Sub

在选项卡管理器事件处理程序中,我将其更改为通过调用程序调用更新例程:

In the tab manager event handler, I changed it to call the update routine through the invoker:

formOrderTicketView.InvokeOnUiThread(New Action(AddressOf UpdateButtons))

UpdateButtons 方法的底部,如果发生需要重新查询的更改,我添加了对 CommandManager 的调用:

At the bottom of the UpdateButtons method, I added a call to the CommandManager if a change has been made that would require a requery:

CommandManager.InvalidateRequerySuggested()

我不知道这会对性能造成什么类型​​的影响,但显然 WPF 在焦点发生变化等情况下会以自己的方式执行它.直接调用它是建议的强制方法.

I do not know what type of performance hit this would have, but apparently WPF executes it in its own way when focus changes and such. Calling it directly is the advised way to force it.

来自 MSDN CommandManager.InvalidateRequerySuggested:

CommandManager 在确定命令目标何时发生变化时只关注某些条件,例如键盘焦点的变化.在 CommandManager 没有充分确定导致命令无法执行的条件变化的情况下,可以调用 InvalidateRequerySuggested 以强制 CommandManager 引发 RequerySuggested 事件.

既然它现在可以工作,我就以此为解决方案.

Since it is working now, I am taking this as the resolution.

这篇关于命令按钮的异常禁用行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-03 14:45