0

この質問と同じ問題があります。コンテキストメニューが表示されているときに、TreeViewItemがアクティブに選択されているように見せたい場合です。ただし、私のツリーでは、レベルごとに異なるタイプのオブジェクトがあるため、レベルごとに異なるContextMenuが必要です。HierachicalDataTemplateを使用してこれを達成しています。したがって、次のXAMLがあります。

<Window x:Class="Project.MainWindow">
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Project" ContentRendered="Window_ContentRendered">
    <Grid>
        <Grid.Resources>
            <DataTemplate x:Key="VolumeTemplate">
                <StackPanel Orientation="Horizontal">
                    <Image Source="{StaticResource VolumeIcon}" Margin="3,3,3,3" />
                    <TextBlock Text="{Binding Path=Name}" Margin="3,3,3,3">
                        <TextBlock.ContextMenu>
                            <ContextMenu>
                                <MenuItem Command="{Binding VolumeTestCommand}"
                                          Header="VolumeTest" />
                            </ContextMenu>
                        </TextBlock.ContextMenu>
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
            <HierachicalDataTemplate x:Key="ServerTemplate"
                                     ItemsSource="{Binding Volumes}"
                                     ItemTemplate="{StaticResource VolumeTemplate}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="{StaticResource ServerIcon}" Margin="3,3,3,3" />
                    <TextBlock Text="{Binding Name}" Margin="3,3,3,3" >
                        <TextBlock.ContextMenu>
                            <ContextMenu>
                                <MenuItem Command="{Binding ServerTestCommand}"
                                          Header="ServerTest" />
                            </ContextMenu>
                        </TextBlock.ContextMenu>
                    </TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
        </Grid.Resources>
        <TreeView HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                  ItemsSource="{Binding Servers}" Name="tvMain"
                  ItemTemplate="{StaticResource ServerTemplate}"
                  PreviewMouseRightButtonDown="tvMain_PreviewMouseRightButtonDown" />
    </Grid>
</Window>

そして背後にあるコード:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_ContentRendered(object sender, EventArgs e)
    {
        //set DataContext here, based on a login dialog
    }

    static T VisualUpwardSearch<T>(DependencyObject source) where T : DependencyObject
    {
        DependencyObject returnVal = source;

        while (returnVal != null && !(returnVal is T))
        {
            if (returnVal is Visual || returnVal is Visual3D)
            {
                returnVal = VisualTreeHelper.GetParent(returnVal);
            }
            else
            {
                returnVal = LogicalTreeHelper.GetParent(returnVal);
            }
        }

        return returnVal as T;
    }

    private void tvMain_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        TreeViewItem treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject);

        if(treeViewItem != null)
        {
            treeViewItem.IsSelected = true;
            e.Handled = true;
        }
    }
}

参照された質問からの回答を試しましたが、コンテキストメニューがTreeViewItemではなくTextBlockにあるため、機能しないと思います。DataTemplateのTreeViewItemにContextMenuをアタッチする方法、またはこの問題に対処する別の方法はありますか?

4

1 に答える 1

0

結局、添付プロパティを作成する必要がありました。これにより、コンテキストメニューがTextBlockからTreeViewItemに移動します。

public static readonly DependencyProperty StealContextMenuProperty = 
    DependencyProperty.RegisterAttached(
        "StealContextMenu", 
        typeof(bool), 
        typeof(ParentClass),
        new UIPropertyMetadata(false, new PropertyChangedCallback(SCMChanged))
    );

public static bool GetStealContextMenu(FrameworkElement obj)
{
    return (bool)obj.GetValue(StealContextMenuProperty);
}

public static void SetStealContextMenu(FrameworkElement obj, bool value)
{
    obj.SetValue(StealContextMenuProperty, value);
}

public static void SCMChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    FrameworkElement fe = sender as FrameworkElement;
    if (fe == null) return;

    bool value = (bool)e.NewValue;
    if (!value) return;

    fe.Loaded += new RoutedEventHandler(fe_Loaded);
}

public static void fe_Loaded(object sender, RoutedEventArgs e)
{
    FrameworkElement fe = (FrameworkElement)sender;
    FrameworkElement child;
    child = VisualDownwardSearch<FrameworkElement>(fe, x => x.ContextMenu != null);
    if (child != null)
    {
        fe.ContextMenu = child.ContextMenu;
        child.ContextMenu = null;
    }
}

public static T VisualDownwardSearch<T>(T source, Predicate<T> match)
    where T : DependencyObject
{
    Queue<DependencyObject> queue = new Queue<DependencyObject>();
    queue.Enqueue(source);
    while(queue.Count > 0)
    {
        DependencyObject dp = queue.Dequeue();

        if (dp is Visual || dp is Visual3D)
        {
            int childrenCount = VisualTreeHelper.GetChildrenCount(dp);
            for (int i = 0; i < childrenCount; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dp, i);
                if (child is T)
                {
                    T tChild = (T)child;
                    if (match(tChild)) return tChild;
                }
                queue.Enqueue(child);
            }
        }
        else
        {
            foreach (DependencyObject child in LogicalTreeHelper.GetChildren(dp))
            {
                if (child is T)
                {
                    T tChild = (T)child;
                    if (match(tChild)) return tChild;
                }
                queue.Enqueue(child);                    
            }
        }
    }
    return null;      
}
于 2012-10-18T14:03:59.187 に答える