Using commands in datatemplates

Feb 28, 2012 at 10:27 AM

Hello,

I've read this post (http://catel.codeplex.com/discussions/249731) and but that's a WPF solution for my issue, so that's why I created a new discussion.

So what I'm trying to do: I have a itemscontrol where I put Itemssource to a collection in the viewmodel. When I want for example a ClickEvent, I put buttons as datatemplate in the itemscontrol. Because the datacontext in the datatemplate is set to the item in the itemssource, I cannot reach my viewmodel anymore.

Some time ago, I've come across an excellent solution to solve the Silverlight issues on this matter. I've created a DatacontextProxy class based on someone's blog:

#Region " Constructors "
        Public Sub New()
            AddHandler Me.Loaded, AddressOf DataContextProxy_Loaded
        End Sub
#End Region

#Region " Members & Properties "
        Public Shared ReadOnly DataSourceProperty As DependencyProperty = DependencyProperty.Register("DataSource", GetType(Object), GetType(DataContextProxy), Nothing)
        Public Property DataSource() As Object
            Get
                Return DirectCast(GetValue(DataSourceProperty), Object)
            End Get
            Set(value As Object)
                SetValue(DataSourceProperty, value)
            End Set
        End Property

        Private _BindingPropertyName As String
        Public Property BindingPropertyName() As String
            Get
                Return _BindingPropertyName
            End Get
            Set(value As String)
                _BindingPropertyName = value
            End Set
        End Property

        Private _BindingMode As BindingMode
        Public Property BindingMode() As BindingMode
            Get
                Return _BindingMode
            End Get
            Set(value As BindingMode)
                _BindingMode = value
            End Set
        End Property
#End Region

#Region " Events "
        Private Sub DataContextProxy_Loaded(sender As Object, e As RoutedEventArgs)
            Dim binding As New Binding()
            If Not String.IsNullOrEmpty(BindingPropertyName) Then
                binding.Path = New PropertyPath(BindingPropertyName)
            End If
            binding.Source = Me.DataContext
            binding.Mode = BindingMode
            Me.SetBinding(DataContextProxy.DataSourceProperty, binding)
        End Sub
#End Region

I put this class in the resources of my view with a key, so I can use this line of code to access it:

<Button Content="{Binding ItemType}" Command="{Binding Source={StaticResource DataContextProxy}, Path=DataSource.ChooseItemTypeCommand}" CommandParameter="{Binding Id}" />

It basically remembers the datacontext on the view and makes it accessible throughout the entire view.

This worked like a charm, until today. I have a view and which I put a childview. It's in that childview that my itemscontrol is located. When I put a breakpoint on the proxy class, I see that its datacontext is not the viewmodel of the childview, but that of the view on which the childview is located. The breakpoint is in the loaded event of that proxy class as you can see in the code above.

Any ideas on what is happening here or any other ideas on how I can access my viewmodel from within a datatemplate?

Feb 28, 2012 at 3:19 PM

The proxy is loaded as soon as the view is loaded. However, the view model might be set later (if you are using the dynamic view model creation of catel). Thus, what you should do is bind to the root visual (you know this, you are in the view), and then bind to the element name.

Thus, instead of creating the binding like this:

Dim binding As New Binding()
If Not String.IsNullOrEmpty(BindingPropertyName) Then
    binding.Path = New PropertyPath(BindingPropertyName)
End If
binding.Source = Me.DataContext

You can use:

var binding = new Binding(string.Format("DataContext.{0}", BindingPropertyName);
binding.Source = this; 

Unfortunately, you didn't include the the class definition of the proxy so I cannot determine whether it is placed on the UI or not. However, if you want you can also create a behavior (which I think would be a good addition to Catel). A behavior can contain more logic such as that it will only use the ViewModel of the associated control instead of any parent which has set the DataContext.

Feb 28, 2012 at 3:27 PM
Edited Feb 28, 2012 at 4:35 PM

Thank you for your reply:

The Proxy class is indeed placed on the UI, in the resources of the grid:

<Grid.Resources>
       <IMTToolkit:DataContextProxy x:Key="DataContextProxy"/>
</Grid.Resources>

The definition looks like:

Namespace Toolkit
Public Class DataContextProxy
Inherits FrameworkElement

So i'll try to proposed solution and get back to post the result.

Edit:

I've tried your solution, but no succes adjusting the proxy.
I managed to solve it using the elementName :

{Binding DataContext.SelectCellCommand, ElementName=LayoutRoot}

This worked in this case. So I will have to work with those 2 methods, cause where I use the proxy, this solution didn't work.