11

I am not sure if my question header represent exactly my problem, I will do the best to explain:

I have a grid cell DataTemplate: (the grid belong to third party company but it`s not important for my question)

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

I want make this DataTemplate to show some hyperlink ("Data.MapLink" is the object which contain the link value) and each click on this link will fire the command "OpenLinkCommand".

The problem is that "Data.MapLink" and "OpenLinkCommand" are located in different dataContext and then I have to choose one of the next choices:

  1. leave hyperlink dataContext as it - the command won`t work and the hyperlink will get the Data.MapLink value.

  2. change hyperlink dataContext to the command datacontext - The command will work but the hyperlink name will be empty.

Regretfully I don`t have option put those items in same dataContext so I must find a way how to tell the command that it dataContext is "X" and tell the hyperLink that it dataContext is "Y".

I am hoping that my question is clear How can I solve this problem?

Deantwo
  • 1,113
  • 11
  • 18
Ofir
  • 5,049
  • 5
  • 36
  • 61
  • 1
    Where is the second datacontext coming from? is it bound to an element because you can bind an elements datacontext to another and just set the relative source. If you have access to both datacontext in your view model (assuming you are using a viewmodel), you can pull in the other command. – TYY Mar 19 '13 at 12:20
  • use RelativeSource in your Binding to find the right datacontext – blindmeis Mar 19 '13 at 13:25
  • I just reported the bug (another time?) to Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/1398835/binding-of-readonly-dependencyproperty – Eric Ouellet Jun 04 '15 at 13:39

3 Answers3

21

There are some binding properties you can use to specify a different Source for your binding than the default DataContext

The most common ones are ElementName or RelativeSource, which will find another UI element in the VisualTree so you can bind to it's properties.

For example, the following uses ElementName to tell the binding that it should use MyGridView as the binding source, and to bind to MyGridView.DataContext.OpenLinkCommand

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

You can also use RelativeSource in a binding to find an object further up the VisualTree of the specified object type, and use it as the binding source. This example does the same thing as the above example, except it uses RelativeSource instead of ElementName, so your GridView doesn't need to have a Name specified.

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

A third option is to set the binding's Source property to a static object, like this:

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

Based on your comment here about binding to a singleton, this would probably be the best option for you.

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Hello, Your solution seems like what I am looking for. I will try it today and report if it solved my problem. – Ofir Mar 19 '13 at 13:44
0

You will have to have an instance of the desired data context (usually in the resources of a control or window). Once you have that, you should be able to explicitly set the data context of the textblock instead of inheriting the parent data context automatically.

For example:

<TextBlock DataContext="{StaticResource MyDataMapLinkDataContext}" Text="{Binding Data.MapLink}" TextDecorations="underline"/>
Backlash
  • 541
  • 3
  • 8
  • 1
    Yes I thought about this way but I can`t create another datacontext instace (My datacontext must be singleTon). Any other suggestion? – Ofir Mar 19 '13 at 12:07
  • You can bind your hyperlink to the parent data context by using relativesource FindAncestor. Exact binding code depends on how you have structured your xaml. Maybe post your xaml if need help with this. – failedprogramming Mar 19 '13 at 12:17
  • Also, you said that you can't put both commands in the same datacontext, if you are using mvvm, you can wrap both view models in a third one and expose both commands. – failedprogramming Mar 19 '13 at 12:23
  • failedprogramming - If I understood right your solution, it`s not good. If i will do that the command will work (as I said before) but my hyperLink won`t have access Data.MapLink value Also please see again - I have just 1 command. The other item to bind it some property which gives me link value – Ofir Mar 19 '13 at 13:41
0

If you really do need to use another property for an extra data context then you can just use an attached property.

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>

Code

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();
        }
    }
}
Andy
  • 6,366
  • 1
  • 32
  • 37