1

私が自分で考案した非常に基本的なWPFの演習、つまりViewModelからメニューに動的にデータを入力するという奇妙なことに遭遇しました。次のメインウィンドウのマークアップがあるとします。

<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>

およびViewModel(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は、マークアップ内の2つのコメント化された行(FileとEditと呼ばれる2つのMenuItem)の結果を正確に再現することを期待します。

ただし、バインドされたバージョンは、マウスオーバーで奇妙な動作をします。

マークアップバージョン:

ここに画像の説明を入力してください

バインドされたバージョン:

ここに画像の説明を入力してください

なぜ違うのですか?

4

2 に答える 2

3

HierarchicalDataTemplate実際には正しい方法を使用していないため、おかしな結果が得られます。

Menu に itemssource を設定すると、コレクション内の各オブジェクトに対して MenuItem が作成されます。またHierarchicalDataTemplate、itemssource セットを指定すると、そのコレクション内の各子オブジェクトに対しても MenuItem が作成されます。階層。

あなたの場合、テンプレートに MenuItem を自分で追加しましたが、これは必要ありません。フレームワークは、これらの項目を暗黙的に作成します。これにより、メニューが奇妙に動作します。

したがって、正しい結果を得るには、次のようにする必要があります。

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

更新
何かに DataTemplate を設定することで、WPF に、各項目をどのように表示するかを制御するように指示します。

この場合、HierarchicalDataTemplateヘッダー付きコントロールを生成するためのテンプレートである a が使用されます。この種類のコントロールには、ヘッダーとアイテム コレクションが含まれます。

この種のテンプレートをオブジェクトに適用すると、テンプレートに入力したものはすべてヘッダーとして使用され、ItemsSource として設定されたコレクション内の各子オブジェクトにテンプレートを適用することで、items コレクションが作成されます。テンプレート。したがって、階層内のすべてのオブジェクトにテンプレートを再帰的に適用します。

あなたの例では、メニューがあります。これを行うだけで作成できます:

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

それは問題なく動作しますが、テンプレートを適用していないため、コレクション内のアイテムをどのように表示するかを伝えるために、itemssource 内の各オブジェクトに対して MenuItem を作成し、それを呼び出すToString()だけです。この値は、MenuItem の Header プロパティとして使用されます。

それはあなたが望むものではないので、暗黙的に生成されたMenuItemのヘッダーにコンテンツとして表示したいものをWPFに伝えるために、テンプレートを適用する必要があります。

この例では、ビューモデルの Text プロパティにバインドする TextBlock を含むテンプレートを作成しただけです。

更新 2
暗黙的に作成されたメニュー項目にプロパティを設定したい場合は、にItemContainerStyleプロパティを設定する必要がありHierarchicalDataTemplateます。ここで定義されたスタイルは、生成されたすべてのメニュー項目に適用されます。

したがって、MenuItem の Command プロパティをビューモデルの Command プロパティにバインドするには、次のようにします。

<HierarchicalDataTemplate.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Command"
                Value="{Binding Command}" />
    </Style>
</HierarchicalDataTemplate.ItemContainerStyle>
于 2012-11-24T10:12:41.067 に答える
1

これを試してくださいHierarchicalDataTemplate

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

MenuItem ControlTemplate の例 ( msdn リンク)

Windows Presentation Foundation (WPF) のコントロールには、そのコントロールのビジュアル ツリーを含む ControlTemplate があります。コントロールの ControlTemplate を変更することで、コントロールの構造と外観を変更できます。コントロールのビジュアル ツリーの一部だけを置き換える方法はありません。コントロールのビジュアル ツリーを変更するには、コントロールの Template プロパティを新しい完全な ControlTemplate に設定する必要があります。

では、ビジュアル ツリーを見てみましょう。

次のようなものがあるとします。

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

このビジュアル ツリーを以下に示します。

ここに画像の説明を入力

わかりMenuItemましContentPresenterTextBlock

がある場合はどうなりHierarchicalDataTemplateますか?

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

ビジュアル ツリーで見てみましょう。

ここに画像の説明を入力

うーん、なんだろう???

ControlTemplateしたがって、 ofを指定しない場合はMenuItem、それ自体ContentPresenterが of になりMenuItemます (これは 2 番目の画面で確認できます)。したがって、(最初の画面)で使用する場合ControlTemplateはオーバーライドする必要があります。MenuItemHierarchicalDataTemplate

以下は、私のソリューションを含むビジュアル ツリーです。

ここに画像の説明を入力

于 2012-11-24T09:45:30.450 に答える