请参阅下一个职位。 这种原始的一个问题内容已被删除,因为没有任何意义。 简单地说,我问如何绑定XML(我产生错误而解析DLL组件)在MVVM方式使用XmlDataProvider到TreeView控件。 但后来我明白,这种做法是错误的,我切换到下一代数据实体模型(只写它代表了所有我想树,以实体类),而不是XML。
因此,结果在接下来的文章中。 目前,从我不时更新此“物品”,所以F5和
享受阅读!
请参阅下一个职位。 这种原始的一个问题内容已被删除,因为没有任何意义。 简单地说,我问如何绑定XML(我产生错误而解析DLL组件)在MVVM方式使用XmlDataProvider到TreeView控件。 但后来我明白,这种做法是错误的,我切换到下一代数据实体模型(只写它代表了所有我想树,以实体类),而不是XML。
因此,结果在接下来的文章中。 目前,从我不时更新此“物品”,所以F5和
享受阅读!
正确的方法我已经找到了阅读本文章
这是一个很长的故事,你们中的大多数只是可以忽略它:)但是,这些,谁想要了解的问题和解决方案,一定要读这一切!
我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学习!
任务的短重复 :
前一段时间我有一个任务来开发应用程序,它可以分析一个DLL(包含测试夹具,测试方法和行动 - 我们的单元测试基础的自动化框架的单位),并导出其结构在人类可读的格式(TXT ,HTML,CSV,XML,任何其他)。 我决定使用WPF和纯MVVM(两者都是绝对新的东西对我来说)来实现它。 对我来说,2个最困难的问题变得MVVM的做法本身,然后MVVM绑定到TreeView控件。 我跳过约MVVM师的一部分,它是单独一篇文章的主题。 下列步骤是有关MVVM方式结合的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; }
}
_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++;
}
}
Fixtures
的的List<MintFixutre>
类型创建回到刚刚创建数据模型(灯具,其中包含的测试,其中包含操作的列表列表的列表)。 这将是我们为绑定源TreeView
。 public List<MintFixutre> Fixtures
{
get { return _fixtures; }
}
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");
主窗口的XAML -设置数据模板 :在内部网格,其中包含的TreeView,我们创建<Grid.Resources>
部分,包含了一套我们的模板TreeViewItem
秒。 HierarchicalDataTemplate
(固定装置和测试)用于那些谁个子项目, DataTemplate
用于“叶”项目(措施)。 对于每个模板,我们指定的内容(文字,图像树型视图等)的ItemsSource(在此项目的情况下有孩子,如用于灯具是{Binding Path=Tests}
),和ItemTemplate中(同样,只有在情况这个项目有孩子,在这里我们设置的模板之间的联系 - FixtureTemplate使用TestTemplate其子女,TestTemplate使用ActionTemplate其子女,行动模板不使用任何东西,它是一个叶)! 重要提示:不要忘了,在以“链接”,“一个”模板“另一个”时,“另外一个”,“模板必须在XAML高于所定义的”! (只是列举我自己的失误:))
XAML - TreeView的联动 :我们设置的TreeView用:从视图模型(记住公共财产?),并已经准备就绪模板,它代表的内容,外观,数据来源和树项目嵌套数据模型链接! 还有一个重要的注意事项。 不要定义您的视图模型作为内部XAML “静态”资源,像<Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources>
如果这样做,那么你将不能够通知它的属性已更改。 为什么? 静态资源是静态的资源,它初始化的,并且仍然不变之后。 我可能是错在这里,但它是我的失误之一。 因此,对于TreeView控件使用ItemsSource="{Binding Fixtures}"
代替ItemsSource="{StaticResource myStaticViewModel}"
视图模型- 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);
}
}
XAML - OnPropertyChanged和指挥 。 用户点击一个按钮,以打开包含单元测试数据DLL。 当我们使用MVVM
,然后通过单击命令进行处理。 在结束OpenDllExecuted
处理OnPropertyChanged("Fixtures")
执行,通知树的财产,它被绑定到已经改变,现在是时候对自己进行刷新。 RelayCommand
辅助类可以采取例如从那里 )。 顺便说一句,因为我知道,有一些辅助库和工具包存在类似的事情发生在XAML:
和视图模型-指挥
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");
}