IAbstract
IAbstract

Reputation: 19881

How to bind TreeView to ViewModel?

I have a view model as follows:

public class SolutionViewModel : TreeViewItemViewModel {

    public ObservableCollection<TreeViewItemViewModel> Children {
        get { return mChildItems; }
    }

    public bool IsExpanded {
        get { return mIsExpanded; }
        set {
            if (value != mIsExpanded) {
                mIsExpanded = value;
                OnPropertyChanged("IsExpanded");
            }

            if (mIsExpanded && mParentItem != null)
                mParentItem.IsExpanded = true;

            if (this.HasDummyChild) {
                Children.Remove(EmptyItem);
                LoadChildren();
            }
        }
    }

    public bool IsSelected {
        get { return mIsSelected; }
        set {
            if (value != mIsSelected) {
                mIsSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }
    }

    protected override void LoadChildren() {
        var subFolders = default(ReadOnlyCollection<Folder>);
        if (!GetSubFolders(mSolution.Folder.Name, out subFolders)) {
            subFolders = new ReadOnlyCollection<Folder>(new List<Folder>());
        }

        foreach (var folder in subFolders) {
            Children.Add(new SolutionItemViewModel(this, folder));
        }

    }

    public string SolutionName {
        get { return mSolution.Name; }
    }

}

The .Xaml for the TreeView is as follows:

<TreeView Name="SolutionTree" ItemsSource="{Binding SolutionViewModel}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="FontWeight" Value="Bold" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TreeView.ItemContainerStyle>

    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type localmodels:SolutionViewModel}" 
                                  ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <Image Width="16" Height="16" Margin="3,0" Source="..\Resources\Folder_25x25.png" />
                <TextBlock Text="{Binding SolutionName}" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

After a file is selected from SolutionExplorer:

    public void SetBindingContext(SolutionViewModel SolutionViewModel) {
        SolutionTree.DataContext = SolutionViewModel;
    }

This is a lazy loading model so that when the item is expanded, the children are loaded.

The problem is that I am not even getting the Solution Name as the top level node.

Update:
I verified that the model has SolutionName assigned:
enter image description here

In addition, per comment from @elgonzo, I edited the .Xaml to reflect a change to ItemsSource binding:

<TreeView Name="SolutionTree" ItemsSource="{Binding}">
    <TreeView.ItemContainerStyle>

Update 2
Code to assign the TreeView data context is executed when an event handler is raised after selecting a file from the OpenFileDialog:

private void OnOpenFile(string FilePath) {
    mSolutionManager = SolutionManager.Load(FilePath);
    mSolutionViewModel = new SolutionViewModel(mSolutionManager.Solution);
    mMainWindow.SolutionExplorer.SetBindingContext(mSolutionViewModel);
    mSolutionViewModel.Refresh();

}  

When I step into the Refresh() method:

public void Refresh() {
    OnPropertyChanged("SolutionName");
}

...I find that the PropertyChangedEventHandler has no subscribers.

Upvotes: 1

Views: 2085

Answers (1)

franssu
franssu

Reputation: 2430

Your binding to ItemsSource is wrong :

<TreeView Name="SolutionTree" ItemsSource="{Binding SolutionViewModel}">

ItemSource needs to be an IEnumerable.

Upvotes: 2

Related Questions