4
votes

after some time of silverlight-development I am currently doing some WPF work...

I often used this trick to make my life easier in some of my ValueConverters:

public class MyCovnerterWithDataContext : FrameworkElement, IValueConverter
{
    private MyDataContextType Data
    { 
        get { return this.DataContext as MyDataContextType; }
    }
    ....

Now I could access my DataContext in the Converter-Method, which comes handy in lots of situations as you can imagine.

I tried the same trick in WPF and found out, that unfortunately this does not work at all. There is the following error in the debug-output:

"Cannot find element that provides DataContext"

I suppose the resources aren't part of the visual tree in WPF whereas they are in Silverlight.

So - is my little trick possible in WPF as well? Is my little trick to be considered a dirty hack?

What's your opinion and suggestions?

Regards Johannes

Update: as requested some more info - actually a minimal example:

XAML:

    <Window x:Class="WpfDataContextInResources.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfDataContextInResources"
            x:Name="window"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <local:TestWrapper x:Key="TestObj" />
        </Window.Resources>
        <StackPanel>
            <TextBlock Text="{Binding Text}" />
            <TextBlock Text="{Binding DataContext.Text, Source={StaticResource TestObj}, FallbackValue='FALLBACK'}" />
        </StackPanel>
    </Window>

the .cs file:

    namespace WpfDataContextInResources
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new DataClass()
                    {
                        Text = "Hello",
                    };
            }
        }

        public class TestWrapper : FrameworkElement {}

        public class DataClass
        {
            public string Text { get; set; }
        }
    }

At least on my PC the lower text-block stays on the fallbackvalue

Update #2:

I tried the suggestion Matrin posted (deriving from DependencyObject, creating own DependencyProperty, etc) - it did not work either. This time however the error-message is a different one:

"System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:(no path); DataItem=null; target element is 'TestWrapper' (HashCode=28415924); target property is 'TheData' (type 'Object')"

I also have some suggestions for workarounds though:

1.) - Use MultiBinding --> not compatible with Silverlight, not enough in some cases.

2.) - Use yet another wrapping object, set DataContext by hand in code-behind, like this --> fully compatible with Silverlight (apart from the fact, that you can't use a Framework-Element directly - you have to make an empty class deriving from it)

xaml:

<Window.Resources>
    <FrameworkElement x:Key="DataContextWrapper" />
    <local:TestWrapper x:Key="TestObj" DataContext="{Binding DataContext, Source={StaticResource DataContextWrapper}}" />
    ...

code behind:

 //of course register this handler!
 void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
 {
        var dcw = this.Resources["DataContextWrapper"] as FrameworkElement;
        dcw.DataContext = this.DataContext;
 }
1
Interesting. With a stubbed MyDataContextType, this compiles and works for me (ex Silverlight guy). More info about what this IValueConverter is trying to do please... - Gayot Fow

1 Answers

0
votes

There may be a problem with your type being derived from FrameworkElement: From the msdn page about suitable objects in ResourceDictionaries

[...] Being shareable is required [...] Any object that is derived from the UIElement type is inherently not shareable [...]

Derive from DependencyObject instead:

public class TestWrapper : DependencyObject {}