0

I have a custom object that consists of 2 properties. The first is a string that i wish to use as a summary or header in the tree view. The second is a list of a custom type that contains objects that are to be included under each header. The objects contain things such as name, id, area, etc.. Ill most likely default to the name property of those list objects. How can I push this into a tree view. Concatenated Model

    public class WVWellModel : Notifier
{
    private string _API;
    public string API
    {
        get
        {
            return this._API;
        }
        set
        {
            this._API = value; OnPropertyChanged("API");
        }
    }
    private string _WellName;
    public string WellName
    {
        get
        {
            return this._WellName;
        }
        set
        {
            this._WellName = value; OnPropertyChanged("WellName");
        }
    }

    private string _Division;
    public string Division
    {
        get
        {
            return this._Division;
        }
        set
        {
            this._Division = value; OnPropertyChanged("Dvision");
        }
    }

    private string _Area;
    public string Area
    {
        get
        {
            return this._Area;
        }
        set
        {
            this._Area = value; OnPropertyChanged("Area");
        }
    }

    private string _FieldOffice;
    public string FieldOffice
    {
        get
        {
            return this._FieldOffice;
        }
        set
        {
            this._FieldOffice = value; OnPropertyChanged("FieldOffice");
        }
    }...............

** Model that will be put in a list to be injected into tree view**

public class groupingModel : Notifier
{

    private string _Header;

    public string Header
    {
        get { return _Header; }
        set { _Header = value; OnPropertyChanged("Header"); }
    }


    private List<WVWellModel> _Wells;

    public List<WVWellModel> Wells
    {
        get { return _Wells; }
        set { _Wells = value; OnPropertyChanged("Wells"); }
    }

}

List of Custom Type to be injected into tree view

List treeViewList = someMethod();

In summary, I would like to bind my tree view to a custom list object.List<groupingModel> The object in those lists have two properties, a string header that is to be used to group the objects in the tree view, and a second property that contains a list of custom objects "WVWellModel".

EDIT TO XAML to Allow Selection of all items in group

I've attempted to go ahead and make the group selectable with he goal that if the group is selected all children are selected underneath. Ive successfully bound it to a property inside of the group called "IsChecked". it defaults to false and works successfully. The problem is i am unable to capture the change in value and thus cannot run any logic to select its children.

<TreeView DockPanel.Dock="Bottom" ItemsSource="{Binding Groups}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="wellModel:WellGroupModel" ItemsSource="{Binding Wells}">

            **<CheckBox Content="{Binding Header}" IsChecked="{Binding IsChecked}"/>**
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate DataType="{x:Type wellModel:WellModel}">
                    <CheckBox Content="{Binding WellName}" IsChecked="{Binding IsSelected}" />
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>

</TreeView>
1
  • 1
    I found a typo: "Dvision", you can use nameof(Division) to avoid having such bugs (also, see [CallerMemberName] version of OnPropertyChanged event riser for simpler property syntax). As for the question, can you write your question as a single sentence with ? at the end? Commented Jan 25, 2017 at 15:02

1 Answer 1

2

The TreeView control uses HierarchicalDataTemplate to control how items are displayed and how their children are populated. If your item class has children of a different type, it can specify its own child ItemTemplate, and so on recursively.

I've also added a minimal top-level viewmodel which owns a collection of GroupingModel. I'm using conventional C# naming conventions: Classes and properties start with a capital letter, private fields start with an underscore and a lower-case letter. It seems silly but when everybody uses the same convention, you always know what you're looking at.

Finally, I used ObservableCollection<T> rather than List<T>. If you bind an ObservableCollection to a control, then you can add/remove items in the collection and the control will automatically be notified and update itself without any additional work on your part.

XAML

<TreeView
    ItemsSource="{Binding Groups}"
    >
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate
            DataType="{x:Type local:GroupingModel}"
            ItemsSource="{Binding Wells}"
            >
            <TextBlock Text="{Binding Header}" />
            <HierarchicalDataTemplate.ItemTemplate>
                <!-- This can be DataTemplate if no child collection is specified -->
                <DataTemplate
                    DataType="{x:Type local:WVWellModel}"
                    >
                    <TextBlock Text="{Binding WellName}" />
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Alternatively, if you have heterogeneous collections of objects, you can create implicit templates as resources and let them be applied by type rather than by hierarchy. In your particular case, this will produce identical results, because you have a strict item hierarchy.

<TreeView
    ItemsSource="{Binding Groups}"
    >
    <TreeView.Resources>
        <HierarchicalDataTemplate
            DataType="{x:Type local:GroupingModel}"
            ItemsSource="{Binding Wells}"
            >
            <TextBlock Text="{Binding Header}" />
        </HierarchicalDataTemplate>
        <DataTemplate
            DataType="{x:Type local:WVWellModel}"
            >
            <TextBlock Text="{Binding WellName}" />
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

C#

public class ViewModel : Notifier
{
    public ViewModel()
    {
        Groups = new ObservableCollection<GroupingModel>
        {
            new GroupingModel {
                Header = "First Group",
                Wells = new List<WVWellModel> {
                    new WVWellModel() { WellName = "First Well" },
                    new WVWellModel() { WellName = "Second Well" },
                    new WVWellModel() { WellName = "Third Well" },
                }
            },
            new GroupingModel {
                Header = "Second Group",
                Wells = new List<WVWellModel> {
                    new WVWellModel() { WellName = "Third Well" },
                    new WVWellModel() { WellName = "Fourth Well" },
                    new WVWellModel() { WellName = "Fifth Well" },
                }
            }
        };
    }

