一个控件需要多个数据上下文 - MVVM

11

我不确定我的问题标题是否准确地表达了我的问题,我会尽力解释:

我有一个网格单元的数据模板:(该网格属于第三方公司,但对我的问题并不重要)

<DataTemplate>
    <TextBlock>
        <Hyperlink Command="{Binding OpenLinkCommand}"> 
            <Hyperlink.ToolTip>
                <TextBlock Text="{Binding Data.MapLink}"/>
            </Hyperlink.ToolTip>
            <TextBlock Text="{Binding Data.MapLink}" TextDecorations="underline">
        </Hyperlink>
    </TextBlock>
</DataTemplate>

我想让DataTemplate显示一些超链接(“Data.MapLink”包含链接值的对象),每次单击此链接都会触发“OpenLinkCommand”命令。
问题是“Data.MapLink”和“OpenLinkCommand”位于不同的数据上下文中,因此我必须选择以下其中一个选项:
1. 保留超链接数据上下文 - 命令将不起作用,并且超链接将获取Data.MapLink值。 2. 将超链接数据上下文更改为命令数据上下文 - 命令将起作用,但超链接名称将为空。
遗憾的是,我没有将这些项目放在同一数据上下文中的选项,因此我必须找到一种方法告诉命令它的数据上下文是“X”,并告诉超链接它的数据上下文是“Y”。
我希望我的问题已经清楚。如何解决这个问题?

1
第二个DataContext是从哪里来的?它是否绑定到一个元素,因为您可以将元素的DataContext绑定到另一个元素并设置相对源。如果您在视图模型中可以访问两个DataContext(假设您正在使用视图模型),则可以拉入其他命令。 - TYY
在绑定中使用RelativeSource来查找正确的数据上下文。 - blindmeis
我刚刚向微软报告了这个错误(又一次?):https://connect.microsoft.com/VisualStudio/feedback/details/1398835/binding-of-readonly-dependencyproperty - Eric Ouellet
3个回答

21

有一些绑定属性可以用来指定不同于默认的DataContext的Source,最常见的是ElementName或RelativeSource,它们将在VisualTree中查找另一个UI元素,以便您可以绑定到其属性。

例如,以下使用ElementName告诉绑定应使用MyGridView作为绑定源,并绑定到MyGridView.DataContext.OpenLinkCommand。

<Hyperlink Command="{Binding ElementName=MyGridView, 
                             Path=DataContext.OpenLinkCommand}"> 

您还可以在绑定中使用RelativeSource来查找指定对象类型更高级别的VisualTree中的对象,并将其用作绑定源。此示例执行与上面的示例相同的操作,只是它使用RelativeSource而不是ElementName,因此您的GridView不需要指定Name

<Hyperlink Command="{Binding 
               RelativeSource={RelativeSource AncestorType={x:Type GridView}}, 
               Path=DataContext.OpenLinkCommand}"> 

第三种选项是将绑定的Source属性设置为静态对象,如下所示:

<Hyperlink Command="{Binding 
               Source={x:Static local:MyStaticClass.OpenLinkCommand}}"> 

根据你在这里的评论,关于绑定到单例,这可能是最好的选择。


你好,你的解决方案看起来是我正在寻找的。我会在今天尝试它,并报告是否解决了我的问题。 - Ofir

0

您需要拥有所需数据上下文的实例(通常在控件或窗口的资源中)。一旦您拥有了它,就应该能够明确地设置文本块的数据上下文,而不是自动继承父级数据上下文。

例如:

<TextBlock DataContext="{StaticResource MyDataMapLinkDataContext}" Text="{Binding Data.MapLink}" TextDecorations="underline"/>

1
是的,我也考虑过这种方式,但我无法创建另一个数据上下文实例(我的数据上下文必须是单例)。还有其他建议吗? - Ofir
你可以使用RelativeSource FindAncestor将超链接绑定到父数据上下文。确切的绑定代码取决于你如何构建你的XAML。如果需要帮助,请发布你的XAML代码。 - failedprogramming
另外,你说不能将两个命令放在同一个数据上下文中,如果你正在使用mvvm,可以将两个视图模型包装在第三个视图模型中,并公开这两个命令。 - failedprogramming
失败的编程 - 如果我理解你的解决方案是正确的,那么它并不好。如果我这样做,命令将会起作用(就像我之前说的那样),但我的超链接将无法访问Data.MapLink值。另外,请再次查看 - 我只有一个命令。绑定其他项目的某些属性,以便给我链接值。 - Ofir

0
如果您确实需要使用另一个属性作为额外的数据上下文,那么您可以使用附加属性。
XAML
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <ContentPresenter Content="{Binding (local:ExtraDataContextProvider.ExtraDataContext), RelativeSource={RelativeSource TemplatedParent}}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Grid>
        <Button Margin="172,122,131,79" Foreground="Green" local:ExtraDataContextProvider.ExtraDataContext="A test">
            test
        </Button>
    </Grid>
</Window>

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 WpfApplication1
{

    public class ExtraDataContextProvider : DependencyObject
    {
        public static object GetExtraDataContext(DependencyObject obj)
        {
            return (object)obj.GetValue(ExtraDataContextProperty);
        }

        public static void SetExtraDataContext(DependencyObject obj, object value)
        {
            obj.SetValue(ExtraDataContextProperty, value);
        }

        public static readonly DependencyProperty ExtraDataContextProperty = DependencyProperty.RegisterAttached("ExtraDataContext", typeof(object), typeof(ExtraDataContextProvider), new PropertyMetadata(null));
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接