This is a migrated thread and some comments may be shown as answers.

Bind To Model could be better

5 Answers 206 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Mark Rabjohn
Top achievements
Rank 1
Mark Rabjohn asked on 10 May 2010, 12:31 PM
Hi,

Firstly, I have to say that the Bind to Model Mappings stuff is absolutely ace - being able to map an entire abitrary tree of objects without adhering to interface contracts is brilliant!

For my own application however I could do with something a little smarter with respect to unknown types.

For example, in my CMS application, I have a class for Content, that derives from IContentNode. In the future I would more than likely expect to derive new classes from Content.

This mapping stuff really needs 2 things:

1) An ability to fall back to a mapping for a base type i.e. I don't have a mapping for Content, but I do have one for IContentNode, so it uses that instead.
2) An ability to keep running when a type has no mapping whatsoever. Clearly if we had #1, we could add a mapping for System.Object, we could add a hidden TreeViewItem, and a flag to recurse no further would be good (does it have one already?).

Currently, these scenarios end up with a LINQ error stating that the Enumerable list is empty.

What do other users think?


Obviously if I'm missing something, and this functionality already exists, then please set me straight on this.

Best Regards,

Mark

5 Answers, 1 is accepted

Sort by
0
Atanas Korchev
Telerik team
answered on 10 May 2010, 12:39 PM
Hello Mark Rabjohn,

I am not 100% sure that I understand your suggestions. Some sample code would be of great help - just paste here how your model looks like, how you would like to bind it and what the desired behavior is.

Regards,
Atanas Korchev
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
0
Mark Rabjohn
Top achievements
Rank 1
answered on 10 May 2010, 01:40 PM
It's abit tricky to supply full code, because in my head it's a fairly simple concept, let me explain three things 1) My Class Inheritance Hierarchy 2) The simple data structure 3) How I'm using the TreeView.

1) Class Inheritance Hierarchy

My Base class is an IContentNode, it has a Guid, and a ChildNodes collection.

Next I have a Content class which inherits IContentNode and adds a Title property.

Next I have a Site class, which inherits Content.

i.e. Site inherits Content which in turn inherits IContentNode.

2) The simple data structure

For now, I have a site node that has two child nodes which are both Content nodes.

Site
+-- Content
+-- Content

3) My use of the TreeView control:

            <%= Html.Telerik().TreeView() 
            .Name("ContentsTreeView") 
            .ShowCheckBox(true) 
            .ClientEvents(events => events.OnLoad("ContentTreeView_OnLoad").OnSelect("ContentTreeView_OnSelect") ) 
            .BindTo((IEnumerable<MyNamespace.Site>)ViewData["Contents"], mappings =>  
            { 
                mappings.For<MyNamespace.Site>(binding => binding 
                        .ItemDataBound((item, site) => 
                        { 
                            item.Text = site.Title.Value; 
                            item.Value = site.Guid.ToString(); 
                            item.ImageUrl="~/Content/TreeView/FirstLook/mail.gif"
                            item.ImageHtmlAttributes.Add("alt", site.Title.Value ); 
                        }) 
                        .Children(site => site.ChildNodes)); 
                mappings.For<MyNamespace.Content>(binding => binding 
                        .ItemDataBound((item, content) => 
                        { 
                            item.Text = content.Title.Value; 
                            item.Value = content.Guid.ToString(); 
                            item.ImageUrl = "~/Content/TreeView/FirstLook/notesItems.gif"
                            item.ImageHtmlAttributes.Add("alt", content.Title.Value); 
                        }) 
                        .Children(site => site.ChildNodes)); 
                mappings.For<MyNamespace.IContentNode>(binding => binding 
                        .ItemDataBound((item, content) => 
                        { 
                            item.Text = content.Guid.ToString(); 
                            item.Value = content.Guid.ToString(); 
                            item.ImageUrl = "~/Content/TreeView/FirstLook/notesItems.gif"
                            item.ImageHtmlAttributes.Add("alt", content.Guid.ToString()); 
                        }) 
                        .Children(site => site.ChildNodes)); 
            }) 
 