    #region Groups Property
    private ObservableCollection<GroupingModel> _groups = new ObservableCollection<GroupingModel>();
    public ObservableCollection<GroupingModel> Groups
    {
        get { return _groups; }
        set
        {
            if (value != _groups)
            {
                _groups = value;
                OnPropertyChanged(nameof(Groups));
            }
        }
    }
    #endregion Groups Property
}

Update

Let's make the WVWellModel items checkable. First, we'll give them a boolean property that we'll bind to the checkbox's IsChecked property:

public class WVWellModel : Notifier
{
    private bool _isSelected;
    public bool IsSelected
    {
        get
        {
            return this._isSelected;
        }
        set
        {
            this._isSelected = value; OnPropertyChanged();
        }
    }

And then we'll change the content in the WVWellModel DataTemplate from a TextBlock to a CheckBox:

<DataTemplate
    DataType="{x:Type local:WVWellModel}"
    >
    <CheckBox 
        Content="{Binding WellName}" 
        IsChecked="{Binding IsSelected}"
        />
</DataTemplate>

You can put any valid XAML UI in a template as long as there's a single root element.

<TreeView
    Width="300"
    Height="200"
    ItemsSource="{Binding Groups}"
    Grid.IsSharedSizeScope="True"
    >
    <TreeView.Resources>
        <HierarchicalDataTemplate
            DataType="{x:Type local:GroupingModel}"
            ItemsSource="{Binding Wells}"
            >
            <TextBlock Text="{Binding Header}" />
        </HierarchicalDataTemplate>
        <DataTemplate
            DataType="{x:Type local:WVWellModel}"
            >
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" SharedSizeGroup="CheckBoxColumn" />
                    <ColumnDefinition Width="Auto" SharedSizeGroup="APIColumn" />
                </Grid.ColumnDefinitions>
                <CheckBox 
                    Grid.Column="0"
                    Content="{Binding WellName}" 
                    IsChecked="{Binding IsSelected}"
                    />
                <TextBlock 
                    Grid.Column="1"
                    Margin="12,0,0,0"
                    Text="{Binding API}" 
                    HorizontalAlignment="Right"
                    />
            </Grid>
        </DataTemplate>
    </TreeView.Resources>
</TreeView>
Sign up to request clarification or add additional context in comments.

6 Comments

This did the trick, I learned a lot form your example! If i wanted to each a check-able item, would you have a suggestion as to how o go about that?
Bravo! I'm starting to like WPF more and more everyday,
What you can do with templates is really fantastic.
What if i would like my groupings to be check boxes as well? Ive successfully bound them to a bool property on the Grouping Model as we did for the header but am unable to capture the change in value in order to mirror its selection to its children. Ive appended an update to my question to reflect my latest code.
@LCaraway If I understand you correctly, it sounds like the setter on the bool property on GroupModel should loop through this.Wells and set IsSelected = true; on each of them. The bindings will do their job.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.