2012-11-24 162 views
1

我曾經爲我自己設計過一個非常基本的WPF練習,也就是從ViewModel動態填充菜單,我遇到了一個奇怪的問題。考慮下面的主窗口中的標記:WPF DataTemplates - 爲什麼渲染的區別?

<Window x:Class="Demosne.Client.WPF.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525" 
    xmlns:project="clr-namespace:Demosne.Client.WPF"> 
<Grid> 
    <Menu Height="26" Name="menu1" VerticalAlignment="Top" HorizontalAlignment="Stretch" ItemsSource="{Binding MainMenuItems}"> 
     <Menu.ItemTemplate>     
      <HierarchicalDataTemplate > 
       <MenuItem Header="{Binding Text, Mode=OneTime}" ItemsSource="{Binding MenuItems}"/> 
      </HierarchicalDataTemplate> 
     </Menu.ItemTemplate> 
     <!--<MenuItem Header="File" /> 
     <MenuItem Header="Edit" />--> 
    </Menu> 
</Grid> 

和視圖模型(S):

public class MainWindowViewModel 
{ 
    private IList<MenuItemViewModel> _menuItems = new List<MenuItemViewModel>() 
    { 
     new MenuItemViewModel() { Text = "File" }, 
     new MenuItemViewModel() { Text = "Edit" } 
    }; 

    public IList<MenuItemViewModel> MainMenuItems 
    { 
     get 
     { 
      return _menuItems; 
     } 
    } 
} 

    public class MenuItemViewModel 
{  
    public string Text { get; set; } 

    public IList<MenuItemViewModel> MenuItems 
    { 
     get 
     { 
      return _menuItems; 
     } 
    } 

    private IList<MenuItemViewModel> _menuItems = new List<MenuItemViewModel>(); 
} 

我希望GUI來準確再現兩個commented-的結果在標記中出現兩行 - 兩個菜單項稱爲文件和編輯。

然而,紙版奇怪的行爲鼠標懸停:

標記版本:

enter image description here

綁定版本:

enter image description here

爲什麼他們有什麼不同?

+0

我認爲這應該幫助你。同樣的問題。 http://weblogs.asp.net/okloeten/archive/2007/11/14/5149692.aspx –

+0

情況類似,但不完全相同。我想在閱讀這篇文章之前我必須再讀幾遍這篇文章。我認爲'HierarchicalDataTemplate'應該通過隱式地創建一個'MenuItem'樹來解決這個問題,這樣就不會出現沒有可視化表示的子項的問題。 –

+0

地獄誰低估了這個? –

回答

3

你得到有趣的結果,因爲你並沒有真正使用HierarchicalDataTemplate的正確方法。

當您在菜單上設置itemssource時,它將爲集合中的每個對象創建一個MenuItem,如果您還提供了帶有itemssource集的HierarchicalDataTemplate,它將爲該集合中的每個子對象創建MenuItems以及在層次結構中。

就你而言,你已經在模板中自己添加了一個MenuItem,這是不需要的。框架爲您隱式創建這些項目。這導致菜單行爲異常。

因此,要獲得一個正確的結果,你應該做這樣的事情:

<HierarchicalDataTemplate ItemsSource="{Binding MenuItems}"> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Text="{Binding Text}" /> 
    </StackPanel> 
</HierarchicalDataTemplate> 

更新
通過對一些設置一個DataTemplate,你告訴WPF要控制,怎麼它的每一個應該顯示項目。

在這種情況下,使用HierarchicalDataTemplate,這是一個用於生成headered控件的模板。這種控件包含一個標題和一個項目集合。

將這種類型的模板應用於對象時,您將模板中的任何內容都用作標題,並且將通過將模板應用於集合集中的每個子對象來創建項目集合作爲模板上的ItemsSource。所以它會遞歸地將模板應用到層次結構中的所有對象。

在你的例子中,你有一個菜單。你可以只通過這樣創造的:

<Menu ItemsSource="{Binding MainMenuItems}" /> 

它的工作很好,但因爲你還沒有應用模板,告訴它如何集合中的項目應顯示,它只是創造一個菜單項爲itemssource中的每個對象,並在其上調用ToString()。這個值將被用作MenuItem的Header屬性。

