【问题标题】:How to redirect bindings to "outside" of a XAML control如何将绑定重定向到 XAML 控件的“外部”
【发布时间】:2025-11-30 07:20:03
【问题描述】:

假设我有一个带有 ContextMenuListView。我想将它用作一个名为ListViewWithContextMenu 的单独控件。如何从 ContextMenu 重定向命令绑定,以便它们在 ListViewWithContextMenu 中可见?

示例代码:

ListViewWithContextMenu.xaml

<ListView x:Class="WpfApplication4.ListViewWithContextMenu"
         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:WpfApplication4"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<ListView.ContextMenu>
    <ContextMenu>
        <MenuItem  Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ListViewWithContextMenu}}, Path= PreviewCommand}" />
    </ContextMenu>
</ListView.ContextMenu>

ListViewWithContextMenu.xaml.cs

using System.Windows;
using System.Windows.Input;

namespace WpfApplication4
{
    public partial class ListViewWithContextMenu
    {
        public ICommand PreviewCommand
        {
            get { return (ICommand)GetValue(PreviewCommandProperty); }
            set { SetValue(PreviewCommandProperty, value); }
        }

        public static readonly DependencyProperty PreviewCommandProperty =
            DependencyProperty.Register("PreviewCommand", typeof(ICommand), typeof(ListViewWithContextMenu));

        public ListViewWithContextMenu()
        {
            InitializeComponent();
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApplication4.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:WpfApplication4"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext><local:MainWidnowViewModel></local:MainWidnowViewModel></Window.DataContext>
    <Grid>
            <local:ListViewWithContextMenu PreviewCommand="{Binding Preview}"></local:ListViewWithContextMenu>
    </Grid>
</Window>

MainWindowViewModel.cs

using System.Windows;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;

namespace WpfApplication4
{
    public class MainWidnowViewModel
    {
        public MainWidnowViewModel()
        {
            Preview = new DelegateCommand(PreviewMethod);
        }

        private void PreviewMethod()
        {
            MessageBox.Show("PREVIEW");
        }

        public ICommand Preview { get; set; }
    }
}

此代码不会调用我想要实现的 ViewModel 中的 PreviewMethod

【问题讨论】:

  • 我有办法解决你的问题。给我一点时间把它写下来作为答案......

标签: c# wpf xaml mvvm binding


【解决方案1】:

首先,不要将“ListViewWithDataContext”创建为常规用户控件,而是将其创建为 WPF“CustomControl”。为此,请执行以下步骤:

  • 删除现有的“ListViewWithDataContext”控件
  • 右键单击您的项目,然后单击“添加新项目”->“自定义控件 (WPF)”。

  • Visual Studio 将自动创建一个新的项目文件夹“Themes”并在其下方创建一个名为“Generic.xaml”的新 .xaml 文件,如下所示:

  • Visual Studio 还将直接创建一个名为“ListViewWithContextMenu.cs”的 C# 类文件

将以下代码从此处复制并粘贴到相应的文件中:

Generic.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4">


<Style TargetType="{x:Type local:ListViewWithContextMenu}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ListViewWithContextMenu}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">

                    <Border.ContextMenu>
                        <ContextMenu>
                            <MenuItem  Command="{TemplateBinding PreviewCommand}" />
                        </ContextMenu>
                    </Border.ContextMenu>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

ListViewWithContextMenu:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication4
{

   public class ListViewWithContextMenu : ListView
   {

       public ICommand PreviewCommand
       {
           get { return (ICommand)GetValue(PreviewCommandProperty); }
           set { SetValue(PreviewCommandProperty, value); }
       }

       public static readonly DependencyProperty PreviewCommandProperty =
        DependencyProperty.Register("PreviewCommand", typeof(ICommand), typeof(ListViewWithContextMenu));


       static ListViewWithContextMenu()
       {
           DefaultStyleKeyProperty.OverrideMetadata(typeof(ListViewWithContextMenu), new FrameworkPropertyMetadata(typeof(ListViewWithContextMenu)));
       }
   }
}

我测试了它,它可以工作。

我们在这里做了什么: 由于我们要做的只是利用现有的 ListView 控件,而不是更改任何现有的 UI 外观,因此我们只是创建了一个 ListView“CustomControl”,它基本上覆盖了当前模板。在 .cs 文件中,我们定义了 PreviewCommand DependencyProperty。

在 Generic.xaml 文件中,我们使用“边框”作为根级控件来容纳控件(尽管我们可以使用 Grid 等),并且我们向其添加了上下文菜单。

【讨论】:

  • 感谢您的回答!我明天会测试它:)
  • 没问题!如果您对我提供的答案有任何疑问,请联系我。