This project is read-only.

ForView.Unwrap<T>

Feb 23, 2010 at 6:19 PM

 

Michael,

Do you know if there is a way to unwrap some object which is known to derive from some base class.

I have a view which can host multiple different view models at run time. I would like to invoke some common behavior for the datacontext of that view.
Since DataContext is wrapped into ObjectInstance<T> I can't cast it in the following manner.

public class ViewModelBase : IViewModel
{
}

public class SomeViewModel : ViewModelBase
{
}

public class MainViewModel : ViewModelBase

... in App.cs I set DataContext = ForView.Wrap(MainViewModel)

... via data binding at run-time SomeView gets some DataContext of type SomeViewModel or any other view model derived from ViewModelBase, this determined at run-time.

... in SomeView.cs I would like to know what kind of model is attached to it so that we can load a proper view for a view model. So I do this

ViewModelBase item = ForView.Unwrap<ViewModelBase>(DataContext) // this technically should not work, because generics to not support casting to derived types.
item.RunSomeMethod();

But I know that all the objects wrapped in ObjectInstance derive from ViewModelBase.

 

This is what I tried:

Implementing interface condition in ObjectInstance like so

public class ObjectInstance<TWrappedObjectType> : IObjectInstance, INotifyPropertyChanged, IDataErrorInfo where TWrappedObjectType:IViewModel

where IViewModel defines behavior and ViewModelBase implements it.

Then I tired casting like so:

 

<font size="2" color="#2b91af"><font size="2" color="#2b91af">

IViewModel

</font></font><font size="2" color="#2b91af">

 

</font>

item = ForView.Unwrap<IViewModel>(this.DataContext)

 

 But this still doesn't work.

Do you know if there is a proper way to get around this problem?
Maybe have a non-generic   public Object ForView.Unwrap(ObjectInstance DataContext)?
Or maybe ObjectInstance<T> can inherit from non-generic ObjectInstance, and within generic class override a method which returns wrapped object as Object (this actually works for me)?

    public abstract class ObjectInstance
    {
        public abstract Object GetWrappedObject();
    }

    [TypeDescriptionProvider(typeof(ClassInstanceProvider))]
    public class ObjectInstance<TWrappedObjectType> : ObjectInstance, IObjectInstance, INotifyPropertyChanged, IDataErrorInfo 
    {
        public override object GetWrappedObject()
        {
            return _wrappedObject;
        }

        // ... the rest of the code.

    }

 Basically I have a list of items in some higher level MainViewModel. All items derive from ViewModelBase, this list is an ItemSource for ListView control in a view which has MainViewModel as its datacontext. Template of this ListView has some container user control which suppose to load appropriate view based on its current DataContext. But I can't get to DataContext object because it is wrapped.

Thank you,

Ivan

 

 

Feb 24, 2010 at 4:23 AM

This is an unintended consequence of the last change I made. The generic parameter to ObjectInstance<T> is now the concrete class. So you would have to use ForView.Unwrap<SomeViewModel>(), which defeats the purpose.

Fortunately, ObjectInstance<T> implements the non-generic interface IObjectInstance. This is very close to your suggestion for a non-generic base class. I'm modifying ForView.Unwrap to take advantage of this.

 

public static TWrappedObjectType Unwrap<TWrappedObjectType>(object dataContext)
    where TWrappedObjectType : class
{
    IObjectInstance wrapper = dataContext as IObjectInstance;
    return
        wrapper == null
            ? default(TWrappedObjectType)
            : wrapper.WrappedObject as TWrappedObjectType;
}

Get this change and see if it works for you.

 

Feb 24, 2010 at 5:46 PM
Edited Feb 24, 2010 at 6:24 PM

That's right, I didn't even noticed IObjectInstance :(, how weird.

Thanks!

On the other note. I have ListView.DataContext and I bind List<T> to it. When I try to get that list out from ItemsSource tells me it is ObservableCollection<T>.

Do you some how convert it? I couldn't figure this out. May be some other components (I am using) are doing that.

Feb 24, 2010 at 6:46 PM

Yes, the wrapper is converting all Lists into ObservableCollections.

In most cases, you shouldn't need to unwrap the wrapped objects. If all of your logic is in the view model, then you already have access to the source data. I would look carefully at the code that references ItemsSource. Either that code is in the view, or it represents a backwards dependency. If it's in the view, think about moving it to the view model. If it's a backwards dependency, go to the source property before it gets wrapped.

Feb 24, 2010 at 6:52 PM
Edited Feb 24, 2010 at 6:54 PM

Ok, I understand about backward dependencies from ViewModel to View.

I am doing Drag and Drop on a ListView to itself (to rearrage items in the list), and that logic doesn't have to do anything with ViewModel, so that logic gets called directly from a view.

But I worked around this already. I just wasn't sure why I was getting ObservableCollection when I was putting List. So that is fine. I can work with ObservableCollection as long as I know this fact.

Thank you very much for your help! You are very quick!