由於這不是你想要的,所以你必須應用一個模板,告訴WPF你想要顯示的內容在隱式生成的MenuItem的頭部中。

在我的示例中,我只是製作了一個包含TextBlock的模板,該模板綁定到viewmodel的Text屬性。

更新2
如果您現在想對隱式創建的菜單項設置屬性,可以通過設置在HierarchicalDataTemplateItemContainerStyle財產必須是。這裏定義的樣式將應用於所有生成的菜單項。

所以在菜單項的命令屬性綁定到該視圖模型命令屬性,你可以這樣做:

<HierarchicalDataTemplate.ItemContainerStyle> 
    <Style TargetType="MenuItem"> 
     <Setter Property="Command" 
       Value="{Binding Command}" /> 
    </Style> 
</HierarchicalDataTemplate.ItemContainerStyle> 
+0

謝謝;就像@ kmatyaszek在下面的答案也會產生正確的視覺效果,但我仍然不確定我是否真的明白爲什麼。解釋爲什麼我所做的是錯誤的是非常清楚,但我不明白爲什麼StackPanel是必要的 - 不要MenuItems知道如何顯示子菜單項已經?我想要做的就是我)讓他們出席並且ii)設置他們的文本。在我看來,'DataTemplate'控制數據 - 而StackPanel不是數據,它是佈局。爲什麼涉及? –

+0

DataTemplate現在定義您想要顯示的數據。我已經更新了我的答案來解釋這一點。 –

+0

謝謝。不完全相信WPF在這種情況下表達了它實際上非常清楚的事情。這有點複雜的事情,因爲我打算將Command綁定到每個MenuItem - 但由於它們是由模板隱式生成的,所以我不再有權訪問標記中的Command屬性。 –

1

試試這個HierarchicalDataTemplate

<HierarchicalDataTemplate> 
    <MenuItem ItemsSource="{Binding MenuItems}"> 
     <MenuItem.Template> 
      <ControlTemplate> 
        <TextBlock Text="{Binding Text, Mode=OneTime}" /> 
      </ControlTemplate> 
     </MenuItem.Template> 
    </MenuItem> 
</HierarchicalDataTemplate> 

菜單項的ControlTemplate示例(msdn link

控件的Windows Presentation Foundation(WPF)有 的ControlTemplate包含控件的可視化樹。您可以通過修改該控件的控制模板 來更改控件的結構和外觀。沒有辦法只替換控件的可視化樹的部分 ;要更改 控件的可視化樹,必須將該控件的模板屬性設置爲其新的 並完成ControlTemplate。

好吧,讓我們看看現在的視覺樹。

如果我們有這樣的事情:

<Menu Height="26" Grid.Row="1"> 
    <MenuItem Header="File" /> 
    <MenuItem Header="Edit" /> 
</Menu> 

視覺的這棵樹如下表示:

enter image description here

好了,MenuItem具有ContentPresenterTextBlock

如果我們有HierarchicalDataTemplate會發生什麼?

<Menu.ItemTemplate>     
      <HierarchicalDataTemplate > 
       <MenuItem Header="{Binding Text, Mode=OneTime}" ItemsSource="{Binding MenuItems}"/> 
      </HierarchicalDataTemplate> 
    </Menu.ItemTemplate> 

讓我們視覺上的樹看到:

enter image description here

哇,它是什麼???

所以,如果你沒有指定的MenuItemControlTemplate做的,它本身就是MenuItemContentPresenter(你可以看到這第二個屏幕上)。因此,如果您想在HierarchicalDataTemplate(第一個屏幕)中使用它,則必須覆蓋MenuItemControlTemplate

下面是可視化樹我的解決方案:

enter image description here

+0

謝謝,值得一試,但我需要問 - 爲什麼?這意味着做什麼不同? –

+0

@TomW再次檢查我的答案:) – kmatyaszek

+0

謝謝。我認爲我現在明白了一點,並且我相信所得到的視覺樹的例子。這似乎與@Peter Hansen的答案相矛盾,因爲在HierarchicalDataTemplate中有一個'MenuItem'。我導致相信這會爲你創建MenuItems,所以這不會產生一個MenuItem作爲它的Header屬性嗎? –