0
votes

OK, once again, my Google-Fu isn't up to par and I would really appreciate a little guidance here.

I have a WPF app with multiple pages, one of which contains a TabControl, of which one of the tabs contains a grid, in which one of the columns contains a StackPanel with just two items on it: a Label and a TreeView. I have no need to update the TreeView once the content is obtained. The app uses MVVMLight (great toolkit!) to expose the data and said data is exposed in an mvvm (observable) property as it should be. I have checked and the data itself is available in correct form once I get to the point of setting the ItemsSource property so I know it's not the lack of data or the structure of the data itself. I have looked at all the entries on the web (at least the first 4 pages worth) matching the search terms "wpf treeview dictionary" and all articles come close, but don't get me to where I need to be. I'm missing something and what's worse, IntelliSense is even "helping" by providing the correct values for the xaml settings. So I know I'm close, but after two days of frustration, I'm throwing in the towel and asking for an assist.

So...to the meat of the problem: The data that the TreeView needs to display is in the form of SortedDictionary<string, List<ServerEntityNameMsSqlSvr>>. A ServerEntityNameMsSqlSvr class contains multiple properties, one of which is FullName. What I want the TreeView to display is the dictionary Key as the parent node and the FullName from each of the items in the List<ServerEntityNameMsSqlSvr>. You'd think that'd be simple, no? Not for me, the closest I can get is to display the Key of the dictionary, but either nothing for the children (best case) or throw an exception that stops the app with a null exception (worst case).

Here is the xaml I'm using (worst case):

<TreeView Grid.Column="0" ItemsSource="{Binding TableHierarchy}">
    <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Path=Value}">
                <TextBlock Text="{Binding Path=Key}"/>
                <HierarchicalDataTemplate.ItemTemplate>
                    <DataTemplate DataType="awe:ServerEntityNameMsSqlSvr">
                        <TextBlock Text="{Binding FullName}"/>
                    </DataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>

"TableHierarchy" is the MVVM property that exposes the data, it is declared as SortedDictionary<string, List<ServerEntityNameMsSqlSvr>>. The DataType "awe:ServerEntityNameMsSqlSvr" is a simple class with a few methods and properties, nothing special. One layer of inheritance, no interfaces. Of the properties that are available, I want to expose just the FullName, which is declared as public string FullName => _FullName(); Yep, it calls an overloaded method to build the full name but the result is a simple string (and the method call happens when the data is built, not a display time, iow, the values are already there, at least debugging to the setter shows that the content is correct.

Some of the solutions that I have researched suggest that the data type be changed to something other than a dictionary. In this case, that's not possible and given that the lists are, on occasion, quite large, I don't want to rebuild it into something else. This needs to work with the sorted dictionary as declared.

1
"the method call happens when the data is built" -- _FullName() is called when the property getter is called. I can't say what's in the method, of course. If I were a betting man I'd suspect there might be a null reference in it. Anyway: Your XAML works perfectly for me with a minimal test viewmodel. Can you share enough code to reproduce the problem? Thanks. - 15ee8f99-57ff-4f92-890c-b56153
Oh I get what you were thinking. This would call _FullName() on construction: public string FullName { get; } = _FullName();. That's an initializer for a readonly automatic property. What you have instead is an "expression-bodied property": The expression to the right of => is used for a property getter. It is called whenever anybody gets the value of the property. - 15ee8f99-57ff-4f92-890c-b56153
Yeah, now you see what I'm up to - I'm using an abstract ancestral class to define the functionality of the class, but leaving the details of implementation up to the descendants (as is proper in standard OOP programming). I am, as of this writing, going back through the hierarchy to see if I did indeed miss something. I don't think I did, but, you may be right. Just 'cause the unit tests pass, doesn't mean that the Real World can't throw something I didn't expect. I'll keep this thread posted to let everyone know what I find out. - Fred
I'd put an old-style getter on that property with a try/catch in it, and set a breakpoint in the catch. - 15ee8f99-57ff-4f92-890c-b56153
Went one step further - rebuilt the entire gadget that fills those properties and added a method call that fills said property with a string. Still fails even after hard-coding the property with a test string that has nothing to do with any method calls. I guess I'll go one step further and just build a test wpf program with all the gunk taken out and see where that leads me. - Fred

1 Answers

1
votes

The xaml shown above is indeed correct, however, the gadget that supports the data (the methods in the ServerEntityNameMsSqlServer class) all need to not throw exceptions under any circumstances. In this case, one of the methods not directly involved with the author's code but used somewhere else in the framework (an overloaded call to "Equals" that was constructed to check individual property equality to determine the result) was throwing a null exception because a property wasn't filled in for the particular use case.

Difficult to find, but that was the cause.