2016-10-03 56 views
0

今天早上我问了一个问题here并且做了一个简单的工作示例,给了我一个不同于预期的行为。为什么DataBinding不会传播到UserControl

全工作样品在GitHub。主要部分代码如下。

在这种情况下,如果UserControl直接用作窗口的子窗口,则该命令永远不会传播到任何UserControl。如果UserControl用作ListBox ItemTemplate的DataTemplate,它也不起作用。

我还包括一个黑客按钮来解决命令到达UserControls的问题。黑客来自StackOverflow

但是使用hack并没有解释为什么UserControl没有收到命令(没有它)并且使用这个hack也破坏了良好编码的第一条规则:“高内聚和低耦合”。应该在窗口代码中使用hack以便管理UserControl中的Command,我的想法是它应该在默认情况下发生。

为什么命令不会默认传播给UserControl,我该怎么做才能以干净的方式将命令传播到UserControl?

注意:在UserControl中只使用一个CommandBinding(删除一个或另一个)不能解决问题。

部分代码:

<Window x:Class="CommandRoutingIntoItemTemplate.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:CommandRoutingIntoItemTemplate" 
     xmlns:system="clr-namespace:System;assembly=mscorlib" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition></RowDefinition> 
      <RowDefinition></RowDefinition> 
      <RowDefinition Height="Auto"></RowDefinition> 
     </Grid.RowDefinitions> 

     <local:UserControlTest></local:UserControlTest> 

     <ListBox Grid.Row="1"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <Border BorderBrush="Aqua" BorderThickness="2"> 
         <local:UserControlTest></local:UserControlTest> 
        </Border> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 

      <ListBox.Items> 
       <system:String>1</system:String> 
       <system:String>2</system:String> 
      </ListBox.Items> 
     </ListBox> 

     <StackPanel Grid.Row="2" Orientation="Horizontal"> 
      <Button Command="local:Commands.CommandTest">Put focus on TestBlock and click here to see if command occurs</Button> 
      <Button Click="AddHack">Hack</Button> 
     </StackPanel> 
    </Grid> 
</Window> 

用户控件:

<UserControl x:Class="CommandRoutingIntoItemTemplate.UserControlTest" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:CommandRoutingIntoItemTemplate" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"> 
    <UserControl.CommandBindings> 
     <CommandBinding Command="local:Commands.CommandTest" CanExecute="CommandTestCanExecuteUserControl" Executed="CommandTestExecuteUserControl"></CommandBinding> 
    </UserControl.CommandBindings> 
    <Grid> 
     <TextBox Text="UserControlTest"> 
      <TextBox.CommandBindings> 
       <CommandBinding Command="local:Commands.CommandTest" CanExecute="CommandTestCanExecuteTextBox" Executed="CommandTestExecuteTextBox"></CommandBinding> 
      </TextBox.CommandBindings> 
     </TextBox> 
    </Grid> 
</UserControl> 

回答

1

,你没有得到调用您的用户控制命令的原因是,你的按钮在不同的焦点范围。为了使WPF能够正确地为命令目标拾取焦点元素,它需要与命令调用控件处于单独的焦点范围内。

框架将只是从按钮中查找可视化树,在其焦点范围内查找命令绑定(在您的情况下它将找不到任何)。当框架在当前焦点范围中没有找到任何命令绑定时,只有它才会查找焦点元素的父焦点范围(在您的情况下,按钮位于没有父范围的焦点范围Window中,因此搜索将在那里结束)。

只需在您的StackPanel上设置FocusManager.IsFocusScope="True"即可解决问题。

您也可以在按钮上指定CommandTarget属性指向您的用户控件,而不依赖焦点。

+0

很好的解释!十分感谢!完美的作品。你知道我是否可以在UserControl上做些什么来确保它始终适用于每种类型的用法?虽然现在工作正常,但我可能想在其他地方使用我的UserControl,但我必须记住这一点。这听起来是一个更好的解决方案,只在一个常见的地方编写代码(UserControl),以确保在每种情况下的正确使用。 –

+0

我也用menuItem测试过,它工作正常可能是因为Menu元素有你提出给我的修正:FocusManager.IsFocusScope设置为true?这是我必须知道和记住的事情;-)? –

+0

@EricOuellet完全正确。 MenuItems显示在具有自己的焦点范围的弹出窗口中。 – ghord

相关问题