【问题标题】:Using the parent's DataContext (WPF - Dynamic Menu Command Binding)使用父级的 DataContext(WPF - 动态菜单命令绑定)
【发布时间】:2010-11-03 03:24:37
【问题描述】:

我查看了这个网站和谷歌,但解决方案对我不起作用。

我在 UserControl 的 ViewModel 上有一个命令。好吧,用户控件有一个绑定到 ObservableCollection 的 ItemsControl。在 ItemsControl.ItemTemplate 的 DataTemplate 中,我有一个按钮,我想使用该命令。我无法绑定命令,因为在 DataTemplate 内部,datacontext 不是 ViewModel,而是 ObservableCollection 的一项。

问题是:如果丢失了父数据上下文,如何将按钮绑定到命令?

我认为这需要一个简单的解决方案,因为我认为这是一个常见问题。

想象一下这个场景:

您有一个带有 observableCollection 作为 ItemsSource 的 ListBox 项,因此您在 ListBox 内为集合中的每个元素使用一个数据模板。好吧,您想删除选定的项目,并在该作业的每一行中放置一个按钮。 ¿ 你是怎么做到的?

在MVP中,我可以在按钮的点击事件中做到这一点:

Button but = e.Source as Button;

if (but != null)
      Presenter.ActualNote = but.DataContext as Note;

简而言之。您将行(所选项目)的数据上下文发送给演示者。

但是,我怎样才能以 mvvm 方式做到这一点?因为我需要使用命令,但我无法将命令分配给按钮,因为按钮对 ViewModel(命令所在的位置)一无所知。

如您所见,按钮必须存在于数据模板中,然后数据上下文不再是 ViewModel....这就是为什么我需要访问父级的 DataContext 以访问命令的原因。

我希望你能更好地理解我的问题。

谢谢。

【问题讨论】:

    标签: c# wpf dynamic menu commandbinding


    【解决方案1】:

    为您的按钮命令使用下面的绑定:

    {Binding DataContext.CommandName, 
             RelativeSource={RelativeSource FindAncestor, 
                             AncestorType={x:Type MyUserControl}}}
    

    这将告诉它找到您的 UserControl 并使用它的 DataContext。

    【讨论】:

    • 这是我找到的解决方案,但它对我不起作用。如果一个 put: Command="{Binding DataContext.CommandName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" 它表示应该在 FindAncestor 模式下为 RelativeSource 指定 AncestorType。问题出在哪里?感谢您的回答。
    • 我刚刚在一个示例中尝试过,它对我有用。这听起来像一个语法错误。您可以复制并粘贴按钮的 XAML 吗?
    • 哦,没有错误,VS标记了它,但它可以编译。我想知道一些简单的事情。我在用户控件中放置了一个 Tag 元素,我想在菜单项的标题中打印它: .但是,Visual Studio 说:无法通过引用'RelativeSource FindAncestor,AncestorType='System.Windows.Controls.UserControl',AncestorLevel='1''找到绑定源。绑定表达式:路径=标签;数据项=空;目标元素是'MenuItem'(名称='');目标属性是“标题”(类型“对象”
    • 不要为您的类型放置 UserControl,而是尝试放置您的控件类型。因此,如果您的控件名为 MyControl 并且您已将 xmlns:ctrl 映射到其命名空间,那么您将拥有 {x:Type ctrl:MyControl}。
    • 太棒了,就像一个魅力!附带说明:如果您仍希望保留按钮的数据上下文以传递给命令绑定,请使用 CommandParameter="{Binding Path=.}" 这样参数对象就是控件的上下文。跨度>
    【解决方案2】:

    好的,那么如何修改您的数据项类,使其具有引用整个模型视图的属性?

    如果您的 ItemsSource 是 ObservableCollection<DataItem> 类型,则像这样修改 DataItem 类型:

    public class DataItem
    {
        public BusinessObject Value { get; set; }
    
        private ModelView modelView;
    
        public ModelView ModelView
        {
            get
            {
                return modelView;
            }
        }
    
        public DataItem(ModelView modelView)
        {
            this.modelView = modelView;
        }
    }
    

    【讨论】:

    • 你能再解释一下吗?用我的英语,我无法理解你想向我解释什么。对不起。
    • 添加了更详细的解释。
    【解决方案3】:

    如果您想要一个肮脏的、破坏 MVVM 的解决方案,请在按钮上设置 Tag="{Binding}" 并处理 Click 事件。在事件处理程序中,调用 ViewModel 上的命令。

    【讨论】:

    • :P。我正在寻找一个不破坏 mvvm 的好解决方案:P,但是谢谢,这是一个解决方案:P
    • 我是一名 SL 开发人员,所以打破 mvvm 是理所当然的 :)
    • 也就是说,我在我的应用程序中使用了半 MVVM 模式,这很好。
    • 最后,我使用了这个解决方案,但没有使用 Tag 属性。只需单击事件并执行命令,非常脏,但我需要继续。改天我会尝试另一种解决方案。
    【解决方案4】:

    RelativeSource 有效,但我认为让控件在彼此的属性之间徘徊是不对的。奇怪的是,放置在项目视图中的按钮对外部数据源而不是绑定项目执行某些操作。您可能需要查看程序代码的设计。

    【讨论】:

    • 我喜欢你的回答,但我需要这个解决方案。我有一个里面有网格的项目控件,每个项目都是一个注释。好吧,我在网格中有一个上下文菜单,其中包含一些关于“选定”注释的选项。每个选项都是一个命令,但菜单的数据上下文是实际注释而不是视图模型。我不能把菜单放在外面,因为我需要每个项目的菜单。
    • 嗯,是的,不认为这是一个理想的解决方案,但您可以将您的 ModelView 对象作为静态资源放入 Window.Resources 元素中,可能使用 ObjectDataProvider,然后通过静态扩展名引用它在窗口任何部分的任何数据绑定中。
    • 我同意——当我使用这个解决方案时,它总是感觉像是一个 hack——但是一个有效的 hack。 ;)
    猜你喜欢
    • 2013-02-02
    • 2012-11-05
    • 2021-08-29
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-12-09
    相关资源
    最近更新 更多