2016-10-03 102 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

相關問題