尽管在WinForms TextBox中有焦点,WPF命令也会被激活

时间:2011-09-22 10:44:41

标签: wpf textbox commandbinding windowsformshost

假设:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Grid.CommandBindings>
        <CommandBinding Command="Cut" 
                        Executed="CommandBinding_Executed"/>
    </Grid.CommandBindings>

    <TextBox x:Name="WpfTextBox" 
             VerticalAlignment="Center" 
             Text="Hello there" />

    <WindowsFormsHost Grid.Column="1" 
                      VerticalAlignment="Center">
        <wf:TextBox x:Name="WinFormsTextBox" 
                    Text="Hello there" />
    </WindowsFormsHost>
</Grid>

WinFormsTextBox中按 Ctrl + X 会导致CommandBinding_Executed触发,但在WpfTextBox时不会。

我希望WpfTextBox的行为WinFormsTextBox。即该命令只应在没有焦点的情况下触发 - 它应该像全局视图命令一样工作。

注意:在命令的CanExecute事件中添加处理程序只会有助于阻止WinFormsTextBox中发生任何事情( Ctrl + X e.CanExecute设置为true时 - 意味着没有剪切文字),或完全正常执行时完全被吞下。

注2:剪切只是一个例子,我想要一个适用于任何命令绑定的解决方案。

注3:如果命令具有焦点,该命令应该能够从另一个控件触发 - 如ListView或其他东西。除非它有一个焦点在其中的TextBox(想想编辑模式)。

我不确定能做什么,我不想接受在CommandBinding_Executed方法中添加特定处理。但是,C'est la vie。

2 个答案:

答案 0 :(得分:0)

路由WPF命令,并在WindowsFormsHost的父控件中为Ctrl + X命令定义了CommandBinding。因此,如果您希望仅在WPF TextBox中处理它,请从Grid中删除CommandBinding并将其放在那里:

<TextBox>    
    <TextBox.CommandBindings>
        <CommandBinding Command="Cut" 
                        Executed="CommandBinding_Executed"/>
    </TextBox.CommandBindings>
</TextBox>

在路由命令时,Ctrl + X命令将由具有此命令绑定的第一个父级处理。只要您的焦点位于Grid的范围内并执行Ctrl + X命令,Grid命令绑定就会处理它。


以下是关于WPF中路由事件和命令的优秀文章:Understanding Routed Events and Commands In WPF


修改

如果您不希望在TextBox中处理该命令,则只需在Ctrl + X对您有意义的情况下定义CommandBindings。我认为你没有其他解决方案。无论如何,通常像Cut这样的ApplicationCommands是特定范围的上下文,例如RichTextBox或ListBox。


修改

您无法阻止WindowsFormsHost触发基础路由命令。但是你可以做的只是从CommandBindings范围中删除主机:

<Grid>
    <Grid.ColumnDefinitions>
       <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Grid Grid.Column="0">
        <Grid.CommandBindings>
            <CommandBinding Command="Cut" 
                            Executed="CommandBinding_Executed"/>
        </Grid.CommandBindings>

        <TextBox x:Name="WpfTextBox" 
                VerticalAlignment="Center" 
                Text="Hello there" />
    </Grid>

    <WindowsFormsHost Grid.Column="1" 
                      VerticalAlignment="Center">
        <wf:TextBox x:Name="WinFormsTextBox" 
                    Text="Hello there" />
    </WindowsFormsHost>
</Grid>

当然,如果你有更多的对象要布局,它可能会有点棘手,但它会起作用。只需从CommandBindings的范围中删除您不想处理命令的对象。

答案 1 :(得分:0)

对于一个稍微愚蠢的问题,这是一个稍微愚蠢的解决方案。这是我最终解决方案的简单版本:

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.ContinueRouting = IsFocusInWinFormsInputControl();
}

private static bool IsFocusInWinFormsInputControl()
{
    // Try get focus control
    WinForms.Control focusedWinFormsControl = GetFocusedWinFormsControl();

    // Is there anything and is it a textbox etc?
    return focusedWinFormsControl != null &&
        (focusedWinFormsControl is WinForms.TextBox ||
         focusedWinFormsControl is WinForms.RichTextBox);
}

private static WinForms.Control GetFocusedWinFormsControl()
{
    // Try get focused WinForms control
    IntPtr focusedControlHandle = GetFocus();

    WinForms.Control focusedControl = null;
    if (focusedControlHandle != IntPtr.Zero)
    {
        // Note: If focused Control is not a WinForms control, this will return null
        focusedControl = WinForms.Control.FromHandle(focusedControlHandle);
    }

    return focusedControl;
}

[DllImport("user32.dll")]
private static extern IntPtr GetFocus();

基本上,如果我们在WinForms TextBox之外,请添加命令验证逻辑以仅执行命令。