WPF:教程一步在MVVM的方式逐步绑定的TreeView(WPF: Binding TreeVie

2019-07-04 19:33发布

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

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

享受阅读!

Answer 1:

介绍

正确的方法我已经找到了阅读本文章

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

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

对于我们的自动化,我们使用了一个框架,由MbUnit的的(如公堂亚军)和薄荷(除MbUnit的,这是我们合作的客户书面)。 MbUnit的为我们提供了测试夹具和测试,并MINT增加了额外的小层 - 内部测试操作。 例。 灯具被称为“FilteringFixture”。 它包括像“TestingFilteringById”或“TestingFilteringWithSpecialChars”等各试验测试量的包括动作,也就是我们的测试的原子单位。 的动作例子是 - “打开应用(参数)”,“OpenFilterDialog”等

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

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

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

DLL可与fixrtures等解析DLL被写相当快(约1-2天)。 从那以后,我已经开始与WPF,本文将告诉你它是如何结束。

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

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

如果你想解决一个存档文件,我可以了一下后给它,就在我已经做出了决定,这是值得的那个。 一个限制,我不知道我可以共享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,发现夹具,测试方法和操作使用反射(单元测试,写在我们公司的多个更小的水平)。 如果你有兴趣在它是如何做了,看看这里: 解析使用反射功能/方法内容
  2. DLL:分居类两个夹具,测试和动作(?数据模型,实体模型)创建我们将使用他们的结合。 你应该想到自己,什么将是你的树的实体模型。 主要思想 - 树的每个级别应该由相应的类暴露,这些特性,这有助于你代表的树的模型(和,理想情况下,将采取正确的地方在你的MVVM,如模型或模型的一部分)。 就我而言,我感兴趣的是实体名称,儿童和序号的列表。 序数是一个数,其表示在内部DLL的代码实体的顺序。 它帮助我告诉序号在TreeView,还不能确定这是正确的做法,但它的工程!
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; } }
}

顺便说一句,我还创建了下面的接口,它实现了所有的实体。 这样的界面可以帮助你在未来。 仍不能确定,我应该还添加有Childrens的财产List<IMintEntity>键入,或类似的东西?

public interface IMintEntity
{
    string Name { get; }
    bool IsParent { get; }
    int OrdinalNumber { get; }
}
  1. DLL -构建数据模型 :DLL具有与单元测试,并列举数据打开DLL的方法。 在枚举过程中,它建立类似下面的数据模型。 实际方法示例给出,反射芯+ Mono.Reflection.dll被使用,不与复杂性混淆。 所有你所需要的-看看该方法如何填充_fixtures与实体名单。
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:公共属性Fixtures的的List<MintFixutre>类型创建回到刚刚创建数据模型(灯具,其中包含的测试,其中包含操作的列表列表的列表)。 这将是我们为绑定源TreeView
public List<MintFixutre> Fixtures
{
    get { return _fixtures; }
}
  1. 主窗口的视图模型(用树视图内):包含从DLL可以解析单元测试的DLL对象/类。 也暴露Fixtures从的DLL公共属性List<MintFixutre>类型。 我们将结合到它从主窗口的XAML。 类似的东西(简体):
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 -设置数据模板 :在内部网格,其中包含的TreeView,我们创建<Grid.Resources>部分,包含了一套我们的模板TreeViewItem秒。 HierarchicalDataTemplate (固定装置和测试)用于那些谁个子项目, DataTemplate用于“叶”项目(措施)。 对于每个模板,我们指定的内容(文字,图像树型视图等)的ItemsSource(在此项目的情况下有孩子,如用于灯具是{Binding Path=Tests} ),和ItemTemplate中(同样,只有在情况这个项目有孩子,在这里我们设置的模板之间的联系 - FixtureTemplate使用TestTemplate其子女,TestTemplate使用ActionTemplate其子女,行动模板不使用任何东西,它是一个叶)! 重要提示:不要忘了,在以“链接”,“一个”模板“另一个”时,“另外一个”,“模板必须在XAML高于所定义的”! (只是列举我自己的失误:))

  2. XAML - TreeView的联动 :我们设置的TreeView用:从视图模型(记住公共财产?),并已经准备就绪模板,它代表的内容,外观,数据来源和树项目嵌套数据模型链接! 还有一个重要的注意事项。 不要定义您的视图模型作为内部XAML “静态”资源,像<Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources> 如果这样做,那么你将不能够通知它的属性已更改。 为什么? 静态资源是静态的资源,它初始化的,并且仍然不变之后。 我可能是错在这里,但它是我的失误之一。 因此,对于TreeView控件使用ItemsSource="{Binding Fixtures}"代替ItemsSource="{StaticResource myStaticViewModel}"

  3. 视图模型- ViewModelBase -属性更改 :几乎所有的。 停止! 当用户打开一个应用程序,那么最初的TreeView是空的,当然,因为用户没有打开任何DLL呢! 我们必须等待,直到用户打开一个DLL,然后才进行绑定。 它是通过做OnPropertyChanged事件。 为了使生活更轻松,我所有的ViewModels从ViewModelBase,其中权公开此功能,我所有的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和指挥 。 用户点击一个按钮,以打开包含单元测试数据DLL。 当我们使用MVVM ,然后通过单击命令进行处理。 在结束OpenDllExecuted处理OnPropertyChanged("Fixtures")执行,通知树的财产,它被绑定到已经改变,现在是时候对自己进行刷新。 RelayCommand辅助类可以采取例如从那里 )。 顺便说一句,因为我知道,有一些辅助库和工具包存在类似的事情发生在XAML:

  2. 和视图模型-指挥

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. 最终UI(但不是最后对我来说,很多事情应该做的!)。 扩展WPF工具的某处使用: http://wpftoolkit.codeplex.com/



文章来源: WPF: Binding TreeView in MVVM way step by step tutorial