This project is read-only.

NullReferenceException while executing TreeViewFor

Sep 1, 2012 at 11:12 PM

Hi,

I'm trying to show product categories and subcategories in a treeview in a MVC4 application. The user isn't supposed to edit anything in the treeview.

Here's the syntax:

<div>
    @Html.TreeViewFor(m => m.Categories,
        i => i == 0 ? "Subcategories" : null,
        ExternalContainerType.span,
        null,
        new object[]
        {
            _S.L<MyProject.Web.External.Models.ProductCategoryViewModel>(
            h => "<span>" + h.DisplayFor(m => m.Name).ToString() + "</span>")
        },
                (x, y) => 0, null, null, null, TreeViewMode.InitializeDisplay,
                        (x) => "allnodes",
        (x, y) => TreeViewItemStatus.initializeShow, null);
</div>

It results in a NullReferenceException. Can you see anything odd in the code above? How do I debug this? The object that contains the categories is filled ok with categories, the categories also contain the correct subcategories.

The stack trace:

[NullReferenceException: Object reference not set to an instance of an object.]
   MVCControlsToolkit.Controls.TreeViewHelpers.TreeViewRec(StringBuilder sb, StringBuilder sbInit, Boolean editMode, HtmlHelper`1 htmlHelper, RenderInfo`1 renderInfo, Func`2 collectionName, ExternalContainerType itemContainer, String rootClass, Object[] itemTemplates, Func`3 itemTemplateSelector, Func`2 itemClassSelector, Func`3 itemStatus, Single opacity, Boolean canMove, Int32 level, Int32 totalCount, String fatherName, String root_id) +3967

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +0
   System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +72
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) +251
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +28
   System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19
   MVCControlsToolkit.Controls.TreeViewHelpers.TreeViewRec(StringBuilder sb, StringBuilder sbInit, Boolean editMode, HtmlHelper`1 htmlHelper, RenderInfo`1 renderInfo, Func`2 collectionName, ExternalContainerType itemContainer, String rootClass, Object[] itemTemplates, Func`3 itemTemplateSelector, Func`2 itemClassSelector, Func`3 itemStatus, Single opacity, Boolean canMove, Int32 level, Int32 totalCount, String fatherName, String root_id) +5792
   MVCControlsToolkit.Controls.TreeViewHelpers.TreeViewTop(StringBuilder sb, Boolean editMode, HtmlHelper`1 htmlHelper, RenderInfo`1 renderInfo, Func`2 collectionName, ExternalContainerType itemContainer, String rootClass, Object[] itemTemplates, Func`3 itemTemplateSelector, Func`2 itemClassSelector, Func`3 itemStatus, Single opacity, Boolean canMove, Boolean canAdd, TreeViewOptions treeViewOptions) +844
   MVCControlsToolkit.Controls.TreeViewHelpers.TreeViewFor(HtmlHelper`1 htmlHelper, RenderInfo`1 renderInfo, Func`2 collectionName, ExternalContainerType itemContainer, String rootClassDisplay, Object[] displayTemplates, Func`3 itemTemplateSelectorDisplay, String rootClassEdit, Object[] editTemplates, Func`3 itemTemplateSelectorEdit, TreeViewMode mode, Func`2 itemClassSelector, Func`3 itemStatus, TreeViewOptions treeViewOptions) +1053
   MVCControlsToolkit.Controls.TreeViewHelpers.TreeViewFor(HtmlHelper`1 htmlHelper, Expression`1 expression, Func`2 collection, ExternalContainerType itemContainer, String rootClassDisplay, Object[] displayTemplates, Func`3 itemTemplateSelectorDisplay, String rootClassEdit, Object[] editTemplates, Func`3 itemTemplateSelectorEdit, TreeViewMode mode, Func`2 itemClassSelector, Func`3 itemStatus, TreeViewOptions treeViewOptions) +354
   ASP._Page_Views_Home_Index_cshtml.Execute() in d:\Workspace\AdventureWorks\Source\AdventureWorks.Web.External\AdventureWorks.Web.External\Views\Home\Index.cshtml:6
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +207
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +97
   System.Web.WebPages.StartPage.RunPage() +19
   System.Web.WebPages.StartPage.ExecutePageHierarchy() +65
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +76
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +258
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +115
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +303
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13
   System.Web.Mvc.<>c__DisplayClass1a.b__17() +23
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +260
   System.Web.Mvc.<>c__DisplayClass1c.b__19() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +177
   System.Web.Mvc.Async.<>c__DisplayClass2a.b__20() +92
   System.Web.Mvc.Async.<>c__DisplayClass25.b__22(IAsyncResult asyncResult) +126
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +45
   System.Web.Mvc.<>c__DisplayClass1d.b__18(IAsyncResult asyncResult) +14
   System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +25
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +61
   System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +25
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +49
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.<>c__DisplayClassb.b__4(IAsyncResult asyncResult) +28
   System.Web.Mvc.Async.<>c__DisplayClass4.b__3(IAsyncResult ar) +25
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.b__d() +50
   System.Web.Mvc.SecurityUtil.b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8970061
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184

 

