WPF:教程一步在MVVM的方式逐步TreeView控件绑定 [英] WPF: Binding TreeView in MVVM way step by step tutorial

查看:2546
本文介绍了WPF:教程一步在MVVM的方式逐步TreeView控件绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

查看下一个职位。这种原始的一个问题内容已被删除,因为没有任何意义。简单地说,我问如何绑​​定XML(我产生的错误在解析DLL组件)的MVVM方式使用XmlDataProvider到TreeView的。但后来我明白,这种做法是错误的,我切换到新一代数据实体模型(只写它重新present所有我想在树中暴露实体类),而不是XML。

因此​​,结果在接下来的文章中。目前,从我不时更新此物品,所以F5,以及

享受阅读!


解决方案

简介

正确的方法我发现读<一个href=\"http://www.$c$cproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode\">this文章


  

这是一个漫长的故事,你们中的大多数只是可以跳过吧:)但是,这些,谁想要了解的问题和解决方案,一定要读这一切!


我QA,和前一段时间已成为对产品负责我点击自动化。幸运的是,这个自动机发生未在一些测试工具,但是在Visual Studio中,所以它是最大接近显影

有关我们的自动化,我们使用一个框架,由MbUnit的的(如加利奥亚军)和MINT(除MbUnit的,这是我们合作的客户所撰写的)的。 MbUnit的为我们提供了测试夹具和测试,和薄荷增加了额外的一层小 - 内部测试操作。例。灯具被称为FilteringFixture。它包括像TestingFilteringById',或'TestingFilteringWithSpecialChars'等各试验测试量的包括动作,也就是我们的测试的原子单元。行动例子是 - 开放式应用程序(参数),OpenFilterDialog等

我们已经有很多的测试,其中包含了很多的行动,这是一个烂摊子。他们使用我们的QA产品的内部API。此外,我们开始研究一种新的自动化方法 - UI自动化通过微软UI自动化(对不起,同义反复)。因此,一些出口或记者的工具的必要性变得严厉的经理。

前段时间我有一个任务,开发一些应用程序,它可以分析一个DLL(包含所有夹具,测试和行动),并在人类可读的格式导出其结构(TXT,HTML,CSV,XML , 任何其他)。但是,之后,我去度假(2周)。

这种事的话,那我女朋友去了她家,直到假期(她也知道了),我很孤单留在家里。想什么,我做这一切的时间(2周),我记得关于写出口工具的任务,并有多长,我一直打算开始学习WPF。所以,我决定在假期使我的任务,并且还穿着一个应用WPF。当时我听到了一些有关MVVM,我决定使用纯MVVM来实现它。

可与fixrtures等解析的DLL

DLL被写而快速(〜1-2天)。从那以后,我已经开始与WPF,本文将告诉你它是如何结束。

我已经花了我的假期的主要部分(近8天。),试图整理出来在我的头,code,最后是做(几乎)。我的女朋友也不会相信我在做什么这一切的时候,但我有一个证明!

在伪code步骤分享我的解决方案的步骤,帮助他人避免类似的问题。这个答案是更加的样子教程=)(真的吗?)。如果你有兴趣,而从头开始学习WPF什么是最复杂的东西,我会说 - !让这一切真的MVVM和f * G树视图结合

如果你想解决一个存档文件,我可以稍后给它,就在我已经做出了决定,这是值得的了。一个限制,我不知道我可以共享MINT.dll,带来操作的,因为它已经被我们公司的客户开发的。但我可以只是将其删除,共享应用程序,它可以显示有关的灯具,只有测试的信息,但不是行动。

自夸的话。只需一点点C#/的WinForms / HTML的背景,也没有实践,我已经能够实现在近1本周版本的应用程序(写这篇文章)。所以,不可能是可能的!刚刚休假像我这样的,它花WPF学习!

一步教程步骤(W / O附加文件还)

任务的短重复