Here we can see that I have a mapping for Site, Content and IContentNode, and this works perfectly. If however I remove my binding for Content, I get a "Sequence contains no elements" error. i.e. there is no handling for unknown types.

What I need is a way to declare that the mapping for IContentNode should catch all it's derived types as well. Two API constructs I can see for this would be either 1) have a "mappings.ForWithDerived<...> or similar or 2) a property of the binding itself, i.e. DerivedClasses(true).

The other thing that concerns be is those types that could be attached to an arbitrary tree, but actually have no mapping on the tree. e.g. if my ChildNodes collection was of System.Object, someone could add a String - the current TreeView would cause error - in some cases this is desirable, but shouldn't the error be something like "Unbound type 'System.String' encountered in TreeView 'ContentsTreeView'.

The issue is whether such unknown objects could be simply ignored, and if so how? I would suggest a mapping.ForUnknown item which could be either fully configured, or could carry a flag to ensure that no node is added.

I guess that your question here, is what's the point to all this? In my mind the issue is extensibility - if I write my tree to be able to display any Content node, and then subsequently create a new derived class in my model such as RSSContent, why should this break my tree, when my tree should still be able to understand this in the context of a Content class?

Get this right, and I can't imagine a binding scenario that won't work!

Is it clear now?


Incidentally, I have found a bug in the Q1 version, whereby the .data('tTreeview') data item doesn't exist within the OnLoad event callback!

Best Regards,

Mark
0
Atanas Korchev
Telerik team
answered on 10 May 2010, 02:30 PM
Hello Mark Rabjohn,

Thank you for providing additional info.

I fully understand your first suggestion and fortunately it is easily doable by modifying the provided source code. Just open NavigationItemContainerExtensions.cs and locate this method:

public static void Bind<TNavigationItem>(TNavigationItem component, object dataItem,
            NavigationBindingFactory<TNavigationItem> factory)


Then change this:
INavigationBinding<TNavigationItem> binding = factory.container.Where(b => b.Type == dataItem.GetType()).First();

with this:
INavigationBinding<TNavigationItem> binding = factory.container.Where(b => b.Type.IsAssignableFrom(dataItem.GetType())).First();

Then interface mapping should work as expected.

As for the second issue - if you apply the aforementioned fix you could create a mapping for object like this:
<%= Html.Telerik().TreeView()
        .Name("TreeView")
        .BindTo(Model, mappings =>
        {
            mappings.For<IContent>(binding => binding
                    .ItemDataBound((item, content) =>
                    {
                        item.Text = content.Guid.ToString();
                    })
                    .Children(category => category.Children));
            
            mappings.For<object>(binding => binding
                    .ItemDataBound((item, content) =>
                    {
                        item.Text = content != null ? content.ToString() : "null";
                    }));
        })
%>


The TreeView should fallback to that mapping. The TreeView will use the first mapping that will match the target data item type so you should define the mappings them from the most specific to the less specific type.

I hope this helps,
Atanas Korchev
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
0
Mark Rabjohn
Top achievements
Rank 1
answered on 10 May 2010, 02:46 PM
Hi,

Thanks, that makes perfect sense, I'll see if it works.

Thanks.

Mark
0
Mark Rabjohn
Top achievements
Rank 1
answered on 10 May 2010, 03:41 PM
That works great, but I think that if you don't want any nodes for an unknown object, a better solution seems to be to use:

mappings.For<System.Object>(binding => binding.ItemDataBound((item, content) => { }));

Cheers.

Mark
Tags
TreeView
Asked by
Mark Rabjohn
Top achievements
Rank 1
Answers by
Atanas Korchev
Telerik team
Mark Rabjohn
Top achievements
Rank 1
Share this question
or