Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF: TreeView in MVVM

I have a TreeView that I (finally) have been able to populate from a database using data binding.

There are 2 objects that live in the tree:

  • FavoriteFolder — an object that can have children: either folders or reports.
  • FavoriteReport — an object that cannot have any children: when a user clicks on this item it will run a report.

Currently I have a Model-View type setup (I think), and I would like to change it to MVVM so I can do things with the TreeView-items rather than simply display them.

I have looked at many examples, but I am still new to MVVM and WPF in general, so any guidance for my particular example would be much appreciated

My two classes that exist in the TreeView are:

Folder items:

public class FavoriteFolder
{
    private string _connectionString = new ServerInfo().ConnectionString;
    private string _folderID;
    private string _parentID;
    private string _folderTitle;

    private ObservableCollection<FavoriteFolder> _folders;
    private ObservableCollection<FavoriteReport> _reports;
    private ObservableCollection<object> _children;

    public FavoriteFolder()
    {

    }

    public ObservableCollection<object> Children
    {
        get 
        {
            _getChildren();
            return _children; 
        }
    }

    public string FolderID
    {
        get { return _folderID; }
        set { _folderID = value; }
    }

    public string ParentID
    {
        get { return _parentID; }
        set { _parentID = value; }
    }

    public string FolderTitle
    {
        get { return _folderTitle; }
        set { _folderTitle = value; }
    }

    private void _getChildren()
    {
        _folders = new ObservableCollection<FavoriteFolder>();
        _reports = new ObservableCollection<FavoriteReport>();

        using (SqlConnection cnn = new SqlConnection(_connectionString))
        {
            cnn.Open();
            string sql = "SELECT * FROM tbl_report_folders where fdr_parent_id =" + _folderID;
            SqlCommand cmd = new SqlCommand(sql, cnn);

            SqlDataReader reader = cmd.ExecuteReader();

            while (reader.Read())
            {
                FavoriteFolder folder = new FavoriteFolder();

                folder.FolderID = reader["fdr_folder_id"].ToString();
                folder.FolderTitle = reader["fdr_folder_name"].ToString();
                folder.ParentID = reader["fdr_parent_id"].ToString();

                _folders.Add(folder);
            }

            reader.Close();

            sql = "SELECT * FROM tbl_reports where rpt_folder_id =" + _folderID;
            cmd = new SqlCommand(sql, cnn);

            reader = cmd.ExecuteReader();

            while (reader.Read())
            {
                FavoriteReport report = new FavoriteReport();

                report.ReportID = reader["rpt_report_id"].ToString();
                report.ReportTitle = reader["rpt_report_name"].ToString();
                report.ParentID = reader["rpt_folder_id"].ToString();

                _reports.Add(report);
            }
        }

        //add the children to the collection
        foreach (var folder in this._folders)
            _children.Add(folder);

        foreach (var report in this._reports)
            _children.Add(report);
    }
}

Report Items:

public class FavoriteReport
{
    private string _reportID;
    private string _parentID;
    private string _reportTitle;

    public FavoriteReport()
    {

    }

    public string ReportID
    {
        get { return _reportID; }
        set { _reportID = value; }
    }

    public string ParentID
    {
        get { return _parentID; }
        set { _parentID = value; }
    }

    public string ReportTitle
    {
        get { return _reportTitle; }
        set { _reportTitle = value; }
    }
}

And the MainWindow.xaml.cs -

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ObservableCollection<object> items = new ObservableCollection<object>();

        FavoriteFolder fdr = new FavoriteFolder();

        fdr.FolderID = "0";

        items = fdr.Children;

        this.DataContext = items;
    }
}
like image 899
ChandlerPelhams Avatar asked Jun 22 '11 16:06

ChandlerPelhams


2 Answers

My first recommendation would be to drop in an MVVM toolkit, as it's easier than having to do everything yourself (e.g. implement the INotifyPropertyChanged interface). I use MVVM Light by Laurent Bungion.

On the assumption that you're using MVVM Light (you can extrapolate from this if you're using another toolkit)...

So, to convert this to MVVM you need to do a few things.

  1. Create defined Models. I generally use POCO's, simply defining the properties of a model. This means abstracting out your data access layer (more below).

  2. Create your ViewModel. This is where you'd have your properties that you're binding to in your veiw. Your ObservableCollections would sit here. Initializing an instance of the classes you created would go here. Calls to your DAL layer would go here. (instead of in the constructor of your view, for example).

    Add this ViewModel to your ViewModelLocator (I use the mvvmlocatorproperty code snippet provided in MVVM Light).

    Bind your View to the ViewModel using the Locator. In your UserControl, you'd put something like this in the declaration:

    DataContext="{Binding YourViewModel, Source={StaticResource Locator}}"
    
  3. I would follow Brennan Vincent's advice. I generally create a service interface (note: not a class, but an interface) that defines the methods my DAL (data access layer) will have. I do this to allow for Blendability, aka design time data. If you're not familiar with interfaces, perhaps simply a DAL class is a good way to start. Then I use dependency injection (DI) to inject an instance of my DAL Service. DI is pretty simple - in the ViewModelLocator you need to new up (instantiate) your DAL service class & pass it through your vm = New ViewModel(MyDALService dalService) call. You also then, obviously, need to accept a MyDALService reference in your ViewModel constructor. Here's an example of my EquipmentViewModel constructor on a project I've worked on:

    public EquipmentViewModel(Services.IEquipmentService equipmentService)
    {
        EquipmentService = equipmentService;
        LoadData();
    }
    

This ViewModel accepts a parameter of the IEquipmentService type (which is my interface). In the LoadData method, I call the EquipmentService.GetEquipment() method of my DAL that hits my database layer.

Any questions let me know. MVVM can be a pain, but I'm very happy I stuck with it. Good luck. :)

like image 154
Scott Silvi Avatar answered Nov 03 '22 23:11

Scott Silvi


Josh Smith gave the definitive description of how to use MVVM to simplify the TreeView There's not much more I can add to it.

like image 2
Michael Brown Avatar answered Nov 04 '22 01:11

Michael Brown