前段时间,我有我的任务开发一个应用程序,它可以分析一个DLL(包含测试夹具,测试方法和行动 - 我们的单元测试基础的自动化框架的单位),并出口结构在人类可读格式(TXT,HTML,CSV,XML,任何其他)。我决定使用WPF和纯MVVM(无论是绝对新的东西对我来说)来实现它。对我来说,2最困难的问题变得MVVM的做法本身,然后MVVM绑定到TreeView控件。我跳过关于MVVM分工的一部分,它是另外一篇文章的主题。下面的步骤是关于MVVM的方式绑定到TreeView的。


  1. 不那么重要:创建DLL可与单元测试打开DLL,发现固定装置,试验方法和操作使用反射(单元测试,写在我们的公司更小级别)。如果你有兴趣,它是怎么做了,看看这里:<一href=\"http://stackoverflow.com/questions/13679240/parsing-function-method-content-using-reflection\">Parsing使用反射功能/法内容

  2. DLL :这两个夹具,测试和动作(?数据模型,实体模型),我们将使用它们结合创建分居类。你应该想到自己,会是怎样为你的树的实体模型。主要思想 - 树的每个级别应该由相应的类暴露出来,这些特性,这有助于你重新present树模型(和,理想情况下,将采取正确的地方在你的MVVM,为模型或部分该模型)。就我而言,我感兴趣的是实体名称,儿童和序数的列表。序数是多少,从而重新presents里面DLL中的code实体的秩序。它帮助我告诉序号在TreeView,还不能确定这是正确的做法,但它的工程!

公共类MintFixutre:IMintEntity
{
    私人只读字符串_name;
    私人只读INT _ordinalNumber;
    私人只读表&LT; MintTest&GT; _tests =新的List&LT; MintTest&GT;();
    公共MintFixutre(字符串fixtureName,诠释ordinalNumber)
    {
        _name = fixtureName;
        如果(ordinalNumber&下; = 0)
            抛出新的ArgumentException(序数必须从1开始);
        _ordinalNumber = ordinalNumber;
    }
    公开名单&LT; MintTest&GT;测试
    {
        {返回_tests; }
    }
    公共字符串名称{{返回_name; }}
    公共BOOL IsParent {获得{返回true; }}
    公众诠释OrdinalNumber {{返回_ordinalNumber; }}
}公共类MintTest:IMintEntity
{
    私人只读字符串_name;
    私人只读INT _ordinalNumber;
    私人只读表&LT; MintAction&GT; _actions =新的List&LT; MintAction&GT;();
    公共MintTest(字符串测试名,INT ordinalNumber)
    {
        如果(string.IsNullOrWhiteSpace(测试名称))
            抛出新的ArgumentException(测试名称不能为空或空格填写);
        _name =测试名称;
        如果(ordinalNumber&下; = 0)
            抛出新的ArgumentException(OrdinalNumber必须从1开始);
        _ordinalNumber = ordinalNumber;
    }
    公开名单&LT; MintAction&GT;操作
    {
        {返回_actions; }
    }
    公共字符串名称{{返回_name; }}
    公共BOOL IsParent {获得{返回true; }}
    公众诠释OrdinalNumber {{返回_ordinalNumber; }}
}公共类MintAction:IMintEntity
{
    私人只读字符串_name;
    私人只读INT _ordinalNumber;
    公共MintAction(字符串actionName,诠释ordinalNumber)
    {
        _name = actionName;
        如果(ordinalNumber&下; = 0)
            抛出新的ArgumentException(序数必须从1开始);
        _ordinalNumber = ordinalNumber;    }
    公共字符串名称{{返回_name; }}
    公共BOOL IsParent {{返回FALSE; }}
    公众诠释OrdinalNumber {{返回_ordinalNumber; }}
}

顺便说一句,我还创建了下面一个接口,它实现了所有的实体。这样的接口可以帮助你在未来。仍不能确定,我应该还添加有儿童 的财产名单,LT; IMintEntity&GT; 键入,或者类似的东西?

公共接口IMintEntity
{
    字符串名称{; }
    布尔IsParent {搞定; }
    INT OrdinalNumber {搞定; }
}

