RowDetailsTemplate and View.Model issue

Jan 17, 2011 at 4:03 PM

Hi,

I've been struggling for the last hour or so with a very weird issue.

I have a DataGrid like so:

 

        <Controls:DataGrid x:Name="ViewModelCollection" Grid.Row="1" IsReadOnly="False"
...
            <Controls:DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <ContentControl cal:View.Model="{Binding}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
                </DataTemplate>
            </Controls:DataGrid.RowDetailsTemplate>
        </Controls:DataGrid>

 

ViewModelCollection is a collection of ViewModels (daaah ! :D) a ViewModel has a View associated to it and I was hoping that this View will show in the details. But instead the application freezes when the grid is loading !

A couple of WTF's later, I came up with this:

 

        <Controls:DataGrid x:Name="ViewModelCollection" Grid.Row="1" IsReadOnly="False"
...
            <Controls:DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <ContentControl cal:View.Model="{Binding Path=Self}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
                </DataTemplate>
            </Controls:DataGrid.RowDetailsTemplate>
        </Controls:DataGrid>

 

With the property Self looking like this:

        public ViewModel Self
        {
            get { return this; }
        }

This works as expected. Alt-how I guess the first one should also work.

Regards,

Coordinator
Jan 17, 2011 at 5:24 PM

Can you send me a simple reproduction? Please send it to robertheisenberg at hotmail dot com I'm not sure what is going on here, but I would like to figure that out.

Jan 18, 2011 at 6:19 AM
Edited Jan 18, 2011 at 6:44 AM

Neata,

Don't forget that for binding properties of the viewmodel has to implement INotifyPropertyChange. 

For the first example you first have to set the datacontext (an instance of the viewmodelcollection). The constructor passed there in the datacontext has to be public(internal doesn't do the job).

for example like this:

  <MyControl.DataContext>                

 <ViewModel:ViewModelCollection></ViewModel:ViewModelCollection>            

   </MyControl.DataContext>        

Also if you set the binding in code you can verify with this interesting method if the property you are binding the specific control to actually exists.

 [Conditional("DEBUG")]        

[DebuggerStepThrough]        

public void VerifyPropertyName(string propertyName)        

{            // Verify that the property name matches a real,            

 // public, instance property on this object.        

 if (TypeDescriptor.GetProperties(this)[propertyName] == null)          

 {                string msg = "Invalid property name: " + propertyName;
                if (this.ThrowOnInvalidPropertyName)                  

 throw new Exception(msg);                else                    Debug.Fail(msg);            }      

 }

 

You probably already done something like this but reading this might trigger something alse:P

Jan 18, 2011 at 8:08 AM

Hi,

I've send the sample.

I manage to pinpoint the error to the View class SetContentPropertyCore property. A simple try...catch my do for a quick fix, I don't have time to investigate further.

        private static void SetContentPropertyCore(object targetLocation, object view)
        {
            var type = targetLocation.GetType();
            var contentProperty = type.GetAttributes<ContentPropertyAttribute>(true).FirstOrDefault() ?? new ContentPropertyAttribute("Content");

            PropertyInfo propertyInfo = type.GetProperty(contentProperty.Name);
            try
            {
                propertyInfo.SetValue(targetLocation, view, null);
            }
            catch (TargetInvocationException e)
            {
                // somehow we first try to add the shellview in the content control, why ? it should add the detailsView
                throw;
            }
        }
Regards,

Coordinator
Jan 18, 2011 at 4:25 PM

I implemented a try/catch that simply logs the error. You can get it in the latest revision. I did some investigation and it looks like this is a bug in the Silverlight DataGrid. The row template has the wrong datacontext and that causes the error because it thinks the context is the ShellViewModel and it tries to plug the ShellView into the details. By simply swallowing the exception, we fix the problem because the datagrid eventually switched the datacontext to the row model. Your property hack worked because the databinding was silently failing when it couldn't find the Self property on the ShellViewModel and then succeeding when the control actually switched the datacontext to the correct model.