Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF DataContextProxy in resources section

I’m having trouble using a DataContextProxy in my WPF application. When I place a DataContextProxy in the Resources section of a Grid it is never loaded. If I move the DataContextProxy out of the resource section everything works correctly.

I’ve been investigating this for some time and have tried a number of methods to debug the application.

  • I’ve placed a DebugConverter on the control that I’m trying to use the Proxy with. The Debug converter is never called.

  • I’ve used WPFSnoop to see if there are any binding errors. I get the following binding error on the DataContextProxy,

    System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:(no path); DataItem=null; target element is 'Proxy' (Name=''); target property is 'DataContext' (type 'Object')

  • I’ve placed a breakpoint on the loaded event of my DataContextProxy. The loaded event is never called and I’ve placed a breakpoint in the DataContextChanged event which is never called.

Here is some sample code to demonstrate this. Obviously I know I do not really need to use a DataContextProxy on the TextBox.

<Window x:Class="WpfDataContextProxyBug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfDataContextProxyBug"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:DebugConverter x:Key="DebugConverter"/>
    </Window.Resources>
    <Grid>
        <Grid.Resources>
            <local:Proxy x:Key="Proxy" DataContext="{Binding}" />
        </Grid.Resources>

    <TextBox DataContext="{Binding Path=Name, Source={StaticResource Proxy}, Converter={StaticResource DebugConverter}}"/>
    </Grid>
</Window>

The DataContextProxy class

public class Proxy : FrameworkElement
{
    public Proxy()
    {
        Loaded += DataContextProxy_Loaded;
        DataContextChanged += Proxy_DataContextChanged;
    }

    void Proxy_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {

    }

    void DataContextProxy_Loaded(object sender, RoutedEventArgs e)
    {

    }

}
like image 552
chrism233 Avatar asked Nov 04 '22 00:11

chrism233


1 Answers

I've also hit this problem when I tried to use the DataContextProxy in WPF. I've come up with a solution inspired by it which seems to handle the job pretty decently. Check it out:

public class DataContextProxyBehavior : Behavior<FrameworkElement>
{
    public Object DataSource
    {
        get { return (Object)GetValue(DataSourceProperty); }
        set { SetValue(DataSourceProperty, value); }
    }
    public static readonly DependencyProperty DataSourceProperty =
        DependencyProperty.Register("DataSource", typeof(Object), typeof(DataContextProxy), null);

    protected override void OnAttached()
    {
        base.OnAttached();

        // Binds the target datacontext to the proxy,
        // so whenever it changes the proxy will be updated
        var binding = new Binding();
        binding.Source = this.AssociatedObject;
        binding.Path = new PropertyPath("DataContext");
        binding.Mode = BindingMode.OneWay;
        BindingOperations.SetBinding(this, DataContextProxyBehavior.DataSourceProperty, binding);

        // Add the proxy to the resource collection of the target
        // so it will be available to nested controls
        this.AssociatedObject.Resources.Add(
            "DataContextProxy",
            this
        );
    }
    protected override void OnDetaching()
    {
        base.OnDetaching();

        // Removes the proxy from the Resources
        this.AssociatedObject.Resources.Remove(
            "DataContextProxy"
        );
    }
}

You just need to attach it to the parent element. The static resource reference in the child element will remain the same. I've posted an usage example here.

like image 133
Arthur Nunes Avatar answered Nov 12 '22 19:11

Arthur Nunes