<醇开始=3>

  • DLL - 构建数据模型:DLL具有与单元测试,并列举数据打开DLL的方法。在枚举过程中,它建立像下面的数据模型。真正的方法算例,反射核心+ Mono.Reflection.dll的使用,不与复杂性混淆。所有你需要 - 寻找方法如何填充与实体 _fixtures 列表

  • 私人无效ParseDllToEntityModel()
    {
        _fixutres =新的List&LT; MintFixutre&GT;();    //枚举灯具
        INT F = 1;
        的foreach(类型灯具在AssemblyTests.GetTypes()式(T =方式&gt; t.GetCustomAttributes(typeof运算(TestFixtureAttribute),假的)。长度大于0))
        {
            VAR tempFixture =新MintFixutre(fixture.Name,F);        //枚举测试方法
            INT T = 1;
            的foreach(在fixture.GetMethods变种TestMethod的()其中(M =方式&gt; m.GetCustomAttributes(typeof运算(TestAttribute),假)。长度大于0))
            {
                //过滤操作
                VAR指令= testMethod.GetInstructions()。其中​​(
                    I =&GT; i.Op code.Name.Equals(newobj)及和放大器; ((ConstructorInfo)i.Operand).DeclaringType.IsSubclassOf(typeof运算(BaseAction)))了ToList();            VAR tempTest =新MintTest(testMethod.Name,T);            //枚举操作
                对于(INT A = 1; A&LT; = instructions.Count; A ++)
                {
                    指令动作说明= [A-1];
                    字符串actionName =(action.Operand为ConstructorInfo).DeclaringType.Name;
                    VAR tempAction =新MintAction(actionName,A);
                    tempTest.Actions.Add(tempAction);
                }            tempFixture.Tests.Add(tempTest);
                Ť++;
            }        _fixutres.Add(tempFixture);
            ˚F++;
        }
    }

    <醇开始=4>

  • DLL :公共属性灯具的名单,LT的; MintFixutre&GT; 类型创建回到刚刚创建的数据模型(灯具,其中包含的测试,其中包含了操作的名单列表的列表)。这将是我们 TreeView控件绑定源。

  • 公开名单&LT; MintFixutre&GT;灯具
    {
        {返回_fixtures; }
    }


  • 主窗口的视图模型(带TreeView控件内部):包含对象/类从DLL可以解析单元测试的DLL。也暴露了灯具的DLL公共财产清单&LT; MintFixutre&GT; 键入。我们将结合从主窗口的XAML。类似的东西(简体):

  • VAR _exporter = MySuperDllReaderExporterClass();
    视图模型为TreeView的//公共财产,从4号返回财产
    公开名单&LT; MintFixture&GT;灯具{{返回_exporter.Fixtures; }}
    //初始化类出口,ParseDllToEntityModel()被调用内部的getter
    //(来自步骤#3)。冷静,我们有实体模型绑定。
    _exporter.PathToDll = @打开文件对话框可以帮助;
    //通知所有这些是如何被绑定到灯具的财产,有他们的工作,TreeView控件,是ü听吗?
    //将在本文后面要面对,预计事件
    OnPropertyChanged(灯具);

    <醇开始=6>

  • 主窗口中的 XAML - 设置数据模板:里面一格,它包含的TreeView,我们创建&LT; Grid.Resources&GT; 部分,其中包含了一套我们的树型视图的templates。 HierarchicalDataTemplate (夹具和测试)用于那些谁拥有子项,并用于叶子的项目的DataTemplate (操作)。对于每个模板,我们指定的内容(文字,图像树型视图等)的ItemsSource(在此项目的情况下有孩子,如用于灯具是 {绑定路径=测试} )和ItemTemplate中(模板之间再次,只有在情况下,这个项目有孩子,在这里我们设置联动 - FixtureTemplate使用TestTemplate其子女,TestTemplate使用ActionTemplate它的孩子,动作模板不使用任何东西,它是叶!)。重要提示:不要忘了,在以链接一模板另一个时,另外一个,模板,必须在上面的XAML定义的! (只是列举我自己的失误:))


        
            
                
                
            
        
        
            
                
                
            
        
        
            
                
                
            
        


  • XAML - TreeView的联动:用我们的设置树视图:从视图模型(?记得公共财产)的数据模型联系起来,并只用prepared模板,从而重新present内容,外观,数据来源和树项筑巢!还有一个重要的注意事项。不要定义您的视图模型作为内部XAML静态的资源,如&LT; Window.Resources&GT;&LT; MyViewModel X:键=DontUseMeForThat/></Window.Resources>。如果这样做,那么你将不能够通知它的属性已更改。为什么?静态资源是静态的资源,它初始化的人,而且依然在一成不变的。我可能是错在这里,但它是我的失误之一。因此,对于TreeView控件使用的ItemsSource ={结合灯具}而不是的ItemsSource ={StaticResource的myStaticViewModel}


  • 视图模型 - ViewModelBase - 属性更改:几乎所有的。停止!当用户打开一个应用程序,那么最初的TreeView是空的,当然,因为用户没有打开的任何DLL呢!我们必须等待,直到用户打开一个DLL,然后才进行绑定。它是通过 OnPropertyChanged 事件完成。为了使生活更轻松,我所有的ViewModels从ViewModelBase,其中权公开此功能,我所有的ViewModel继承。


  • 公共类ViewModelBase:INotifyPropertyChanged的
    {
        公共事件PropertyChangedEventHandler的PropertyChanged;    受保护的虚拟无效OnPropertyChanged(字符串propertyName的)
        {
            OnPropertyChanged(新PropertyChangedEventArgs(propertyName的));
        }    受保护的虚拟无效OnPropertyChanged(PropertyChangedEventArgs参数)
        {
            VAR处理器=的PropertyChanged;
            如果(处理!= NULL)
                处理程序(这一点,参数);
        }
    }


  • XAML - OnPropertyChanged指挥。用户点击一个按钮,打开包含单元测试数据的DLL。当我们使用 MVVM ,然后单击通过指挥处理。在 OpenDllExecuted 处理程序 OnPropertyChanged(灯具)的结尾执行,通知树,该财产,其所结合已被更改,并且现在是时候刷新本身。 RelayCommand 辅助类可以采取例如从的)。顺便说一句,因为我知道,有一些辅助库和工具包存在类似的事情发生在XAML:


  • 并视图模型 - 指挥


  • 私人ICommand的_openDllCommand;        // ...公众的ICommand OpenDllCommand
    {
        {返回_openDllCommand? (_openDllCommand =新RelayCommand(OpenDllExecuted,OpenDllCanExecute)); }
    }        // ...//决定,当在&lt; OpenDll&GT;按钮启用与否
    私人布尔OpenDllCanExecute(obj对象)
    {
        返回true; //总是打开DLL按钮真
    }        // ...//事实上,处理程序
    私人无效OpenDllExecuted(obj对象)
    {
        VAR openDlg =新的OpenFileDialog {...};
        _pathToDll = openDlg.FileName;
        _exporter.PathToDll = _pathToDll;
                    //通过绑定通报TreeView控件的属性&LT;装置及GT;已经变了,
                    //从而迫使树刷新自己
        OnPropertyChanged(灯具);
    }


  • 最终UI (但不是最终对我来说,很多事情应该做的!)。扩展WPF工具某处使用: HTTP://wpftoolkit.$c$cplex.com/

  • See the next post. This original one question content has been removed, as doesn't have any sense. Briefly, I asked how to bind XML (which I generated by mistake while parsing DLL assembly) to TreeView using XmlDataProvider in MVVM way. But later I understood that this approach was wrong, and I switched to generation of data entity model (just write classes which represent all the entities I would like to expose in the tree) instead of XML.

    So, the result in the next post. Currently from time to time I update this "article", so F5, and

    Enjoy reading!

    解决方案

    Introduction

    The right way I had found reading this article

    It's a long story, most of you just can skip it :) But those, who want to understand the problem and solution, must read this all !

    I'm QA, and some time ago had become responsible for Automation of the product I clicks. Fortunately, this automaton takes place not in some Testing Tool, but in Visual Studio, so it is maximally close to development.

    For our automation we use a framework which consist of MbUnit (Gallio as runner) and of MINT (addition to MbUnit, which is written by the customer we work with). MbUnit gives us Test Fixtures and Tests, and MINT adds additional smaller layer -- Actions inside tests. Example. Fixture is called 'FilteringFixture'. It consist of amount of tests like 'TestingFilteringById', or 'TestingFilteringWithSpecialChars', etc. Each test consist of actions, which are atomic unit of our test. Example of actions are - 'Open app (parameter)', 'OpenFilterDialog', etc.

    We already have a lot of tests, which contain a lot of actions, it's a mess. They use internal API of the product we QA. Also, we start investigation a new Automation approach - UI automation via Microsoft UI Automation (sorry for tautology). So the necessity of some "exporter", or "reporter" tool became severe for managers.

    Some time ago I have got a task to develop some application, which can parse a DLL (which contains all the fixtures, tests and actions), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). But, right after that, I went to vacation (2 weeks).

    It happens so, that my girlfriend went to her family until vacation (she also got it), and I remained at home so alone. Thinking what me to do all this time (2 weeks), I remember about that "write exporter tool task" and how long I have been planning to start learning WPF. So, I decided to make my task during vacation, and also dress a application to WPF. At that time I heard something about MVVM, and I decided to implement it using pure MVVM.

    DLL which can parse DLL with fixrtures etc had been written rather fast (~1-2 days). After that I had started with WPF, and this article will show you how it ended.

    I have spent a major part of my vacation (almost 8 days!), trying to sorted it out in my head and code, and finally, it is done (almost). My girlfriend would not believe what I was doing all this time, but I have a proof!

    Sharing my solution step by step in pseudo code, to help others avoid similar problems. This answer is more looks like tutorial =) (Really?). If you are interested what were the most complicated things while learning WPF from scratch, I would say -- make it all really MVVM and f*g TreeView binding!

    If you want an archived file with solution, I can give it a bit later, just when I have made a decision, that it is worth of that. One limitation, I'm not sure I may share the MINT.dll, which brings Actions, as it has been developed by the customer of our company. But I can just remove it, and share the application, which can display information about Fixtures and Tests only, but not about actions.

    Boastful words. With just a little C# / WinForms / HTML background and no practice I have been able to implement this version of the application in almost 1 week (and write this article). So, impossible is possible! Just take a vacation like me, and spend it to WPF learning!

    Step by step tutorial (w/o attached files yet)

    Short repetition of the task:

    Some time ago I have got a task to develop an application, which can parse a DLL (which contains test fixtures, test methods and actions - units of our unit testing based automation framework), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). I decided to implement it using WPF and pure MVVM (both were absolutely new things for me). The 2 most difficult problems for me became MVVM approach itself, and then MVVM binding to TreeView control. I skip the part about MVVM division, it's a theme for separate article. The steps below are about binding to TreeView in MVVM way.

    1. Not so important: Create DLL which can open DLL with unit tests and finds fixtures, test methods and actions (more smaller level of unit test, written in our company) using reflection. If you are interested in how it had been done, look here: Parsing function / method content using Reflection
    2. DLL: Separated classes are created for both fixtures, tests and actions (data model, entity model?).We'll use them for binding. You should think by yourself, what will be an entity model for your tree. Main idea - each level of tree should be exposed by appropriate class, with those properties, which help you to represent the model in the tree (and, ideally, will take right place in your MVVM, as model or part of the model). In my case, I was interested in entity name, list of children and ordinal number. Ordinal number is a number, which represents order of an entity in the code inside DLL. It helps me show ordinal number in the TreeView, still not sure it's right approach, but it works!

    public class MintFixutre : IMintEntity
    {
        private readonly string _name;
        private readonly int _ordinalNumber;
        private readonly List<MintTest> _tests = new List<MintTest>();
        public MintFixutre(string fixtureName, int ordinalNumber)
        {
            _name = fixtureName;
            if (ordinalNumber <= 0)
                throw new ArgumentException("Ordinal number must begin from 1");
            _ordinalNumber = ordinalNumber;
        }
        public List<MintTest> Tests
        {
            get { return _tests; }
        }
        public string Name { get { return _name; }}
        public bool IsParent { get { return true; }  }
        public int OrdinalNumber { get { return _ordinalNumber; } }
    }
    
    public class MintTest : IMintEntity
    {
        private readonly string _name;
        private readonly int _ordinalNumber;
        private readonly List<MintAction> _actions = new List<MintAction>();
        public MintTest(string testName, int ordinalNumber)
        {
            if (string.IsNullOrWhiteSpace(testName))
                throw new ArgumentException("Test name cannot be null or space filled");
            _name = testName;
            if (ordinalNumber <= 0)
                throw new ArgumentException("OrdinalNumber must begin from 1");
            _ordinalNumber = ordinalNumber;
        }
        public List<MintAction> Actions
        {
            get { return _actions; }
        }
        public string Name { get { return _name; } }
        public bool IsParent { get { return true; } }
        public int OrdinalNumber { get { return _ordinalNumber; } }
    }
    
    public class MintAction : IMintEntity
    {
        private readonly string _name;
        private readonly int _ordinalNumber;
        public MintAction(string actionName, int ordinalNumber)
        {
            _name = actionName;
            if (ordinalNumber <= 0)
                throw new ArgumentException("Ordinal numbers must begins from 1");
            _ordinalNumber = ordinalNumber;
    
        }
        public string Name { get { return _name; } }
        public bool IsParent { get { return false; } }
        public int OrdinalNumber { get { return _ordinalNumber; } }
    }
    

    BTW, I also created an interface below, which implement all the entities. Such interface can help you in the future. Still not sure, should I was also add there Childrens property of List<IMintEntity> type, or something like that?

    public interface IMintEntity
    {
        string Name { get; }
        bool IsParent { get; }
        int OrdinalNumber { get; }
    }
    

    1. DLL - building data model: DLL has a method which opens DLL with unit tests and enumerating data. During enumeration, it builds a data model like below. Real method example is given, reflection core + Mono.Reflection.dll are used, don't be confused with complexity. All that you need - look how the method fills _fixtures list with entities.

    private void ParseDllToEntityModel()
    {
        _fixutres = new List<MintFixutre>();
    
        // enumerating Fixtures
        int f = 1;
        foreach (Type fixture in AssemblyTests.GetTypes().Where(t => t.GetCustomAttributes(typeof(TestFixtureAttribute), false).Length > 0))
        {
            var tempFixture = new MintFixutre(fixture.Name, f);
    
            // enumerating Test Methods
            int t = 1;
            foreach (var testMethod in fixture.GetMethods().Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Length > 0))
            {
                // filtering Actions
                var instructions = testMethod.GetInstructions().Where(
                    i => i.OpCode.Name.Equals("newobj") && ((ConstructorInfo)i.Operand).DeclaringType.IsSubclassOf(typeof(BaseAction))).ToList();
    
                var tempTest = new MintTest(testMethod.Name, t);
    
                // enumerating Actions
                for ( int a = 1; a <= instructions.Count; a++ )
                {
                    Instruction action = instructions[a-1];
                    string actionName = (action.Operand as ConstructorInfo).DeclaringType.Name;
                    var tempAction = new MintAction(actionName, a);
                    tempTest.Actions.Add(tempAction);
                }
    
                tempFixture.Tests.Add(tempTest);
                t++;
            }
    
            _fixutres.Add(tempFixture);
            f++;
        }
    }
    

    1. DLL: Public property Fixtures of the List<MintFixutre> type is created to return just created data model ( List of Fixtures, which contain lists of tests, which contains lists of Actions ). This will be our binding source for TreeView.

    public List<MintFixutre> Fixtures
    {
        get { return _fixtures; }
    }
    

    1. ViewModel of MainWindow (with TreeView inside): Contains object / class from DLL which can parse unit tests DLLs. Also exposes Fixtures public property from the DLL of List<MintFixutre> type. We will bind to it from XAML of MainWindow. Something like that (simplified):

    var _exporter = MySuperDllReaderExporterClass ();
    // public property of ViewModel for TreeView, which returns property from #4
    public List<MintFixture> Fixtures { get { return _exporter.Fixtures; }}
    // Initializing exporter class, ParseDllToEntityModel() is called inside getter 
    // (from step #3). Cool, we have entity model for binding.
    _exporter.PathToDll = @"open file dialog can help";
    // Notifying all those how are bound to the Fixtures property, there are work for them, TreeView, are u listening?
    // will be faced later in this article, anticipating events
    OnPropertyChanged("Fixtures");
    

    1. XAML of MainWindow - Setup data templates: Inside a Grid, which contains TreeView, we create <Grid.Resources> section, which contains a set of templates for our TreeViewItems. HierarchicalDataTemplate (Fixtures and Tests) is used for those who have child items, and DataTemplate is used for "leaf" items (Actions). For each template, we specify which its Content (text, TreeViewItem image, etc.), ItemsSource (in case of this item has children, e.g. for Fixtures it is {Binding Path=Tests}), and ItemTemplate (again, only in case this item has children, here we set linkage between templates - FixtureTemplate uses TestTemplate for its children, TestTemplate uses ActionTemplate for its children, Action template does not use anything, it is a leaf!). IMPORTANT: Don't forget, that in order to "link" "one" template to "another", the "another" template must be defined in XAML above the "one"! (just enumerating my own blunders :) )

    2. XAML - TreeView linkage: We setup TreeView with: linking with data model from ViewModel (remember public property?) and with just prepared templates, which represent content, appearance, data sources and nesting of tree items! One more important note. Don't define your ViewModel as "static" resource inside XAML, like <Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources>. If you do so, then you won't be able to notify it on property changed. Why? Static resource is static resource, it initializes ones, and after that remains immutable. I might be wrong here, but it was one of my blunders. So for TreeView use ItemsSource="{Binding Fixtures}" instead of ItemsSource="{StaticResource myStaticViewModel}"

    3. ViewModel - ViewModelBase - Property Changed: Almost all. Stop! When user opens an application, then initially TreeView is empty of course, as user hasn't opened any DLL yet! We must wait until user opens a DLL, and only then perform binding. It is done via OnPropertyChanged event. To make life easier, all my ViewModels are inherited from ViewModelBase, which right exposes this functionality to all my ViewModel.

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, args);
        }        
    }
    

    1. XAML - OnPropertyChanged and commanding. User clicks a button to opens DLL which contains unit tests data. As we using MVVM, then click is handled via commanding. At the end of the OpenDllExecuted handler OnPropertyChanged("Fixtures") is executed, notifying the Tree, that the property, to which it is bind to has been changed, and that now is time to refresh itself. RelayCommand helper class can be taken for example from there). BTW, as I know, there are some helper libraries and toolkits exist Something like that happens in the XAML:

    2. And ViewModel - Commanding

    private ICommand _openDllCommand;
    
            //...
    
    public ICommand OpenDllCommand
    {
        get { return _openDllCommand ?? (_openDllCommand = new RelayCommand(OpenDllExecuted, OpenDllCanExecute)); }
    }
    
            //...
    
    // decides, when the <OpenDll> button is enabled or not
    private bool OpenDllCanExecute(object obj)
    {
        return true; // always true for Open DLL button
    }
    
            //...
    
    // in fact, handler
    private void OpenDllExecuted(object obj)
    {
        var openDlg = new OpenFileDialog { ... };
        _pathToDll = openDlg.FileName;
        _exporter.PathToDll = _pathToDll;
                    // Notifying TreeView via binding that the property <Fixtures> has been changed,
                    // thereby forcing the tree to refresh itself
        OnPropertyChanged("Fixtures");
    }
    

    1. Final UI (but not final for me, a lot of things should be done!). Extended WPF toolkit was used somewhere: http://wpftoolkit.codeplex.com/

    这篇关于WPF:教程一步在MVVM的方式逐步TreeView控件绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