Regards,

Nils

Coordinator
Sep 2, 2012 at 5:31 PM

pls show the declaration of Categories and Subcategory. Both are supposed to be Ienumerables and both are supposed to be read/write public properties.

Does all nodes are of type 

ProductCategoryViewModel

Because your template assumes this(_S.L<MyProject.Web.External.Models.ProductCategoryViewModel>)

does your node implement the interface ISafeCreation?(need just tu add ISafeCreation...no member need to br implemented)

Moreover, the 2.2 release has the limitation that cjildren collection like Subcategories must be List<T>. This limitation has been removed in the upcoming release (sources with this fix are already available here)

For the remainder I dont see nothing wrong...

Sep 2, 2012 at 6:49 PM

Hi Frank,

In the mean time, I made a separate class for the subcategory, but I´m still having the same problem. I also added the ISafeCreation interface to the classes, it doesn´t seem to make a difference.

Here are the class definitions:

public class ProductCategoryDto
{
    public ProductCategoryDto()
    {
        Categories = new List<ProductCategoryViewModel>();
    }

    public List<ProductCategoryViewModel> Categories { get; set; }
}

public class ProductCategoryViewModel : ISafeCreation
{
    public string Name { get; set; }
    public List<ProductSubcategoryViewModel> Subcategories { get; set; }
}

public class ProductSubcategoryViewModel : ISafeCreation
{
    public string Name { get; set; }
}

The code in the view:

@Html.TreeViewFor(m => m.Categories,
        i => i == 0 ? "Subcategories" : null,
        ExternalContainerType.span,
        null,
        new object[]
        {
            _S.L<MyProject.Web.External.Models.ProductCategoryViewModel>(
            h => "<span>" + h.DisplayFor(m => m.Name).ToString() + "</span>"),
            _S.L<MyProject.Web.External.Models.ProductSubcategoryViewModel>(
            h => "<span>" + h.DisplayFor(m => m.Name).ToString() + "</span>")
            
        },
                (x, y) => 0, null, null, null, TreeViewMode.InitializeDisplay,
                        (x) => "allnodes",
        (x, y) => TreeViewItemStatus.initializeShow, null);

 

  • Do you have a small sample application using the TreeView that I can try?
  • I downloaded the latest source code, but I can't find a MVC4ControlsToolkit project in the solution, only the MVC2ControlsToolkit and MVC3ControlsToolkit projects.

 

Regards,

Nils

Coordinator
Sep 2, 2012 at 8:40 PM

Now I think the culprit is the (x, y) => 0. Thais is the function that selects the template to be used...and the proble is that it always selects the template 0 that is for ProductSubCategory.

Try substituting with: (x, y) => x is MyProject.Web.External.Models.ProductCategoryViewModel ? 0 : 1

This way you check the type of the node and assign template 0 or 1 according to the type of node. This should fix, since everything else is compatible with your data structures. 

About Mv4 samples: at moment there is only the Mvc4 RC compatible version, that unlikely is not compatible with RTM. However the Mvc3 dll should be able to work also with Mvc4 if you don use ApiController stuffs. An Mvc 4 Compatinle Release will be available in a few days (less than a week). There was a delay in releasing the Mvc 4 version because it depends on the Queryable attribute that has been removed from Mvc4 RTM and inserted in a nuget package with seral bug problems Thus we were forced to wait Microsoft fixes of the Nuget package. Now most of the nuget package bugs have been removed so we can release the Mvc 4 compatible Mvc Controls Toolkit.

 

 

Sep 2, 2012 at 10:34 PM

Thank you, that helped. The previous error disappeared, but now I'm getting an 'Object expected' error in a Javascript function:

http://img688.imageshack.us/img688/390/mvctreeviewobjectexpect.png

I checked the HTML of the page, the element 'Categories___flattened_ItemsContainer' does exist on the page, but when I run the statement $('#Categories___flattened_ItemsContainer') in the QuickWatch window in Visual Studio, it also returns 'Object expected'.

Any clues on this one?

Coordinator
Sep 3, 2012 at 1:59 AM

probably you forgot to include: jquery.treeview.all-x.x.x.min.js .

For all js and css files needed please refer here: 

http://mvccontrolstoolkit.codeplex.com/wikipage?title=TreeView

and here:

http://mvccontrolstoolkit.codeplex.com/wikipage?title=Which%20Javascript%20file%20to%20include

Moreover dont forget the right setting for validation: http://mvccontrolstoolkit.codeplex.com/wikipage?title=Validation%20Setting

and Globalization: http://mvccontrolstoolkit.codeplex.com/wikipage?title=Globalization


Sep 3, 2012 at 6:26 PM

I already had the js files included into the application. The reason for the 'Object expected' might be that my view is a partial view. Do you concur?

After reading the article http://mvccontrolstoolkit.codeplex.com/wikipage?title=Use%20of%20Templates, I declared a helper template. Can you tell me how to use the helper template in the TreeViewFor method call? Is this a viable solution when using a partial view?

@helper ProductCategoryTemplate(HtmlHelper<ProductCategoryViewModel> Html)
{
    @Html.TypedTextBoxFor(m => m.Name)
}

Coordinator
Sep 4, 2012 at 7:14 AM

In order to define ho each node is displayed you can either use a partial view, a lambda expression an in line razor helper or a razor helper like the one in your example. The article you read explain how to use both of them as parameters to various controls. In the case of the treeview the argument is an array of templates.

In your case instead of passing the _S.L(.....) stuff you pass hust the nale of the Razor Helper: ProductCategoryTemplate.

You can use also a Razor in  line template by using a _S.H(....) stuff as shown in the article you referred to. Anyway templates are used in several tutorials (that all have associated code to download). The one to start are:

Mvc Controls Toolkit DataGrid Updated Tutorial

 Editing a List of Heterogeneous Types with the Mvc Controls 

About the javascript problem: I dont understand what you mean with using a partial view, but the important is that the Html page where the tree is rendered contains the needed javascript. Verify this in your output html pages in the browse by hitting "show sources"...or similar depending by the browser...for more help pls shome both the partial view and the View it is in...pinting out where js files are located.

Sep 4, 2012 at 9:17 PM

You were right about the scripts. Even though I added them to project, they weren't rendered on the page. Now that's fixed.

I have another little question about the treeview. I'd like to make hyperlinks for the subcategories. Normally I would use Html.ActionLink, but it looks like I have to do it some other way in a treeview displaytemplate. So instead of the DisplayFor method, I need another method but I'm not sure which one to use. When a user clicks a subcategory, I need to show the products belonging to that subcategory.

Coordinator
Sep 5, 2012 at 4:20 AM

Html.ActionLink should work, if you pass the id of the subcategory as route value in ActionLink...and organize in a coherent way controllers and routing rules

Sep 5, 2012 at 4:54 PM

I can't figure out how to use ActionLink in a treeview displaytemplate. Is there an overload of ActionLink that I can use?

@Html.TreeViewFor(m => m.Categories,
        i => i == 0 ? "Subcategories" : null,
        ExternalContainerType.span,
        null,
        new object[]
        {
            _S.L(
            h => h.ActionLink(???),
            _S.L(
            h => h.ActionLink(???),
        },
        (x, y) => x is AdventureWorks.Web.External.Models.ProductCategoryViewModel ? 0 : 1,
        null, null, null, TreeViewMode.Display,
        null,
        (x, y) => TreeViewItemStatus.initializeShow, null);

Coordinator
Sep 5, 2012 at 9:52 PM

You can use it as you use normally!:

Html.ActionLink("link Text", "ActionName", {id=h.ViewData.Model.id})

 

Where I supposed that you have a route parameter callsed id that expect the id of the item to be shown. Moreover I suppuse that the principal key of each item is called id.

Pls notice that I call the ActionLink with the Html helper of the whole page not h because it has all necessary infos to buid the link from the routing rules.  In order to get the id of the current item, instead I suse  h that contains the current item as its model

Sep 17, 2012 at 9:08 PM
Edited Sep 17, 2012 at 9:09 PM

I tried this: 

 

 _S.L<MyProject.Web.External.Models.ProductCategoryViewModel>(h => Html.ActionLink(h.ViewData.Model.Name, "ViewProducts", h.ViewData.Model.Name))

 

but this results in an error. Cannot implicitly convert type 'System.Web.Mvc.MvcHtmlString' to 'string'
because Html.ActionLink returns a MvcHtmlString where I actually need to return a string. I noticed that there's no ActionLinkFor-like method that returns a string as the DisplayFor method does.

Any pointers how to use Html.ActionLink in a treeview template?

Coordinator
Sep 18, 2012 at 12:01 AM

Html.ActionLink return an MvcHtmlString while you need a string:

Html.ActionLink(h.ViewData.Model.Name, "ViewProducts", h.ViewData.Model.Name).ToString()

 

Should work.

The point is that you are using _S.L templates whose h => ...function must return a string you may also use a 

_S.H<.....>(

@<text>

Html.ActionLink(item.ViewData.Model.Name, "ViewProducts", item.ViewData.Model.Name)

</text>

)

That is whitin the _S.H(.....you can place any on line Razor helper, this means you can write code like in a normal View, the only difference being that the Html helper of the item is called implicitely item. while with the _S.L template you write h => ....and the html helper of the item is named h(name chosen by you). 

be sure that the item vieewmodel id not null otherwise you code breaks ...in any case.