<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-1302241583051203640</atom:id><lastBuildDate>Mon, 09 Nov 2009 11:29:54 +0000</lastBuildDate><title>gandjustas' blog</title><description /><link>http://gandjustas.blogspot.com/</link><managingEditor>noreply@blogger.com (gandjustas)</managingEditor><generator>Blogger</generator><openSearch:totalResults>24</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/GandjustasBlog" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-179745883676786801</guid><pubDate>Sat, 07 Nov 2009 15:24:00 +0000</pubDate><atom:updated>2009-11-07T18:24:43.100+03:00</atom:updated><title>Типы, подтипы и вариантность.</title><description>&lt;p&gt;Скоро (22 марта 2010)&amp;#160; выходит Visual Studio 2010, которая будет поддерживать C# 4.0 Больше всего вопросов возникает из-за новой фичи языка – ко- и контр- вариантности. Попытаюсь дать объяснение на человеческом языке.&lt;/p&gt;  &lt;p&gt;В любой системе типов существуют отношения между типами. Нас интересует отношение типа-подтип. Для типов A и B будем обозначать A :&amp;gt; B, если A является &lt;em&gt;подтипом&lt;/em&gt; B (B является &lt;em&gt;супертипом&lt;/em&gt; A). Если A является подтипом B, то везде где в программе требуется значение типа B можно подставлять значение типа A без каких-либо дополнительных конструкций.&lt;/p&gt;  &lt;p&gt;Например во многих языках тип целых чисел является подтипом вещественных. В ОО-языках такое отношение реализуется за счет &lt;em&gt;наследования&lt;/em&gt;. Если A является наследником B, то A является подтипом B.&lt;/p&gt;  &lt;p&gt;Тут стоит вспомнить принцип LSP (принцип подстановки Барбары Лисков). Он ошибочно приписывается к ООП, хотя имеет к нему весьма отдаленное отношение. Принцип гласит что&lt;em&gt; если A :&amp;gt; B, то любое утверждение для B должно быть верно для A&lt;/em&gt;. Выполнение этого принципа означает что поведение программы при подстановке значения типа A там где требуется B не изменится.&lt;/p&gt;  &lt;p&gt;Но это я ушел в сторону.&amp;#160; Когда у нас чистый ООП язык (как smalltalk) тогда отношения типов-подтипов исчерпываются наследованием, которое создает довольно простые отношения. Все становится сложно когда появляются &lt;em&gt;типы, параметризуемые другими типами&lt;/em&gt; &lt;em&gt;(обобщенные типы)&lt;/em&gt;.&lt;/p&gt;  &lt;p&gt;Будем обозначать обобщенный типа как T&amp;lt;`a&amp;gt;, где `a – параметр типа. Конкретный тип при подстановке параметра будем обозначать T&amp;lt;A&amp;gt;, где A – какой-то тип. Для иллюстраций нам понадобится обобщенный тип с одним параметром, хотя типов-параметром может быть много.&lt;/p&gt;  &lt;p&gt;Тут возникает интересный вопрос. Если A :&amp;gt; B, то как связаны T&amp;lt;A&amp;gt; и&amp;#160; T&amp;lt;B&amp;gt; ?&lt;/p&gt;  &lt;p&gt;Тип T&amp;lt;`a&amp;gt; называется &lt;em&gt;ковариантными, &lt;/em&gt;если для A :&amp;gt; B выполняется T&amp;lt;A&amp;gt; :&amp;gt; T&amp;lt;B&amp;gt;, и &lt;em&gt;контрвариантым, &lt;/em&gt;если A :&amp;gt; B выполняется T&amp;lt;B&amp;gt; :&amp;gt; T&amp;lt;A&amp;gt;,     &lt;br /&gt;если же T&amp;lt;A&amp;gt; и&amp;#160; T&amp;lt;B&amp;gt; не связаны никакими отношениями, то такой тип называет &lt;em&gt;инвариантым.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Примеры.&lt;/p&gt;  &lt;p&gt;1)IEnumerable&amp;lt;T&amp;gt;. Например если Apple унаследован от Fruit (то есть Apple :&amp;gt; Fruit), то вполне резонно было бы иметь IEnumerable&amp;lt;Apple&amp;gt; :&amp;gt; IEnumerable&amp;lt;Fruit&amp;gt;. Действительно, в .NET 4 IEnumerable&amp;lt;T&amp;gt; является ковариантым и имеет сигнатуру &lt;strong&gt;IEnumerable&amp;lt;out T&amp;gt;.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;2)Action&amp;lt;T&amp;gt;. Например есть метод void Eat(Fruit f), он имеет тип Action&amp;lt;Fruit&amp;gt;, и у нас Apple :&amp;gt; Fruit. Тогда было бы хорошо иметь Action&amp;lt;Fuit&amp;gt; :&amp;gt; Action&amp;lt;Apple&amp;gt;, то есть если нам куда-то понадобится передавать Action&amp;lt;Apple&amp;gt; мы могли бы туда передать Action&amp;lt;Fruit&amp;gt;. В .NET 4 Action&amp;lt;T&amp;gt; является конртвариантным и имеет сигнатуру &lt;strong&gt;Action&amp;lt;in T&amp;gt;&lt;/strong&gt;.&lt;/p&gt;  &lt;h5&gt;Магические слова in и out.&lt;/h5&gt;  &lt;p&gt;Такие модификаторы были выбраны неслучайно. Ко- и контр- вариантность может приводить к ошибкам при неумелом использовании. Например массивы в .NET 2 и выше являются ковариантными. То есть там где требуется Fruit[] можно передать Apple[]. Но программист может внутри метода, обрабатывающего Fruit[] присвоить элементу массива значение типа Banana. Что приведет к runtime error.&lt;/p&gt;  &lt;p&gt;Чтобы ковариантность была безопасной необходимо чтобы ковариантные типы-аргументы были&lt;em&gt; только в выходных&lt;/em&gt; значениях методов. То есть для T&amp;lt;out `a&amp;gt; можно писать методы возвращающие `a или имеющие out-параметры типа `a. также могут быть get-only свойства, возвращающие `a.&lt;/p&gt;  &lt;p&gt;Аналогично для контрвариантного T&amp;lt;in `a&amp;gt; параметры типа `a могут быть только во &lt;em&gt;входных &lt;/em&gt;параметрах методов.&lt;/p&gt;  &lt;h5&gt;Темная сторона силы.&lt;/h5&gt;  &lt;p&gt;Ко – и контр- вариантность типов в сочетании с наследованием могут давать довольно сложные графы отношений типов, в том числе имеющие замкнутые направленные контуры (попробуйте сами такое сделать), которые срывают башню алгоритмам вычисления отношений типов.&lt;/p&gt;  &lt;p&gt;PS. В C# отношение тип-подтип проверяется оператором is.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-179745883676786801?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/ZTe89skbolo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/ZTe89skbolo/blog-post.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/11/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-586686195075043971</guid><pubDate>Tue, 29 Sep 2009 07:08:00 +0000</pubDate><atom:updated>2009-09-29T11:08:54.713+04:00</atom:updated><title>MVVM и TreeView</title><description>&lt;p&gt;У многих программистов существует неудержимое желание организовывать любые объекты в иерархии, даже когда иерархия фактически не существует. При разработке интерфейса это проявляется в использовании контролов типа TreeView.&lt;/p&gt;  &lt;p&gt;Основной недостаток TreeView в WPF заключается в том что не существует тривиальных путей подружить этот контрол с паттерном MVVM.&lt;/p&gt;  &lt;p&gt;Привязать данные к элементам дерева достаточно просто. Для этого есть &lt;a href="http://msdn.microsoft.com/ru-ru/library/system.windows.hierarchicaldatatemplate.aspx" target="_blank"&gt;HierarchicalDataTemplate&lt;/a&gt;. В первом приближении ViewModel для элементов дерева будет выглядеть так:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; TreeViewItemModel: ViewModelBase&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; _name;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; TreeViewItemModel()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;         Children = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ObservableCollection&amp;lt;TreeViewItemModel&amp;gt;();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Name&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;         get { &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; _name; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;         set { _name = &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;; OnPropertyChanged(&lt;span style="color: #006080"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;); }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; ObservableCollection&amp;lt;TreeViewItemModel&amp;gt; Children { get; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;ViewModelBase – класс из &lt;a href="http://www.codeplex.com/wpf/Release/ProjectReleases.aspx?ReleaseId=14962" target="_blank"&gt;MVVM Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;В XAML пишем простой темплейт&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemTemplate&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;HierarchicalDataTemplate&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ItemsSource&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding Children}&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TextBlock&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Text&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding Name}&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;HierarchicalDataTemplate&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemTemplate&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Теперь можно в качестве ItemsSource для TreeView указать любую коллекцию TreeViewItemModel.&lt;/p&gt;

&lt;p&gt;Обеспечить обратную связь – от TreeView к модели представления – гораздо сложнее. Свойство TreeView.SelectedValue не имеет сеттера, поэтому привязать его в XAML к модели представления не получится, надо искать обходные пути.&lt;/p&gt;

&lt;p&gt;Можно привязать свойство TreeViewItem.IsSelected к модели вида, но делать это надо в стиле дерева, а не в DataTemplate.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemContainerStyle&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;Style&lt;/span&gt; &lt;span style="color: #ff0000"&gt;TargetType&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{x:Type TreeViewItem}&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;Setter&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Property&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;IsSelected&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Value&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding IsSelected, Mode=TwoWay}&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;Style&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemContainerStyle&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;В класс TreeViewItemModel надо добавить следующее свойство:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; _isSelected;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; IsSelected&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     get { &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; _isSelected; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     set { _isSelected = &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;; OnPropertyChanged(&lt;span style="color: #006080"&gt;&amp;quot;IsSelected&amp;quot;&lt;/span&gt;); }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Теперь можно создать модель представления всего дерева:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; TreeViewModel : ViewModelBase&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; TreeViewModel()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;        TopLevelItems = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ObservableCollection&amp;lt;TreeViewItemModel&amp;gt;();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; ObservableCollection&amp;lt;TreeViewItemModel&amp;gt; TopLevelItems { get; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; TreeViewItemModel SelectedItem &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;        get&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;        {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt;            &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; TopLevelItems&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt;                       .Traverse(item =&amp;gt; item.Children)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt;                       .FirstOrDefault(m =&amp;gt; m.IsSelected);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt;        }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum18"&gt;  18:&lt;/span&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum19"&gt;  19:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div&gt;&amp;#160;&lt;/div&gt;

&lt;div&gt;Изобретение метода Traverse для обхода древообразных структур данных оставлю как домашнее задание.&lt;/div&gt;

&lt;div&gt;&amp;#160;&lt;/div&gt;

&lt;div&gt;Теперь в любой момент времени у TreeViewModel можно получить выбранный элемент. Но свойство SelectedItem не поддерживает событие PropertyChanged, поэтому в интерфейсе вы не увидите изменений.&lt;/div&gt;

&lt;div&gt;&amp;#160;&lt;/div&gt;

&lt;div&gt;Надо исправить эту досадную ситуацию. Для этого надо подписаться на событие PropertyChanged для всех узлов дерева (в том числе для всех добавляемых) и желательно отписываться при удалении узлов из дерева, при изменении свойства IsSelected узла кидать событие PropertyChanged для свойства SelectedItem.&lt;/div&gt;

&lt;div&gt;&amp;#160;&lt;/div&gt;

&lt;div&gt;Полный код TreeViewModel:&lt;/div&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; TreeViewModel : ViewModelBase&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     PropertyChangedEventHandler _propertyChangedHandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     NotifyCollectionChangedEventHandler _collectionChangedhandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; TreeViewModel()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;         TopLevelItems = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ObservableCollection&amp;lt;TreeViewItemModel&amp;gt;();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;         _propertyChangedHandler = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; PropertyChangedEventHandler(item_PropertyChanged);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;         _collectionChangedhandler = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; NotifyCollectionChangedEventHandler(items_CollectionChanged);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;         TopLevelItems.CollectionChanged += _collectionChangedhandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; ObservableCollection&amp;lt;TreeViewItemModel&amp;gt; TopLevelItems { get; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; TreeViewItemModel SelectedItem &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum18"&gt;  18:&lt;/span&gt;         get&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum19"&gt;  19:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum20"&gt;  20:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; TopLevelItems&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum21"&gt;  21:&lt;/span&gt;                        .Traverse(item =&amp;gt; item.Children)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum22"&gt;  22:&lt;/span&gt;                        .FirstOrDefault(m =&amp;gt; m.IsSelected);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum23"&gt;  23:&lt;/span&gt;         }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum24"&gt;  24:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum25"&gt;  25:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum26"&gt;  26:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; subscribePropertyChanged(TreeViewItemModel item)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum27"&gt;  27:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum28"&gt;  28:&lt;/span&gt;         item.PropertyChanged += _propertyChangedHandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum29"&gt;  29:&lt;/span&gt;         item.Children.CollectionChanged += _collectionChangedhandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum30"&gt;  30:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (var subitem &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; item.Children)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum31"&gt;  31:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum32"&gt;  32:&lt;/span&gt;             subscribePropertyChanged(subitem);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum33"&gt;  33:&lt;/span&gt;         }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum34"&gt;  34:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum35"&gt;  35:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum36"&gt;  36:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; unsubscribePropertyChanged(TreeViewItemModel item)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum37"&gt;  37:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum38"&gt;  38:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (var subitem &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; item.Children)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum39"&gt;  39:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum40"&gt;  40:&lt;/span&gt;             unsubscribePropertyChanged(subitem);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum41"&gt;  41:&lt;/span&gt;         }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum42"&gt;  42:&lt;/span&gt;         item.Children.CollectionChanged -= _collectionChangedhandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum43"&gt;  43:&lt;/span&gt;         item.PropertyChanged -= _propertyChangedHandler;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum44"&gt;  44:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum45"&gt;  45:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum46"&gt;  46:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum47"&gt;  47:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; items_CollectionChanged(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; sender, NotifyCollectionChangedEventArgs e)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum48"&gt;  48:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum49"&gt;  49:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (e.OldItems != &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum50"&gt;  50:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum51"&gt;  51:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (TreeViewItemModel item &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; e.OldItems)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum52"&gt;  52:&lt;/span&gt;             {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum53"&gt;  53:&lt;/span&gt;                 unsubscribePropertyChanged(item);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum54"&gt;  54:&lt;/span&gt;             }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum55"&gt;  55:&lt;/span&gt;         }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum56"&gt;  56:&lt;/span&gt;         &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum57"&gt;  57:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (e.NewItems != &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum58"&gt;  58:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum59"&gt;  59:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (TreeViewItemModel item &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; e.NewItems)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum60"&gt;  60:&lt;/span&gt;             {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum61"&gt;  61:&lt;/span&gt;                 subscribePropertyChanged(item);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum62"&gt;  62:&lt;/span&gt;             }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum63"&gt;  63:&lt;/span&gt;         }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum64"&gt;  64:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum65"&gt;  65:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum66"&gt;  66:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; item_PropertyChanged(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; sender, PropertyChangedEventArgs e)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum67"&gt;  67:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum68"&gt;  68:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (e.PropertyName == &lt;span style="color: #006080"&gt;&amp;quot;IsSelected&amp;quot;&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum69"&gt;  69:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum70"&gt;  70:&lt;/span&gt;             OnPropertyChanged(&lt;span style="color: #006080"&gt;&amp;quot;SelectedItem&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum71"&gt;  71:&lt;/span&gt;         }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum72"&gt;  72:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum73"&gt;  73:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;XAML код элемента TreeView:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ItemsSource&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding TopLevelItems}&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemContainerStyle&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;Style&lt;/span&gt; &lt;span style="color: #ff0000"&gt;TargetType&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{x:Type TreeViewItem}&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;Setter&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Property&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;IsSelected&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Value&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding IsSelected, Mode=TwoWay}&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;Style&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemContainerStyle&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemTemplate&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;HierarchicalDataTemplate&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ItemsSource&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding Children}&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;             &lt;span style="color: #008000"&gt;&amp;lt;!--Здесь можно задать любой шаблон для элемента--&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;TextBlock&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Text&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{Binding Name}&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;HierarchicalDataTemplate&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView.ItemTemplate&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;            &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;TreeView&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;PS. TreeViewItem также имеет свойство IsExpanded, которое очень помогает если надо сделать отложенную загрузку элементов дерева.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-586686195075043971?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/URbhuB06Vlw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/URbhuB06Vlw/mvvm-treeview.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/09/mvvm-treeview.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-506446782811183832</guid><pubDate>Wed, 08 Jul 2009 18:25:00 +0000</pubDate><atom:updated>2009-07-08T22:25:55.309+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MVVM</category><category domain="http://www.blogger.com/atom/ns#">WPF</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><title>Паттерн MVVM. Часть 2.</title><description>&lt;p&gt;В &lt;a href="http://gandjustas.blogspot.com/2009/07/mvvm-1.html" target="_blank"&gt;первой части&lt;/a&gt; я рассказал про байндинги и команды, которые позволяют вынести из формы всю логику во viewmodel.&lt;/p&gt;  &lt;p&gt;На просторах интернета можно найти &lt;a href="http://wpf.codeplex.com/Wiki/View.aspx?title=WPF%20Model-View-ViewModel%20Toolkit" target="_blank"&gt;MVVM Toolkit&lt;/a&gt;, в котором есть необходимый код, упрощающий разработку приложений с использованием MVVM.&lt;/p&gt;  &lt;p&gt;Кроме байндингов и команд немаловажную роль в MVVM играют шаблоны данных (&lt;strong&gt;DataTemplate&lt;/strong&gt;). Они позволяют задавать шаблоны отображения определенных типов, что заметно упрощает композицию элементов UI.&lt;/p&gt;  &lt;p&gt;Наиболее подробно, с примерами, применение шаблонов описано &lt;a href="http://msdn.microsoft.com/ru-ru/magazine/dd419663.aspx" target="_blank"&gt;в этой статье&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-506446782811183832?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/EgdbvDecHgI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/EgdbvDecHgI/mvvm-2.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/07/mvvm-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-3133637910528888391</guid><pubDate>Thu, 02 Jul 2009 21:17:00 +0000</pubDate><atom:updated>2009-07-03T01:17:19.658+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MVVM</category><category domain="http://www.blogger.com/atom/ns#">WPF</category><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Паттерн MVVM. Часть 1.</title><description>&lt;p&gt;MVVM – Model – View – ViewModel – паттерн организации PL (&lt;strong&gt;presentation layer&lt;/strong&gt; – уровень представления).&lt;/p&gt;  &lt;p&gt;Паттерн MVVM применяется при создании приложений с помощью WPF и Silverlight.&amp;#160;&amp;#160; Этот паттерн был придуман архитектором этих самых WPF и Silverlight - John Gossman (&lt;a href="http://blogs.msdn.com/johngossman/default.aspx"&gt;его блог&lt;/a&gt;). Паттерн MVVM применяется в Expression Blend.&lt;/p&gt;  &lt;p&gt;Идеологически MVVM похож на &lt;a href="http://martinfowler.com/eaaDev/PresentationModel.html"&gt;Presentation Model&lt;/a&gt; описанный небезызвестным Фаулером, но MVVM сильно опирается на возможности WPF.&lt;/p&gt;  &lt;p&gt;Основная особенность MVVM заключается в том, что все поведение выносится из &lt;strong&gt;представления (view) &lt;/strong&gt;в&lt;strong&gt;&amp;#160; модель представления (view model)&lt;/strong&gt;.&amp;#160; Связывание представления и модели представления осуществляется декларативными байндингами в XAML разметке. Это позволяет тестировать все детали интерфейса не используя сложных инструментальных средств.&lt;/p&gt;  &lt;p&gt;Я сначала хотел кратко описать применение MVVM и Unity для построения PL, но понял что одного поста для описания возможностей MVVM очень мало.&lt;/p&gt;  &lt;p&gt;В WPF для передачи данных между объектами и визуальными элементами используются &lt;strong&gt;байндинги&lt;/strong&gt; (binding – привязка) в простонародии &lt;em&gt;биндинги&lt;/em&gt;. Передача может быть как однонаправленная, так и двунаправленная. Работают байндинги с помощью зависимых свойств (&lt;strong&gt;DependencyProperty&lt;/strong&gt;) или интерфейса &lt;strong&gt;INotifyPropertyChanged&lt;/strong&gt;. Передача управляющих воздействий от визуальных элементов осуществляется с помощью &lt;strong&gt;команд&lt;/strong&gt;, реализующих интерфейс &lt;strong&gt;ICommand&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;Для начала надоевший уже пример SayHello.&lt;/p&gt;  &lt;p&gt;Как всегда используется супер-сложный класс бизнес логики:&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public interface ISayHelloService
{
    string SayHello(string name);
}
 
public class SayHelloSerivce : ISayHelloService
{
    public string SayHello(string name)
    {
        return &amp;quot;Привет, &amp;quot; + name;
    }
}&lt;/pre&gt;

&lt;p&gt;Теперь определение класса команды, которая состоит из пары делегатов&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class DelegateCommand : ICommand
{
    Func&amp;lt;object, bool&amp;gt; _canExecute;
    Action&amp;lt;object&amp;gt; _execute;

    //Конструктор
    public DelegateCommand(Func&amp;lt;object, bool&amp;gt; canExecute, Action&amp;lt;object&amp;gt; execute)
    {
        this._canExecute = canExecute;
        this._execute = execute;
    }

    //Проверка доступности команды
    public bool CanExecute(object parameter)
    {
        return this._canExecute(parameter);
    }

    //Выполнение команды
    public void Execute(object parameter)
    {
        this._execute(parameter);
    }

    //Служебное событие
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested += value; }
    }
}&lt;/pre&gt;

&lt;p&gt;Теперь напишем нашу модель представления.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class ViewModel: INotifyPropertyChanged
{
    //Имя
    public string Name { get; set; }

    //Текст приветствия
    public string HelloText { get; set; }

    //Команда
    public ICommand SayHelloCommand
    {
        get
        {
            return _sayHelloCommand;
        }
    }


    ISayHelloService _service;

    ICommand _sayHelloCommand;

    
    //Конструктор
    public ViewModel(ISayHelloService service)
    {
        this._service = service;

        //Создаем команду
        this._sayHelloCommand = new DelegateCommand(
            o =&amp;gt; CanExecuteHello(),
            o =&amp;gt; ExecuteHello());
    }
        
    private void ExecuteHello()
    {
        this.HelloText = _service.SayHello(this.Name);
        OnPropertyChanged(&amp;quot;HelloText&amp;quot;);
    }

    private bool CanExecuteHello()
    {
        return !string.IsNullOrEmpty(this.Name);
    }


    //Для поддержка байндинга
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, 
                    new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion
}&lt;/pre&gt;

&lt;p&gt;Получилось слегка многословно по причине того, что&amp;#160; пример искусственный.&lt;/p&gt;

&lt;p&gt;Дело за разметкой:&lt;/p&gt;

&lt;pre class="xml" name="code"&gt;&amp;lt;StackPanel&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid.ColumnDefinitions&amp;gt;
            &amp;lt;ColumnDefinition Width=&amp;quot;75&amp;quot; /&amp;gt;
            &amp;lt;ColumnDefinition Width=&amp;quot;*&amp;quot; /&amp;gt;
        &amp;lt;/Grid.ColumnDefinitions&amp;gt;
        &amp;lt;TextBlock Text=&amp;quot;Введите имя&amp;quot;/&amp;gt;
        &amp;lt;TextBox Text=&amp;quot;{Binding Name}&amp;quot; Grid.Column=&amp;quot;1&amp;quot;/&amp;gt;
    &amp;lt;/Grid&amp;gt;
    &amp;lt;TextBox Text=&amp;quot;{Binding HelloText}&amp;quot;/&amp;gt;
    &amp;lt;Button Content=&amp;quot;Сказать привет&amp;quot; Command=&amp;quot;{Binding SayHelloCommand}&amp;quot;/&amp;gt;
&amp;lt;/StackPanel&amp;gt;&lt;/pre&gt;

&lt;p&gt;И немного изменим констрктор View:&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public Window1(ViewModel model)
{
    InitializeComponent();
    DataContext = model;
}&lt;/pre&gt;

&lt;p&gt;В App.xaml уберем атрибут StartupUri, и добавим обработчик события Startup, в котором напишем следующий код:&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;var container = new UnityContainer();
container
    .RegisterType&amp;lt;ViewModel&amp;gt;()
    .RegisterType&amp;lt;ISayHelloService, SayHelloSerivce&amp;gt;();
var window = container.Resolve&amp;lt;Window1&amp;gt;();
window.Show();&lt;/pre&gt;

&lt;p&gt;Можно нажать F5 и смотреть что получилось.&lt;/p&gt;

&lt;p&gt;Теперь воспользуемся фичами WPF. &lt;/p&gt;

&lt;p&gt;Изменим код ViewModel.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class ViewModel : INotifyPropertyChanged
{
    //Имя
    public string Name
    {
        get { return this._name; }
        set
        {
            this._name = value;
            OnPropertyChanged(&amp;quot;Name&amp;quot;);
            OnPropertyChanged(&amp;quot;HelloText&amp;quot;);
        }
    }


    //Текст приветствия
    public string HelloText
    {
        get
        {
            return _service.SayHello(this.Name);
        }
    }

    string _name;
    ISayHelloService _service;

    //Конструктор
    public ViewModel(ISayHelloService service)
    {
        this._service = service;
    }

    //Для поддержка байндинга
    #region INotifyPropertyChanged Members
    //Без изменений
    #endregion
}&lt;/pre&gt;

&lt;p&gt;В разметке View уберем кнопку и поставим Mode=OneWay для байндинга второго текстбокса. &lt;/p&gt;

&lt;p&gt;Кроме этого слега изменим App.xml.cs&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;var container = new UnityContainer();
container
    .RegisterType&amp;lt;ViewModel&amp;gt;(new ContainerControlledLifetimeManager())
    .RegisterType&amp;lt;ISayHelloService, SayHelloSerivce&amp;gt;();
container.Resolve&amp;lt;Window1&amp;gt;().Show();
container.Resolve&amp;lt;Window1&amp;gt;().Show();&lt;/pre&gt;

&lt;p&gt;Два созданных окна будут разделять одну ViewModel и при вводе имени в одном из окон результат будет отображаться во всех.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-3133637910528888391?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/P0Y-VBnR7wk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/P0Y-VBnR7wk/mvvm-1.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/07/mvvm-1.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-4528243353903761752</guid><pubDate>Thu, 25 Jun 2009 17:08:00 +0000</pubDate><atom:updated>2009-06-25T22:25:37.011+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">MVP</category><category domain="http://www.blogger.com/atom/ns#">WinForms</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Паттерн MVP и Unity</title><description>&lt;p&gt;MVP – Model View Presenter – паттерн организации PL (&lt;strong&gt;presentation layer&lt;/strong&gt; – уровень представления). &lt;/p&gt;  &lt;p&gt;MVP применяется при создании десктопных интерфейсов. Выделяют три комопнента: есть &lt;strong&gt;модель&lt;/strong&gt; – группа классов, которые отдают данные или получают команды, &lt;strong&gt;представление&lt;/strong&gt; – форма обладающая состоянием и некоторым поведением. &lt;strong&gt;Презентер&lt;/strong&gt; создают для отделения бизнес-логики от деталей GUI-фреймворка. В отличие от MVC в MVP представление определяет презентер, а не наоборот.&lt;/p&gt;  &lt;p&gt;MVP обычно строится вокруг существующих GUI-фреймворков. На практике существуют две принципиально различные различные реализации паттерна – &lt;a href="http://martinfowler.com/eaaDev/SupervisingPresenter.html" target="_blank"&gt;Supervising Controller&lt;/a&gt; и &lt;a href="http://martinfowler.com/eaaDev/PassiveScreen.html" target="_blank"&gt;Passive View&lt;/a&gt;.     &lt;br /&gt;В первом случае логика помещается в обработчики событий button_click, а сами обработчики помещаются в отдельный класс. Для полной изоляции презентера от деталей представления надо писать достаточно много врапперов\адаптеров.     &lt;br /&gt;Во втором случае создается пара интерфейсов для общения между представлением и презентером. При совершении какого-либо действия представление напрямую обращается к презентеру, тот выполняет некоторый код и вызывает установку свойств представления. Passive View способствует максимальному перемещению кода в в презентер, что облегчает тестирование.&lt;/p&gt;  &lt;h4&gt;Создание MVP в WinForms с помощью Unity.&lt;/h4&gt;  &lt;p&gt;Создаем новое winforms приложение и грохаем оттуда форму.&lt;/p&gt;  &lt;p&gt;Для начала определим служебные интерфейсы.&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;//Маркерный интерфейс представления
public interface IView
{
}

//Интрефейс презентера
public interface IPresenter&amp;lt;T&amp;gt; where T:IView
{
    T View { get; set; }
}

//Базовый класс для презентера
public abstract class BasePresenter&amp;lt;T&amp;gt; :IPresenter&amp;lt;T&amp;gt; where T:IView
{
    public T View { get; set; }
}&lt;/pre&gt;

&lt;p&gt;Класс “бизнес-логики” будем использовать тот же, что и в &lt;a href="http://gandjustas.blogspot.com/2009/06/unity-aspnet-mvc.html" target="_blank"&gt;предыдущем посте&lt;/a&gt;.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public interface ISayHelloService
{
    string SayHello(string name);
}
 
public class SayHelloSerivce : ISayHelloService
{
    public string SayHello(string name)
    {
        return &amp;quot;Привет, &amp;quot; + name;
    }
}&lt;/pre&gt;

&lt;p&gt;Теперь определим рабочие интерфейсы.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public interface ISayHelloPresenter : IPresenter&amp;lt;ISayHelloView&amp;gt;
{
    void SayHello();
}

public interface ISayHelloView: IView
{
    string GetInputText();
    void SetOutputText(string text);
}&lt;/pre&gt;
Для перезнтера код будет тривиальный. 

&lt;pre class="c-sharp" name="code"&gt;public class SayHelloPresenter : BasePresenter&amp;lt;ISayHelloView&amp;gt;, ISayHelloPresenter
{
    ISayHelloService _service;

    public SayHelloPresenter(ISayHelloService service)
    {
        this._service = service;
    }

    public void SayHello()
    {
        this.View.SetOutputText(_service.SayHello(this.View.GetInputText()));
    }
}&lt;/pre&gt;
Теперь нарисуем простую формочку: 

&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_nlXJqAOzvQI/SkOvJHoP_GI/AAAAAAAAABE/MAFgScaqqLs/s1600-h/Form1%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Form1" border="0" alt="Form1" src="http://lh5.ggpht.com/_nlXJqAOzvQI/SkOvKXQMphI/AAAAAAAAABM/G4r4opRjWAQ/Form1_thumb.png?imgmax=800" width="226" height="140" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;В коде напишем следеющее:&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public partial class Form1 : Form, ISayHelloView
{
    ISayHelloPresenter _presenter;

    public Form1(ISayHelloPresenter presenter)
    {
        InitializeComponent();
        _presenter = presenter;
        //Циклическая зависимость
        _presenter.View = this; 
    }

    public string GetInputText()
    {
        return textBox1.Text;
    }

    public void SetOutputText(string text)
    {
        textBox2.Text = text;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        _presenter.SayHello();
    }
}&lt;/pre&gt;

&lt;p&gt;Циклическая зависимость в Passive View не позволяет с помощью контейнера пропихнуть все зависимости. Поэтому передача презентеру ссылки на представление делается в коде view.&lt;/p&gt;

&lt;p&gt;Теперь чтобы увязать это вместе надо создать и сконфигурировать контейнер. Сделаем это прямо в Program.cs.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;static void Main()
{
    var container = new UnityContainer();
    container
        .RegisterType&amp;lt;ISayHelloService, SayHelloSerivce&amp;gt;()
        .RegisterType&amp;lt;ISayHelloPresenter, SayHelloPresenter&amp;gt;()
        .RegisterType&amp;lt;ISayHelloView, Form1&amp;gt;()
        ;
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run((Form)container.Resolve&amp;lt;ISayHelloView&amp;gt;());
}&lt;/pre&gt;

&lt;p&gt;Вот и все.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-4528243353903761752?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/OYaMJFs_qBs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/OYaMJFs_qBs/mvp-unity.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">11</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/06/mvp-unity.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-1020183828573385031</guid><pubDate>Thu, 25 Jun 2009 09:05:00 +0000</pubDate><atom:updated>2009-06-25T13:05:27.056+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">MVC</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Unity + ASP.NET MVC</title><description>&lt;p&gt;Недавно на &lt;a href="http://habrahabr.ru/" target="_blank"&gt;хабре&lt;/a&gt; появилась &lt;a href="http://habrahabr.ru/blogs/net/62830/" target="_blank"&gt;статья о Unity&lt;/a&gt;. В одном из комментов предложили связать ASP.NET MVC и Unity.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;А вот есть прикольная задачка уже не для начинающих — связать ASP.NET MVC с Unity. Требований в целом три: &lt;/p&gt;    &lt;p&gt;— обеспечить связывание инфраструктуры контроллеров MVC с инъектором      &lt;br /&gt;— модульность       &lt;br /&gt;— уникальность контейнера в разрезе сессии &lt;/p&gt;    &lt;p&gt;Я бы расписал все, но блин, не успеваю вообще ничего, кроме работы. А вам в рамках обучающих методик будет полезно, имхо.&lt;/p&gt;    &lt;p align="right"&gt;&lt;em&gt;хабраюзер acerv&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Я напишу серию постов о применении Unity в паттернах уровня представления.&lt;/p&gt;  &lt;h4&gt;Сначала о том что такое MVC.    &lt;br /&gt;&lt;/h4&gt;  &lt;p&gt;&lt;strong&gt;MVC &lt;/strong&gt;– Model View Controller – паттерн организации PL (&lt;strong&gt;presentation layer&lt;/strong&gt; – уровень представления). Целью этого паттерна, как и многих других, служит отделение модели (логики работы программы) от представления (средств отображения информации). В итоге применение такого паттерна должно приводить к улучшению тестируемости кода.&lt;/p&gt;  &lt;p&gt;В современном виде MVC применяется в вебе. Выглядит так: есть &lt;strong&gt;модель&lt;/strong&gt; – группа классов, которые отдают данные или получают команды, есть различные &lt;strong&gt;представления&lt;/strong&gt; этих данных – HTML, сделанные с помощью какого-либо шаблонизатора, JSON, SOAP, XML или еще какой – либо. Для того чтобы передавать данные от модели к представления вводят &lt;strong&gt;контроллер&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;Все MVC фреймворки проектируются так, чтобы управление приходило сразу на контроллер. Контроллер вызывает методы модели, если нужно формирует данные для передачи представлению (эти данные называют &lt;strong&gt;Presentation Entity&lt;/strong&gt;, но термин неустоявшийся), и выбирает представление для этих данных.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;ASP.NET MVC&lt;/strong&gt; – фреймворк для ASP.NET, реализующий паттерн MVC.     &lt;br /&gt;Контроллеры в ASP.NET MVC – классы, унаследованные от класса Controller, содержащие несколько методов - “действий”&amp;#160; (&lt;strong&gt;actions&lt;/strong&gt;). По умолчанию действия отображаются на различные urlы см помощью механизма раутинга (System.Web.Routing).     &lt;br /&gt;Каждое действие возвращает ActionResult, который может вызывать генерацию HTML, сериализацию данных в JSON итд. Можно писать свои ActionResult.&lt;/p&gt;  &lt;h4&gt;Применение Unity в ASP.NET MVC.&lt;/h4&gt;  &lt;p&gt;Инфраструктура ASP.NET MVC получает имя контроллера из параметров запроса, формируемых механизмом раутинга, потом вызывает класс ControllerFactory, который по имени возвращает экземпляр контроллера.&lt;/p&gt;  &lt;p&gt;Сначала создадим новое asp.net mvc приложение. Оно уже включает в себя большой функционал, его трогать не будем.&lt;/p&gt;  &lt;p&gt;Чтобы подключить Unity к механизму создания контроллеров надо написать свой класс ControllerFactory.&amp;#160; По умолчанию используется DefaultControllerFactory, нам надо изменить в нем один метод, который создает объект контроллера.&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public class UnityControllerFactory: DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        this._container = container;
    }

    protected override IController GetControllerInstance(Type controllerType)
    {
        if (controllerType == null)
        {
            return null;
        }

        return (IController)_container.Resolve(controllerType);
    }
}&lt;/pre&gt;

&lt;p&gt;Теперь в Global.asax добавим следующий код.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;protected void Application_Start()
{
    var container = new UnityContainer();
    RegisterControllerFactory(container);
    //...
}

private static void RegisterControllerFactory(IUnityContainer container)
{
    var factory = new UnityControllerFactory(container);
    ControllerBuilder.Current.SetControllerFactory(factory);
}&lt;/pre&gt;

&lt;p&gt;Жмем F5 и все работает! В принципе для успешного применения unity в asp.net mvc этого достаточно. &lt;/p&gt;

&lt;h4&gt;Собственно инъекция зависимостей. &lt;/h4&gt;

&lt;p&gt;Буду использовать один класс “бизнес-логики” во всех примерах. &lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public interface ISayHelloService
{
    string SayHello(string name);
}

public class SayHelloSerivce : ISayHelloService
{
    public string SayHello(string name)
    {
        return &amp;quot;Привет, &amp;quot; + name;
    }
}&lt;/pre&gt;

&lt;p&gt;Надеюсь комментарии не нужны.&lt;/p&gt;

&lt;p&gt;Снова дополним global.asax.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;protected void Application_Start()
{
    var container = new UnityContainer();
    RegisterTypes(container);
    RegisterControllerFactory(container);
    //...
}

private static void RegisterTypes(IUnityContainer container)
{
    container
        .RegisterType&amp;lt;ISayHelloService, SayHelloService&amp;gt;();
}&lt;/pre&gt;

&lt;p&gt;Теперь у нас будут подпихиваться зависимости, осталось создать контроллер куда они будут подпихиваться.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class SayHelloController : Controller
{
    ISayHelloService _service;

    public SayHelloController(ISayHelloService service)
    {
        this._service = service;
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Index()
    {
        return View();
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Index(string name)
    {
	//Преобразование к object нужно чтобы строка 
	//не была принята за имя View
        return View((object)_service.SayHello(name));
    }
}&lt;/pre&gt;

&lt;p&gt;Создаем View с типом string, и помещаем в MainContent незамысловатый код:&lt;/p&gt;

&lt;pre class="html" name="code"&gt;&amp;lt;asp:Content ID=&amp;quot;Content2&amp;quot; ContentPlaceHolderID=&amp;quot;MainContent&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
&amp;lt;%using (Html.BeginForm()){%&amp;gt;
    Введите имя &amp;lt;%=Html.TextBox(&amp;quot;name&amp;quot;)%&amp;gt;
    &amp;lt;br /&amp;gt;      
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Сказать привет&amp;quot; /&amp;gt;
&amp;lt;%} %&amp;gt;

&amp;lt;%if (!string.IsNullOrEmpty(Model)){%&amp;gt;    
    &amp;lt;h3&amp;gt;&amp;lt;%=Html.Encode(Model)%&amp;gt;&amp;lt;/h3&amp;gt;      
&amp;lt;%} %&amp;gt;
&amp;lt;/asp:Content&amp;gt;&lt;/pre&gt;

&lt;p&gt;Теперь жмем F5, переходим о адресу http://localhost:&amp;lt;порт&amp;gt;/SayHello и смотрим как работает.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-1020183828573385031?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/Dtk07R1epNQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/Dtk07R1epNQ/unity-aspnet-mvc.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/06/unity-aspnet-mvc.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-8736523459035163013</guid><pubDate>Wed, 24 Jun 2009 22:09:00 +0000</pubDate><atom:updated>2009-06-25T02:13:10.677+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">MEF</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC</category><title>MEF</title><description>&lt;p&gt;MEF – Managed Extensibility Framework – новая библиотека для создания композитных приложений. То есть приложений, которые собираются из отдельных частей.&lt;/p&gt;  &lt;p&gt;Скачать его можно по адресу &lt;a href="http://mef.codeplex.com/"&gt;http://mef.codeplex.com/&lt;/a&gt;, версия на момент написания поста – Preview 5. На базе MEF построена Visual Studio 2010.&lt;/p&gt;  &lt;p&gt;MEF по функциональности похожа на IoC-контейнеры, но авторы не стремились повторить функциональность существующих контейнеров. В MEF есть несколько уникальных фич, которых нету в&amp;#160; IoC-контейнерах.&lt;/p&gt;  &lt;p&gt;Сразу примеры, снова поиск фильмов. Возьмем основной код из &lt;a href="http://gandjustas.blogspot.com/2009/01/ioc-unity.html"&gt;поста про Unity&lt;/a&gt;.&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;
// Фильм
public class Movie
{
    public string Title { get; set; }
    public string Director { get; set; }
    public int Year { get; set; }
}

/// &amp;lt;summary&gt;
/// Интерфейс репозитария
/// &amp;lt;/summary&gt;
public interface IMovieRepository
{
    IQueryable&amp;lt;Movie&amp;gt; GetMovies();
}

// Сервис поиска фильмов
[Export]
public class MovieFinder
{
    IMovieRepository _repository;

    [ImportingConstructor]
    public MovieFinder(IMovieRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable&amp;lt;Movie&amp;gt; FindByTitle(string q)
    {
        return _repository
                .GetMovies()
                .Where(m =&amp;gt; m.Title.Contains(q));
    }
}

// Заглушка для репозитария
[Export(typeof(IMovieRepository))]
public class InMemoryMovieRepository : IMovieRepository
{
    public IQueryable&amp;lt;Movie&amp;gt; GetMovies()
    {
        return new[]   
        {   
            new Movie   
            {   
                Title = &amp;quot;Гарри Поттер и узник Азкабана&amp;quot;,   
                Director = &amp;quot;Альфонсо Куарон&amp;quot;,   
                Year = 2004   
            },   
            new Movie   
            {   
                Title = &amp;quot;Звездные войны: Эпизод 2 - Атака клонов&amp;quot;,   
                Director = &amp;quot;Джордж Лукас&amp;quot;,   
                Year = 2002   
            },   
            new Movie   
            {   
                Title = &amp;quot;Властелин колец: Братство кольца&amp;quot;,   
                Director = &amp;quot;Питер Джексон&amp;quot;,   
                Year = 2001   
            },   
        }.AsQueryable();
    }
}  &lt;/pre&gt;

&lt;p&gt;В коде атрибутами размечены экспортируемые классы и импортирующий конструктор.&lt;/p&gt;

&lt;p&gt;Теперь код, который это все использует.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;static void Main(string[] args)
{
    //Создание каталога &amp;quot;частей&amp;quot;
    var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

    //Создание контейнера
    var container = new CompositionContainer(catalog);

    //Получение экземпляра
    var finder = container.GetExportedObject&amp;lt;MovieFinder&amp;gt;();
}&lt;/pre&gt;

&lt;p&gt;В текущем виде мало отличается от примера с Unity.&lt;/p&gt;

&lt;p&gt;На самом деле в MEF гораздо более широкие возможности работы с импортами и экспортами. Экспортировать можно не только классы, но и методы, и значения свойств. Импортировать можно не только через конструктор, но и через свойства и даже через приватные поля (но это не будет работать в частично доверенном окружении).&lt;/p&gt;

&lt;p&gt;Упростим пример, класс Movie и Main останутся без изменений.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;
[Export]
public class MovieFinder
{
    [Import]
    Func&amp;lt;IQueryable&amp;lt;Movie&amp;gt;&amp;gt; _getMovies = null;

    public IEnumerable&amp;lt;Movie&amp;gt; FindByTitle(string q)
    {
        return _getMovies().Where(m =&amp;gt; m.Title.Contains(q));
    }
}

public class InMemoryMovieRepository
{
    [Export(typeof(Func&amp;lt;IQueryable&amp;lt;Movie&amp;gt;&amp;gt;))]
    public IQueryable&amp;lt;Movie&amp;gt; GetMovies()
    {
        return new[]   
        {   
            //...
        }.AsQueryable();
    }
}&lt;/pre&gt;

&lt;p&gt;Также важной фичей MEF является возможность поиска частей в сборках в каталоге на диске, а также мониторинг изменений этих сборок и перезагрузка частей. Кроме того есть API для пересборки частей при изменении.&lt;/p&gt;

&lt;p&gt;Но об этом в другой раз.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-8736523459035163013?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/mfhsEk2n1ow" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/mfhsEk2n1ow/mef.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/06/mef.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-8857865872000439552</guid><pubDate>Mon, 11 May 2009 11:37:00 +0000</pubDate><atom:updated>2009-05-11T15:37:11.260+04:00</atom:updated><title>Нельзя построить коммунизм на ESB</title><description>&lt;p&gt;SOA с помощью IoC – это практически коммунизм. В точности как гласит лозунг “от каждого по возможностям, каждому - по потребностям”. Потребности – зависимости классов, возможности – реализуемые интерфейсы, а ЦК КПСС – IoC-контейнер.&lt;/p&gt;  &lt;p&gt;В архитектуре с шиной сообщений (ESB) сервисы не общаются друг с другом, а только с шиной, живут в изоляции. Это не коммунизм, а тирания.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-8857865872000439552?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/uvzGnp2OYIk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/uvzGnp2OYIk/esb.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">15</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/05/esb.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-157163040504965831</guid><pubDate>Fri, 08 May 2009 20:46:00 +0000</pubDate><atom:updated>2009-05-09T00:47:43.107+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SOA</category><category domain="http://www.blogger.com/atom/ns#">архитектура</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Плачь по SOA</title><description>&lt;p&gt;Наверное все слышали о SOA, но у большинства эти три буквы ассоциируются исключительно с ws-* вебсервисами. Хотя на самом деле это очень хорошая концепция построения программной системы из компонент, абсолютно не привязанная к способам реализации взаимодействия.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Принцип SOA состоит в следующем. Вся система разбивается на слабо связные компоненты – сервисы. Сервисы знают друг о друге только контракт. Сервисы в SOA, в отличие от объектов в ООП, не обладают свойством идентичности. Так как сервисы не знают ничего друг о друге, то должен быть общеизвестный сервис, который по контракту может выдать ссылку на сервис.&lt;/p&gt;  &lt;p&gt;Так как сервисы не обладают идентичностью, а все ссылки на сервисы с одним контрактом равнозначны, то это позволяет динамически изменять систему, подменять один компоненты другими и достаточно просто балансировать нагрузку.&lt;/p&gt;  &lt;p&gt;В мире ws-* вебсервисов контракты реализуются с помощью WSDL, а общеизвестными сервисами являются брокеры, которые по WSDL могут возвращать ссылки на сервисы. Сейчас наблюдается практически полное отсутствие ws-брокеров на просторах интернета. WSDL не описывает семантику, а только синтаксис, поэтому просто получить ссылку на сервис не получится, неизвестно вообще говоря что этот сервис делает. Кроме того нету стандартных контрактов, каждый вендор делает такой контракт как захочется. Это все приводит к тому что идея брокеров на практике оказывается нежизнеспособной. В большинстве библиотек вебсервисов не реализована поддержка UDDI – протокола общения с брокерами. Глобального коммунизма не будет.&lt;/p&gt;  &lt;p&gt;А вот в отдельно взятой &lt;strike&gt;стране&lt;/strike&gt; программе такой коммунизм построить можно. Роль контрактов будут играть интерфейсы языка, роль сервисов – объекты, реализующие интерфейсы, а в качестве брокера будет выступать IoC-контейнер. Он как раз умеет по интерфейсу отдавать ссылку на сервис, а его возможность собирать зависимости позволит избавить сами сервисы от общения с брокером.&lt;/p&gt;  &lt;p&gt;Кроме того все сервисы (их семантика) находится под контролем. Вполне можно написать тесты, которые проверяют что все сервисы, реализующие заданный контракт, делают именно&amp;#160; то, что от них требуется.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-157163040504965831?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/NSoJhvyygQM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/NSoJhvyygQM/soa.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/05/soa.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-6698880493741905488</guid><pubDate>Wed, 18 Mar 2009 23:30:00 +0000</pubDate><atom:updated>2009-03-20T17:17:08.141+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">архитектура</category><title>Ортогональность</title><description>&lt;p&gt;&lt;em&gt;Навеяно холиварами на RSDN…&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Если вы читали книгу «Программист-прагматик» Эндрю Ханта и Дэйва Томаса, то наверное знаете о чем пойдет речь.&lt;/p&gt;  &lt;p&gt;Ортогональность также называют Separation of Concerns или применением принципа “Разделяй и властвуй” в архитектуре программ, принцип SRP является отражением идеи ортогональности.&lt;/p&gt;  &lt;p&gt;Сам термин заимствован из геометрии и обозначает что линии пересекаются под прямым углом &lt;em&gt;(на самом деле из алгебры и обозначает что скалярное произведение векторов равно нулю).&lt;/em&gt; В программировании этим термином обозначают &lt;strong&gt;независимость.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Об ортогональности мало пишут в книжках и умных статьях по архитектуре.&amp;#160; Обычно пишут о паттернах, доменной модели, базах данных и об Ubiquitous Language (боюсь даже переводить). При этом ортогональность является основополагающим принципом разработки ПО.&lt;/p&gt;  &lt;p&gt;Вы сами часто сталкивались с ортогональностью в разных проявлениях:&amp;#160; разделение модели и представления, отделение Data Access Layer от Business Logic Layer, разделение данных и алгоритмов их обработки в паттернах State, Strategy. &lt;/p&gt;  &lt;p&gt;Идея ортогональности в архитектуре программ очень проста – разделять на независимые части все что только можно разделить. Если какая-то задача может быть решена независимо от других, то в решении этой задачи не должно появляться лишних связей любой природы с другими частями программы.&lt;/p&gt;  &lt;p&gt;Высокая степень ортогональности позволяет легко менять вашу программу в соответствии с изменяющимися требованиями, делает код более понятным, легче тестируемым и во многих случаях даже уменьшается объем кода.&lt;/p&gt;  &lt;p&gt;Использование IoC-контейнеров делает управление зависимостями и временем жизни объектов независимым от пользователей этих объектов. AOP ортогонализирует (ух какое слово) так называемую сквозную функциональность. Методу, результат которого кешируется, и вызывающему его коду абсолютно не надо знать что происходит кеширование и как оно происходит. Применение паттернов MVC, MVP, MVVM делает независимыми сам интерфейс, от логики работы с низлежащей моделью, и даже если модель переедет на другую машину в сети, то интерфейс править не придется. Это все примеры ортогональности.&lt;/p&gt;  &lt;p&gt;Но такой хороший принцип не очень-то любят приверженцы ООП. Ведь принцип ортогональности диктует всегда отделять данные от алгоритмов их обработки, что сильно противоречит самой идее создания объектов как сочетания данных и методов.&lt;/p&gt;  &lt;p&gt;Тут кроется самый большой обман, который культивируют многие писатели умных книг об архитектуре. На самом деле само по себе ООП не существует, оно неразрывно связано с методиками объектно-ориентированного анализа и дизайна. Следуя этим методикам можно пытаться любую программу построить как взаимодействие объектов, имеющих отражение в реальном мире. Но в реальном мире кроме объектов есть еще и информация, которая в объектах не выражается. Например два человека общаясь обмениваются информацией, которая объектом не является. Также не являются объектами записи о заказах, потому что записи на бумаге или базе данных это только различные представления информации. &lt;/p&gt;  &lt;p&gt;Информация от алгоритмов её обработки не зависит поэтому они должны быть отделены. Это не значит что объекты с методами должны быть без полей. Во-первых многие объекты могут быть параметризованы, во-вторых некоторые объекты могут иметь внутреннее изменяемое состояние, то таких объектов обычно очень мало.&lt;/p&gt;  &lt;p&gt;Под конец лирического отступления хочу привести пример очень популярного способа нарушать ортогональность.&lt;/p&gt;  &lt;h4&gt;Доменная модель или почему Фаулер неправ.&lt;/h4&gt;  &lt;p&gt;Domain Model – очень популярный паттерн построения бизнес-логики в бизнес-приложении. Заключается как раз в объединении данных, хранимых чаще всего в БД, и методов бизнес-логики в одном объекте. Все это преподносится как соответствие модели предметной области из реального мира.&lt;/p&gt;  &lt;p&gt;Сам тезис о соответствии модели предметной области из реального мира кажется мне бредовым, но сейчас я хочу на этом примере описать к чему приводит такое нарушение ортогональности при реализации.&lt;/p&gt;  &lt;p&gt;Во-первых не все методы бизнес-логики удается распихать по доменным объектам, приходится создавать искусственные объекты – контейнеры методов, что уменьшает согласованность и ухудшает читаемость.    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Во-вторых данные, объединенные с методами становится невозможно передавать по сети в многозвенных приложениях, а также возникают сложности с передачей доменных объектов просто в другой модуль. Для этих целей начинают создавать DTO, фактически моделируя передачу информации, это приводит к расползанию изменений при изменении модели данных.    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;В-третьих использование доменных объектов заставляет вытягивать из хранилища больше данных, чем необходимо для решения задачи, что может негативно сказаться на производительности. Для решения этой проблемы применяют такие паттерны, как LazyLoad, что приводит к созданию дополнительных связей которые сильно затрудняют модификацию и тестирование.&lt;/p&gt;  &lt;h4&gt;Заключение&lt;/h4&gt;  &lt;p&gt;Если стремитесь к хорошему дизайну кода, то &lt;em&gt;&lt;strong&gt;всегда&lt;/strong&gt; повышайте ортогональность системы, в том числе отделяйте все данные от алгоритмов их обработки. &lt;/em&gt;Записи о заказе абсолютно незачем знать как вычисляется сумма заказа, также как методу абсолютно неважно каким образом обрабатываются его исключения.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-6698880493741905488?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/Wm9wp-qJYNk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/Wm9wp-qJYNk/blog-post.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">27</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/03/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-5694513052621727003</guid><pubDate>Tue, 17 Mar 2009 21:59:00 +0000</pubDate><atom:updated>2009-03-18T00:59:27.636+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">Enterprise Library</category><title>Применение Enterprise Library</title><description>&lt;p&gt;Все возможности Enterprise Library можно применять по-отдельности и радоваться жизни. Но если бы это была единственная возможность я бы не написал этот пост.&lt;/p&gt;  &lt;p&gt;Магия начинается в сборке Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.    &lt;br /&gt;В этой сборке в одноименном неймспейсе содержатся call handlerы и атрибуты для навешивания обработчиков на методы. Как использовать call handlerы я рассказывал в &lt;a href="http://gandjustas.blogspot.com/2009/01/aop-unity.html"&gt;этом посте&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Кратко опишу доступные хендлеры:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;AuthorizationCallHandler &lt;/strong&gt;– задействует Security Application Block для авторизации при вызове перехваченного метода &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;CachingCallHandler – &lt;/strong&gt;обработчик, кеширующий результаты вызова перехваченного метода средствами Caching Application Block. Может быть чрезвычайно полезным для веб-разработки. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;ExceptionCallHandler&lt;/strong&gt; – позволяет декларативно задавать стратегию обработки исключений в перехваченном методе. Стратегии задаются средствами Exception Handling Application Block. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;LogCallHandler – &lt;/strong&gt;задействует Logging Application Block для логгирования вызова перехваченного метода. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;ValidationCallHandler – &lt;/strong&gt;вызывает валидацию параметров метода. Параметры валидации должны быть заданы атрибутами или в конфигурации Validation Application Block. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;PerformanceCounterCallHandler –&lt;/strong&gt; обработчик, который занимается сбором информации о параметрах быстродействия перехваченного метода. Информация сохраняется в PreformanceCounterах. В самой сборке Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers есть Installer (который можно запустить через installutil), создающий необходимые&amp;#160; счетчики производительности. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Если интересует только runtime AOP, то можно использовать не полновесный IoC-контейнер Unity, а легковесный генератор прокси-классов из Policy Injection Application Block.&lt;/p&gt;  &lt;p&gt;Кроме перехватчиков вызовов существует другой способ интеграции всех application blocks с контейнером Uinty. Практический каждый application block из состава enterprise library содержит класс – расширение Unity, которое контейнер возвращать объекты-фасады при запросе определенных типов.&lt;/p&gt;  &lt;p&gt;Все расширения можно найти в основной сборке application block, в неймспейсе Configuration.Unity. Классы расширений называются ИмяБлокаExtension.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Для Logging Application Block расширение заставляет Unity возвращать фасад логгера по запросу типа &lt;strong&gt;LogWriter&lt;/strong&gt;. &lt;/li&gt;    &lt;li&gt;Для Exception Handling Application Block&amp;#160; основной тип фасада – &lt;b&gt;ExceptionManager.&lt;/b&gt; &lt;/li&gt;    &lt;li&gt;Для Caching Application Block контейнер возвращает реализации интерфейсов &lt;strong&gt;ICacheManager&lt;/strong&gt;, &lt;strong&gt;IStorageEncryptionProvider, IBackingStore.&lt;/strong&gt; &lt;/li&gt;    &lt;li&gt;Для Security Application Block контейнер возвращает реализации интерфейсов &lt;strong&gt;IAuthorizationProvider, ISecurityCacheProvider.&lt;/strong&gt; &lt;/li&gt;    &lt;li&gt;Для Data Access Application Block контейнер возвращает экземпляры класса &lt;strong&gt;Database, &lt;/strong&gt;аналогично &lt;strong&gt;DatabaseFactory.CreateDatabase.&lt;/strong&gt; Можно получать как экземпляр по-умолчанию, так и именованный экземпляр. &lt;/li&gt;    &lt;li&gt;Для Cryptography Application Block (который находится в сборке Microsoft.Practices.EnterpriseLibrary.Security.Cryptography) расширение заставляет Unity возвращать фасад по запросу типа &lt;strong&gt;CryptographyManager, &lt;/strong&gt;а также реализации интерфейсов &lt;strong&gt;IHashProvider &lt;/strong&gt;и &lt;strong&gt;ISymmetricCryptoProvider.&lt;/strong&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;При использовании application blocks совместно с unity все настройки блоков надо задавать в файле конфигурации. Для этого в Enterpise Library есть удобная утилита.&lt;/p&gt;  &lt;p&gt;К сожалению я не смог придумать адекватного примера, который сможет показать хотя бы малую часть возможностей Enterprise Library с использованием Unity.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-5694513052621727003?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/00ngBGLQTok" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/00ngBGLQTok/enterprise-library.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/03/enterprise-library.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-5311666386590489976</guid><pubDate>Tue, 10 Feb 2009 13:13:00 +0000</pubDate><atom:updated>2009-02-10T16:13:28.810+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">Entity Framework</category><title>Баг в Entity Framework</title><description>&lt;p&gt;Обнаружил очень неприятный баг с Entity Framework.    &lt;br /&gt;ObjectContext грузит метаданные не при конструировании, а при первом обращении к методам загрузки данных. При этом вызывается this.MetadataWorkspace.LoadAssemblyForType((тип), Assembly.GetCallingAssembly());     &lt;br /&gt;Если вызывающая сборка (Assembly.GetCallingAssembly()) не содержит прямой или косвенной ссылки на сборку с метаданными, то загружены они не будут и любой метод чтения отвалится с непонятной ошибкой. &lt;/p&gt;  &lt;p&gt;Я наткнулся на такую ситуацию когда вынес extension-метод для ObjecxtContext в отдельную сборку, которая не имела ссылки на сборку с моделью. Сам extension-метод вызывался для только что созданного контекста. &lt;/p&gt;  &lt;p&gt;Workaround для этого такой: для только что созданного контекста вызвать context.MetadataWorkspace.LoadFromAssembly(context.GetType().Assembly);&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-5311666386590489976?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/tpMlhhiuUkI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/tpMlhhiuUkI/entity-framework.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/02/entity-framework.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-599917348621945265</guid><pubDate>Fri, 30 Jan 2009 19:35:00 +0000</pubDate><atom:updated>2009-01-30T22:35:38.779+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">Enterprise Library</category><title>Введение в Enterpise Library</title><description>&lt;p&gt;Enterprise Library – библиотека от группы patterns &amp;amp; practicies Microsoft. Проект живет по адресу &lt;a title="http://www.codeplex.com/entlib" href="http://www.codeplex.com/entlib"&gt;http://www.codeplex.com/entlib&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Enterprise Library состоит из набора компонент, называемых application blocks, каждый из которых решает определенную задачу, часто возникающую при разработке ПО.&amp;#160; &lt;/p&gt;  &lt;p&gt;В состав Enterprise Library входят следующие блоки: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Unity Application Block&amp;#160; - IoC-контейнер Unity&lt;/li&gt;    &lt;li&gt;Policy Injection Application Block – AOP времени выполнения&lt;/li&gt;    &lt;li&gt;Validation Application Block – небольшой фреймворк для валидации&lt;/li&gt;    &lt;li&gt;Logging Application Block – логгер&lt;/li&gt;    &lt;li&gt;Exception Handling Application Block – фреймворк для создания политик обработки исключений в приложении&lt;/li&gt;    &lt;li&gt;Caching Application Block – фреймворк для кеширования&lt;/li&gt;    &lt;li&gt;Security Application Block – библиотека для авторизации, практически повторение ASP.NET Membership&lt;/li&gt;    &lt;li&gt;Data Access Application Block – библиотека, упрощающая работу с ADO.NET, используется другими блоками&lt;/li&gt;    &lt;li&gt;Cryptography Application Block – библиотека для упрощения работы с криптографическими функциями в .NET&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Все эти блоки могут настраиваться через config-файл, для этого в составе Enterprise Library есть утилита упрощающая этот процесс.&lt;/p&gt;  &lt;p&gt;Почти все блоки можно использовать по-отдельности, но основная сила Enterprise Library состоит в том, что все блоки можно подключить через Unity.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-599917348621945265?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/-3O5N2RryHg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/-3O5N2RryHg/enterpise-library.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/enterpise-library.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-1611026501780622068</guid><pubDate>Mon, 26 Jan 2009 11:50:00 +0000</pubDate><atom:updated>2009-01-26T14:50:42.653+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Фабрики объектов в Unity</title><description>&lt;p&gt;В &lt;a title="Управление временем жизни объектов в Unity" href="http://gandjustas.blogspot.com/2009/01/unity_22.html"&gt;этом посте&lt;/a&gt; я описал как с помощью LifetimeManager можно научить Unity использовать фабрику для создания объектов.&lt;/p&gt;  &lt;p&gt;На самом деле так делать не стоит. В составе Unity есть сборка Microsoft.Practices.Unity.StaticFactory.dll в которой находится расширение контейнера для использования фабрики объектов.&lt;/p&gt;  &lt;p&gt;Регистрация фабрики происходит методом &lt;strong&gt;RegisterFactory&lt;/strong&gt;, который принимает делегат &lt;strong&gt;FactoryDelegate&lt;/strong&gt;. Этот делегат принимает параметром &lt;strong&gt;IUnityContainer&lt;/strong&gt;, и возвращает &lt;strong&gt;object&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;Пример&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;var r = new Random();
var container = new UnityContainer();
container
    .AddNewExtension&amp;lt;StaticFactoryExtension&amp;gt;()
    .Configure&amp;lt;StaticFactoryExtension&amp;gt;()
        .RegisterFactory&amp;lt;int&amp;gt;(&amp;quot;random&amp;quot;, c =&amp;gt; r.Next());&lt;/pre&gt;

&lt;p&gt;Чтобы этим всем было удобнее пользоваться можно написать extension-методы&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public static class UnityStaticFactoryExtensions
{
    private static IStaticFactoryConfiguration GetExtension(IUnityContainer container)
    {
        var ext = container.Configure&amp;lt;StaticFactoryExtension&amp;gt;();
        if (ext == null)
        {
            container.AddNewExtension&amp;lt;StaticFactoryExtension&amp;gt;();
            ext = container.Configure&amp;lt;StaticFactoryExtension&amp;gt;();
        }
        return ext;
    }

    public static IUnityContainer RegisterFactory&amp;lt;T&amp;gt;(
        this IUnityContainer container, 
        Func&amp;lt;IUnityContainer, T&amp;gt; factoryMethod)
    {
        GetExtension(container).RegisterFactory&amp;lt;T&amp;gt;(c =&amp;gt; factoryMethod(c));
        return container;
    }

    public static IUnityContainer RegisterFactory&amp;lt;T&amp;gt;(
        this IUnityContainer container, 
        string name, 
        Func&amp;lt;IUnityContainer, T&amp;gt; factoryMethod)
    {
        GetExtension(container).RegisterFactory&amp;lt;T&amp;gt;(name, c =&amp;gt; factoryMethod(c));
        return container;
    }
}&lt;/pre&gt;

&lt;p&gt;Тогда код примера, показанного выше можно записать гораздо короче&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;var r = new Random();
var container = new UnityContainer();
container.RegisterFactory(&amp;quot;random&amp;quot;, c =&amp;gt; r.Next());&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-1611026501780622068?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/ULbtKuZnQRo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/ULbtKuZnQRo/unity_26.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/unity_26.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-1220202804511068580</guid><pubDate>Sat, 24 Jan 2009 22:02:00 +0000</pubDate><atom:updated>2009-01-25T01:06:49.774+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><category domain="http://www.blogger.com/atom/ns#">AOP</category><title>AOP времени исполнения в Unity</title><description>&lt;p&gt;Для тех кто не знает – AOP это Aspect-Oriented Programming, Аспектно-ориентированное программирование. &lt;/p&gt;  &lt;p&gt;При написании любой программы программист производит функциональную декомпозицию, то есть разбивает большие блоки функциональности на более маленькие. Но всегда существуют так называемые cross-cutting concerns&amp;#160; или сквозная функциональность, которая используется всеми остальными частями программы, которую невозможно выделить&amp;#160; в отдельный модуль\класс\метод,    &lt;br /&gt;Чаще всего такой функциональностью является логгирование, разграничение доступа, управление транзакциями.&lt;/p&gt;  &lt;p&gt;Концепция AOP заключается в том что сквозная функциональность выделяется в отдельные сущности , называемые аспектами, и декларативно задается использование аспектов&amp;#160; в коде.&lt;/p&gt;  &lt;p&gt;AOP для .NET может быть реализован двумя способами: изменение кода при компиляции инструментами типа PostSharp или макросами языка Nemerle, или перехват вызовов на стадии выполнения. &lt;/p&gt;  &lt;p&gt;В составе Unity есть сборка Microsoft.Practices.Unity.Interception, которая содержит расширение контейнера Unity для перехвата вызовов объектов собираемых контейнером.&lt;/p&gt;  &lt;p&gt;Чтобы перехватывать вызовы надо контейнеру сообщить &lt;em&gt;что &lt;/em&gt;перехватывать, &lt;em&gt;как &lt;/em&gt;перехватывать, и &lt;em&gt;зачем&lt;/em&gt; перехватывать.     &lt;br /&gt;&lt;em&gt;Что&lt;/em&gt; перехватывать задается политиками (Policy), &lt;em&gt;как&lt;/em&gt; перехватывать определяют перехватчики (Interceptor), &lt;em&gt;зачем&lt;/em&gt; перехватывать определяют обработчики вызовов (CallHandlers).     &lt;br /&gt;Эти три части механизма перехвата не зависят друг от друга.&lt;/p&gt;  &lt;p&gt;Перехватчики – это классы, реализующие интерфейс &lt;strong&gt;IInterceptor&lt;/strong&gt;. В библиотеке есть классы &lt;strong&gt;InterfaceInterceptor&lt;/strong&gt; для перехвата методов интерфейса, &lt;strong&gt;VirtualMethodInterceptor &lt;/strong&gt;– для перехвата виртуальных методов класса, &lt;strong&gt;TransparentProxyInterceptor&lt;/strong&gt; – для перехвата с помощью прокси-классов, используемых для .NET Remoting. &lt;/p&gt;  &lt;p&gt;Обработчики вызовов – это классы, которые реализуют интерфейс &lt;strong&gt;ICallHandler&lt;/strong&gt;, в котором только один нужный метод – Invoke.&lt;/p&gt;  &lt;p&gt;Политики бывают двух видов – управляемая атрибутами (&lt;strong&gt;AttributeDrivenPolicy&lt;/strong&gt;) и управляемая правилами (&lt;strong&gt;RuleDrivenPolicy&lt;/strong&gt;).     &lt;br /&gt;По-умолчанию используется AttributeDrivenPolicy, которая заключается в том что обработчики вызовов задаются атрибутами, унаследованными от &lt;strong&gt;HandlerAttribute&lt;/strong&gt;, и перехватываются только те методы, для которых заданы эти атрибуты (или атрибуты заданы для классов).     &lt;br /&gt;RuleDrivenPolicy позволяет задавать какие методы будут перехватываться с помощью набора правил (&lt;strong&gt;IMatchingRule&lt;/strong&gt;)&amp;#160; и какие обработчики будут при этом вызываться.&lt;/p&gt;  &lt;p&gt;Подробнее по этой ссылке &lt;a title="http://msdn.microsoft.com/en-us/library/dd140045.aspx" href="http://msdn.microsoft.com/en-us/library/dd140045.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd140045.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Если вы сами пишете код, для которого нужен перехват, то атрибутов и стандартной политики вам будет достаточно. Если вы не можете менять код классов, но хотите их перехватывать, то это можно сделать с помощью задания политик, основанных на правилах заданных в коде или конфигурационном файле.&lt;/p&gt;  &lt;h4&gt;Пример&lt;/h4&gt;  &lt;p&gt;Сначала создадим обработчик, который просто выводит Hello, world на консоль&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public sealed class HelloWorldAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new HelloWorldCallHandler();
    }
}

public class HelloWorldCallHandler: ICallHandler
{
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        Console.WriteLine(&amp;quot;Hello, world&amp;quot;);
        return getNext()(input, getNext);
    }

    public int Order { get; set; }
}&lt;/pre&gt;

&lt;p&gt;Теперь создадим класс, унаследованный от &lt;strong&gt;MarshalByRefObject&lt;/strong&gt;, чтобы его можно было перехватывать с помощью &lt;strong&gt;TransparentProxyInterceptor&lt;/strong&gt;. Атрибут HelloWorld объявлен выше.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;[HelloWorld]
public class SomeService1: MarshalByRefObject
{
    public void Method1()
    {
        Console.WriteLine(&amp;quot;SomeService1.Method1&amp;quot;);
    }
}&lt;/pre&gt;

&lt;p&gt;В Main напишем код&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;container
    .AddNewExtension&amp;lt;Interception&amp;gt;()
    .Configure&amp;lt;Interception&amp;gt;()
        .SetDefaultInterceptorFor&amp;lt;SomeService1&amp;gt;(new TransparentProxyInterceptor());

var s = container.Resolve&amp;lt;SomeService1&amp;gt;();
s.Method1();
Console.ReadLine();&lt;/pre&gt;

&lt;p&gt;При выполнении будет выведено &lt;/p&gt;

&lt;pre&gt;Hello, world
SomeService1.Method1&lt;/pre&gt;

&lt;p&gt;Предположим что SomeService1 нам недоступен и уберем атрибут HelloWorld. Чтобы получить такую же функциональность программы надо дописать несколько строк кода&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;container
    .AddNewExtension&amp;lt;Interception&amp;gt;()
    .Configure&amp;lt;Interception&amp;gt;()
        .SetDefaultInterceptorFor&amp;lt;SomeService1&amp;gt;(new TransparentProxyInterceptor())
            .AddPolicy(&amp;quot;Policy&amp;quot;)
                .AddMatchingRule(new TypeMatchingRule(typeof(SomeService1)))
                .AddCallHandler&amp;lt;HelloWorldCallHandler&amp;gt;();

var s = container.Resolve&amp;lt;SomeService1&amp;gt;();
s.Method1();
Console.ReadLine();&lt;/pre&gt;
Аналогичные настройки можно задать в конфигурационном файле. 

  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-1220202804511068580?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/5uxCFkIPPII" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/5uxCFkIPPII/aop-unity.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/aop-unity.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-1208430006916770866</guid><pubDate>Sat, 24 Jan 2009 11:29:00 +0000</pubDate><atom:updated>2009-01-24T15:04:12.414+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Что делать если Unity-контейнер надо передавать как зависимость в другие компоненты?</title><description>&lt;p&gt;Правильный ответ – &lt;strong&gt;ничего.&lt;/strong&gt;&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;var container = new UnityContainer();
var resolvedContainer = container.Resolve&amp;lt;IUnityContainer&amp;gt;();&lt;/pre&gt;

&lt;p&gt;При выполнении такого кода resolvedContainer получит ссылку на сам контейнер. Это значит что если в любом, собираемом из контейнера, классе есть зависимость типа IUnityContainer, то она автоматически будет получать ссылку на контейнер.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-1208430006916770866?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/ZNRgYaPKfYo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/ZNRgYaPKfYo/unity_24.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/unity_24.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-5818286199791224912</guid><pubDate>Thu, 22 Jan 2009 10:14:00 +0000</pubDate><atom:updated>2009-01-22T13:14:32.752+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Конфигурация Unity</title><description>&lt;p&gt;Существует четыре различных способа конфигурирования контейнера.&lt;/p&gt;  &lt;p&gt;1)Использование соглашений. Фактически отсутствие явного конфигурирования. В Unity используется очень простое соглашение, что в классе должен быть один конструктор, который принимает все зависимости параметрами.    &lt;br /&gt;Такой способ подходит для 90% случаев если вы пишите код сами.&lt;/p&gt;  &lt;p&gt;2)Указание зависимостей с помощью атрибутов. Для свойств есть DependencyAttribute, для конструктора указывается InjectionConstructorAttribute, для метода InjectionMethodAttribute. Для параметров конструктора и injection-методов также можно указывать DependencyAttribute.    &lt;br /&gt;При навешивании DependencyAttribute на свойство или параметр можно указать имя зависимости.&lt;/p&gt;  &lt;p&gt;3)Задание конфигурации в коде при добавлении элемента в контейнер.    &lt;br /&gt;Последним параметром метода RegisterType является массив InjectionMember. В этом массиве можно передать объекты типа InjectionProperty, InjectionConstructor и InjectionMethod чтобы указать с помощью каких членов класса проводить инъекцию.     &lt;br /&gt;При указании InjectionConstructor и InjectionMethod для каждого параметра можно указать конкретное значение или попросить контейнер резолвить нужный параметр.     &lt;br /&gt;Такой способ дает гораздо более многословен, чем все остальные способы, но дает гораздо большую гибкость. С помощью конфигурации в коде можно использовать IoC с классами, к исходникам которых нет доступа.&lt;/p&gt;  &lt;p&gt;4)Задание конфигурации в XML.&amp;#160; Этот способ имеет смысл применять только если нужно изменять конфигурацию без&amp;#160; перекомпиляции&amp;#160; программы. Конфигурация в XML не позволяет описывать произвольные типы, поэтому обладает меньшей мощностью по сравнению с конфигурацией в коде.&lt;/p&gt;  &lt;p&gt;Подробнее о возможностях конфигурирования Unity можно почитать по ссылкам    &lt;br /&gt;&lt;a title="http://msdn.microsoft.com/en-us/library/dd203225.aspx" href="http://msdn.microsoft.com/en-us/library/dd203225.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd203225.aspx&lt;/a&gt;     &lt;br /&gt;&lt;a title="http://msdn.microsoft.com/en-us/library/dd203208.aspx" href="http://msdn.microsoft.com/en-us/library/dd203208.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd203208.aspx&lt;/a&gt;     &lt;br /&gt;&lt;a title="http://msdn.microsoft.com/en-us/library/dd203195.aspx" href="http://msdn.microsoft.com/en-us/library/dd203195.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd203195.aspx&lt;/a&gt;     &lt;br /&gt;&lt;a title="http://msdn.microsoft.com/en-us/library/dd203156.aspx" href="http://msdn.microsoft.com/en-us/library/dd203156.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd203156.aspx&lt;/a&gt;     &lt;br /&gt;&lt;a title="http://msdn.microsoft.com/en-us/library/dd203230.aspx" href="http://msdn.microsoft.com/en-us/library/dd203230.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd203230.aspx&lt;/a&gt;     &lt;br /&gt;    &lt;br /&gt;&lt;/p&gt;  &lt;h4&gt;Overconfiguration или Темная сторона силы&lt;/h4&gt;  &lt;p&gt;Часто программисты, познакомившись с IoC-контейнерами и оценив их возможности декларативного конфигурирования, начинают использовать конфигурацию по любому поводу. Это приводит к тому что конфиги становятся монструозных размеров и абсолютно нечитаемые. При этом по коду программы абсолютно невозможно понять какой код когда выполняется. Любые изменения затрагивают не только код, но и конфигурацию, что может привести к проблемам при развертывании.&lt;/p&gt;  &lt;p&gt;Поэтому при использовании любой технологии, которая может очень сильно конфигурироваться, предпочитайте пользоваться соглашениями (Convetions over Configuration) и размещайте конфигурацию как можно ближе к месту использования. Для этого используйте атрибуты и помещайте код конфигурирования в ту же сборку, где находятся конфигурируемые классы.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-5818286199791224912?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/PKvIRBUOC6U" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/PKvIRBUOC6U/unity_8026.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/unity_8026.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-2981088762253866367</guid><pubDate>Thu, 22 Jan 2009 06:39:00 +0000</pubDate><atom:updated>2009-01-22T09:39:29.347+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Управление временем жизни объектов в Unity</title><description>&lt;p&gt;При регистрации класса или объекта в контейнере можно указать объект LifetimeManager, который будет управлять временем жизни экземпляров в контейнере.&lt;/p&gt;  &lt;p&gt;Класс LifetimeManager очень простой, в нем всего три метода GetValue&lt;strong&gt;,&lt;/strong&gt; SetValue и RemoveValue, причем последний не используется.     &lt;br /&gt;При помещении &lt;em&gt;объекта &lt;/em&gt;в контейнер вызывается метод SetValue, а при необходимости получить объект вызывается GetValue и если он вернул &lt;em&gt;null, &lt;/em&gt;то создается новый объект.&lt;/p&gt;  &lt;p&gt;В библиотеку Microsoft.Practices.Unity входит несколько менеджеров.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;TransientLifetimeManager – &lt;/strong&gt;ничего не сохраняет, GetValue всегда возвращает null, поэтому объект создается каждый раз. Этот менеджер используется по-умолчанию при вызове RegisterType.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;ContainerControlledLifetimeManager&lt;/strong&gt; – сохраняет объект в локальной переменной. Поэтому объект живет столько же, сколько и контейнер. Этот (вернее другой, но с таким же поведением) менеджер используется по-умолчанию при вызове RegisterInstance.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;ExternallyControlledLifetimeManager – &lt;/strong&gt;сохраняет слабую ссылку (WeakReference) на объект. При использовании этого менеджера и вызове RegisterInstance сам вызывающий код должен управлять временем жизни объекта, помещенного в контейнер. Когда используется RegisterType этот менеджер будет выдавать уже существующий экземпляр объекта если он есть.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;PerThreadLifetimeManager&lt;/strong&gt; – сохраняет объекты в ThreadStatic словаре. Таким образом каждый поток в программе будет использовать свой набор объектов.&lt;/p&gt;  &lt;p&gt;Для применения Unity в ASP.NET приложениях очень легко реализовать LifetimeManager, который сохраняет объект в контексте или в сессии.&lt;/p&gt;  &lt;h3&gt;Другие области применения LifetimeManager&lt;/h3&gt;  &lt;p&gt;В Unity нет возможности регистрации метода создания объектов, но это очень легко исправить с помощью своего LifetimeManager и пары extension-методов.&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public class FactoryLifetimeManager&amp;lt;T&amp;gt;: LifetimeManager
{
    Func&amp;lt;T&amp;gt; _factoryMethod;
    LifetimeManager _baseManager;

    public FactoryLifetimeManager(Func&amp;lt;T&amp;gt; factoryMethod, LifetimeManager baseManager)
    {
        _factoryMethod = factoryMethod;
        _baseManager = baseManager;
    }

    public override object GetValue()
    {
        var obj = _baseManager.GetValue();
        if (obj == null)
        {
            obj = _factoryMethod();
            SetValue(obj);
        }
        return obj;
    }

    public override void RemoveValue()
    {
        _baseManager.RemoveValue();
    }

    public override void SetValue(object newValue)
    {
        _baseManager.SetValue(newValue);
    }
}

public static class UnityFactoryMethodExtensions
{

    public static IUnityContainer RegisterFactory&amp;lt;T&amp;gt;(this IUnityContainer container, Func&amp;lt;T&amp;gt; factoryMethod)
    {
        return container.RegisterFactory&amp;lt;T&amp;gt;(factoryMethod, new TransientLifetimeManager());
    }
    
    public static IUnityContainer RegisterFactory&amp;lt;T&amp;gt;(this IUnityContainer container, Func&amp;lt;T&amp;gt; factoryMethod, LifetimeManager lifetimeManager)
    {
        return container.RegisterType&amp;lt;T&amp;gt;(new FactoryLifetimeManager&amp;lt;T&amp;gt;(factoryMethod, lifetimeManager));
    }

    public static IUnityContainer RegisterFactory&amp;lt;T&amp;gt;(this IUnityContainer container, Func&amp;lt;T&amp;gt; factoryMethod, string name)
    {
        return container.RegisterFactory&amp;lt;T&amp;gt;(factoryMethod, name, new TransientLifetimeManager());
    }
    
    public static IUnityContainer RegisterFactory&amp;lt;T&amp;gt;(this IUnityContainer container, Func&amp;lt;T&amp;gt; factoryMethod, string name, LifetimeManager lifetimeManager)
    {
        return container.RegisterType&amp;lt;T&amp;gt;(name, new FactoryLifetimeManager&amp;lt;T&amp;gt;(factoryMethod, lifetimeManager));
    }
}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-2981088762253866367?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/3nzBpNiOmZs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/3nzBpNiOmZs/unity_22.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/unity_22.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-6700426482067812687</guid><pubDate>Wed, 21 Jan 2009 22:16:00 +0000</pubDate><atom:updated>2009-01-22T01:16:51.163+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Инъекция массивов в Unity</title><description>&lt;p&gt;Unity умеет резолвить зависимости-массивы. Для такой зависимости контейнер возвращает объекты всех подходящих типов. Даже при разрешении зависимостей с указанием имени будут возвращены все подходящие типы.&lt;/p&gt;  &lt;p&gt;Также этого Unity поддерживает разрешение обобщенных массивов.&lt;/p&gt;  &lt;h4&gt;Пример&lt;/h4&gt;  &lt;p&gt;Интерфейс и классы логгеров&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public interface ILogger
{
    void Write(string message);
}

public class TraceLogger: ILogger
{
    public void Write(string message)
    {
        Trace.Write(message);
    }
}

public class ConsoleLogger: ILogger
{
    public void Write(string message)
    {
        Console.WriteLine(message);
    }
}&lt;/pre&gt;

&lt;p&gt;Сервис, принимающий обобщенный массив&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class SomeService2&amp;lt;T&amp;gt;
{
    public SomeService2(T[] array)
    {

    }
}&lt;/pre&gt;

&lt;p&gt;Код, получающий нужный экземпляр&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;var container = new UnityContainer();
container
    .RegisterType&amp;lt;ILogger, ConsoleLogger&amp;gt;(&amp;quot;ConsoleLogger&amp;quot;)
    .RegisterType&amp;lt;ILogger, TraceLogger&amp;gt;(&amp;quot;TraceLogger&amp;quot;);

var service = container.Resolve&amp;lt;SomeService2&amp;lt;ILogger&amp;gt;&amp;gt;();&lt;/pre&gt;

&lt;p&gt;При вызове такого кода в конструктор SomeService2 будет передан массив из двух логгеров.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-6700426482067812687?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/T0LvDcwf8HQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/T0LvDcwf8HQ/unity.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/unity.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-6989594332339641988</guid><pubDate>Wed, 21 Jan 2009 14:57:00 +0000</pubDate><atom:updated>2009-01-22T01:19:19.511+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Использование generic-ов в Unity</title><description>&lt;p&gt;Unity поддерживает работу с generic-типами.&amp;#160; &lt;br /&gt;Можно в контейнер поместить обобщенный тип,&amp;#160; а потом запросить тип с конкретными параметрами.&amp;#160; Подробнее описано по ссылке &lt;a title="http://msdn.microsoft.com/en-us/library/dd203156.aspx" href="http://msdn.microsoft.com/en-us/library/dd203156.aspx"&gt;http://msdn.microsoft.com/en-us/library/dd203156.aspx&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Пример кода, использующего такую фичу.&lt;/h4&gt;  &lt;p&gt;Рассмотрим обобщенный класс репозитария для работы с данными&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public interface IRepository&amp;lt;T&amp;gt;
{
    IQueryable&amp;lt;T&amp;gt; Items();
    void Save(T item);
    void Delete(T item);
}&lt;/pre&gt;

&lt;p&gt;Реализация этого интерфейса для Entity Framework&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class EFRepository&amp;lt;T&amp;gt; : IRepository&amp;lt;T&amp;gt; where T : EntityObject
{
    ObjectContext _context;
    string _entitySetName;

    public EFRepository(ObjectContext context)
    {
        _context = context;
        var container = _context
            .MetadataWorkspace
            .GetEntityContainer(
                _context.DefaultContainerName, 
                DataSpace.CSpace);
        var edmEntityType = (EdmEntityTypeAttribute)typeof(T)
            .GetCustomAttributes(
                typeof(EdmEntityTypeAttribute), 
                false)
            .Single();

        _entitySetName = container.BaseEntitySets
            .Single(s =&amp;gt; s.ElementType.Name == edmEntityType.Name
                &amp;amp;&amp;amp; 
                s.ElementType.NamespaceName == edmEntityType.NamespaceName)
            .Name;
    }

    public IQueryable&amp;lt;T&amp;gt; Items()
    {
        return _context.CreateQuery&amp;lt;T&amp;gt;(_entitySetName);
    }

    public void Save(T item)
    {
        switch (item.EntityState)
        {
            case System.Data.EntityState.Detached:
                _context.AddObject(_entitySetName, item);
                goto case System.Data.EntityState.Added;

            case System.Data.EntityState.Added:
            case System.Data.EntityState.Modified:
                _context.SaveChanges();
                break;
            default:
                break;
        }
    }

    public void Delete(T item)
    {
        _context.DeleteObject(item);
        _context.SaveChanges();
    }
}&lt;/pre&gt;

&lt;p&gt;Есть EF модель с типом контекста&amp;#160; AddressBookEntities и двумя сущностями Person и PhoneNumber и сервис SomeService1, работающий с этими сущностями. 
  &lt;br /&gt;Конструктор сервиса выглядит так:&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public SomeService1(IRepository&amp;lt;Person&amp;gt; personsRepository, 
                    IRepository&amp;lt;PhoneNumber&amp;gt; phoneNumbersRepository)
{

}&lt;/pre&gt;

&lt;p&gt;Чтобы получить от Unity экземпляр сервиса можно написать такой код:&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;var container = new UnityContainer();
container
    .RegisterType&amp;lt;ObjectContext, AddressBookEntities&amp;gt;(
            new ContainerControlledLifetimeManager(),
            new InjectionConstructor())
    .RegisterType(typeof(IRepository&amp;lt;&amp;gt;), typeof(EFRepository&amp;lt;&amp;gt;));

var service = container.Resolve&amp;lt;SomeService1&amp;gt;();&lt;/pre&gt;

&lt;p&gt;Unity сам создаст инстансы конкретных типов при сборке зависимостей SomeService1.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-6989594332339641988?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/rhLIWgU3Qnw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/rhLIWgU3Qnw/generic-unity.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/generic-unity.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-4309422568270616196</guid><pubDate>Wed, 21 Jan 2009 08:09:00 +0000</pubDate><atom:updated>2009-01-21T11:09:24.861+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">рефакторинг</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Рефакторинг legacy кода для использования Unity</title><description>&lt;p&gt;Представим себе код, который был написан без применения принципов IoC.    &lt;br /&gt;Например такой&lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public class AccountManager
{
    public void CreateAccount(string userName, string password)
    {
        var accountStore = new AccountStore();
        accountStore.AddNewAccount(userName, password);
    }

    public bool Authenticate(string userName, string password)
    {
        var accountStore = new AccountStore();
        if (accountStore.IsValidAccount(userName, password))
        {
            Session.CurrentSession.SetAuthToken(userName);
            return true;
        }
        else
        {
            return false;
        }
    }

    public bool IsUserInRole(string userName, string roleName)
    {
        var roleStore = new RoleStore();
        return roleStore
                .GetRolesForUser(userName)
                .Contains(roleName);
    }
}&lt;/pre&gt;

&lt;p&gt;Классы AccountStore и RoleStore – это персистентные хранилища, а класс Session хранит информацию о текущем сеансе работы пользователя.&lt;/p&gt;

&lt;p&gt;Класс AccountManager применяется во многих местах, поэтому&amp;#160; мы не можем сразу вынести все зависимости в конструктор.&lt;/p&gt;

&lt;p&gt;Поэтому сначала немного отрефакторим класс AccountManager и вынесем зависимости в открытые свойства, чтобы через них можно было подпихивать зависимости.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class AccountManager
{
    [Dependency]
    public AccountStore AccountStore { get; set; }

    [Dependency]
    public RoleStore RoleStore { get; set; }

    [Dependency(&amp;quot;GetCurrentSession&amp;quot;)]
    public Func&amp;lt;Session&amp;gt; GetCurrentSession { get; set; }

    public AccountManager()
    {
        AccountStore = new AccountStore();
        RoleStore = new RoleStore();
        GetCurrentSession = () =&amp;gt; Session.CurrentSession;
    }

    public void CreateAccount(string userName, string password)
    {
        AccountStore.AddNewAccount(userName, password);
    }

    public bool Authenticate(string userName, string password)
    {
        if (AccountStore.IsValidAccount(userName, password))
        {
            GetCurrentSession().SetAuthToken(userName);
            return true;
        }
        return false;
    }

    public bool IsUserInRole(string userName, string roleName)
    {
        return RoleStore
                .GetRolesForUser(userName)
                .Contains(roleName);
    }
}&lt;/pre&gt;

&lt;p&gt;В этом коде атрибутами сразу обозначены зависимости.&amp;#160; Эти атрибуты будет анализировать Unity при инъекции зависимостей. 
  &lt;br /&gt;Так как при инъекции компоненты подбираются по типу, то для функцции GetCurrentSession желательно указать дополнительный строковый идентификатор.&lt;/p&gt;

&lt;p&gt;Теперь будем прикручивать IoC-контейнер. 
  &lt;br /&gt;Нам сначала понадобится создать синглтон контейнера Unity. 

  &lt;br /&gt;Многие, в том числе и я, считают синглтоны злом, но в данном случае немного зла необходимо.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public static class UnitySingleton
{
    static IUnityContainer _instance = new UnityContainer();

    public static UnitySingleton()
    {
	//Здесь будем конфигурировать контейнер
        _instance
            .RegisterType&amp;lt;AccountStore&amp;gt;()
            .RegisterType&amp;lt;RoleStore&amp;gt;()
            .RegisterInstance&amp;lt;Func&amp;lt;Session&amp;gt;&amp;gt;(
                    &amp;quot;GetCurrentSession&amp;quot;,
                    () =&amp;gt; Session.CurrentSession);
    }

    public static IUnityContainer Instance
    {
        get
        {
            return _instance;
        }
    }
}&lt;/pre&gt;

&lt;p&gt;Для регистрации метода получения текущей сессии используется RegisterInstance. Этот метод помещает в контейнер экземпляр объекта, указанный параметром. Контейнер удерживает этот объект все время, но с помощью LifetimeManager это поведение можно изменить.&lt;/p&gt;

&lt;p&gt;Теперь в конструкторе AccountManager напишем только одну строчку&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public AccountManager()
{
    UnitySingleton.Instance.BuildUp(this);
}&lt;/pre&gt;

&lt;p&gt;После таких преобразований можно выделять интерфейсы классов, используемых внутри AccountManager чтобы ослабить зависимости между классами, а также проводить аналогичный рефакторинг для классов, использующих AccountManager. &lt;/p&gt;

&lt;p&gt;Когда сам класс AccountManager нигде не будет инстанцироваться явно через new, а только доставаться из контейнера, тогда можно будет рефакторить AccountManager, чтобы он получал&amp;#160; все зависимости через конструктор.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-4309422568270616196?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/bh_njc8Zlak" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/bh_njc8Zlak/legacy-unity.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/legacy-unity.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-7537845496532528788</guid><pubDate>Tue, 20 Jan 2009 21:22:00 +0000</pubDate><atom:updated>2009-01-21T01:05:59.112+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Используем IoC-контейнер Unity</title><description>&lt;p&gt;Unity – IoC-контейнер от Microsoft, разработанный группой Patterns&amp;amp;Practicies. Найти его можно по адресу &lt;a title="http://www.codeplex.com/unity" href="http://www.codeplex.com/unity"&gt;http://www.codeplex.com/unity&lt;/a&gt;. Также Unity включен в состав Enterprise Library.&lt;/p&gt;  &lt;p&gt;Будет использоваться версия 1.2, последняя на данный момент.&lt;/p&gt;  &lt;p&gt;Итак приступим.    &lt;br /&gt;Как &lt;a href="http://martinfowler.com/articles/injection.html"&gt;завещал дядька Фаулер&lt;/a&gt; будем рассматривать использования контейнера на примере программы, работающей&amp;#160; с базой данных фильмов.&lt;/p&gt;  &lt;p&gt;Чтобы использовать Unity в своей программе надо подключить сборки Microsoft.Practices.ObjectBuilder2 и Microsoft.Practices.Unity.&lt;/p&gt;  &lt;p&gt;Нам надо написать класс, который позволяет искать фильмы по различным параметрам.&lt;/p&gt;  &lt;p&gt;Предположим что фильмы описываются классом Movie &lt;/p&gt;  &lt;pre class="c-sharp" name="code"&gt;public class Movie
{
    public string Title { get; set; }
    public string Director { get; set; }
    public int Year { get; set; }
}&lt;/pre&gt;

&lt;p&gt;Работу с БД завернем в интерфейс&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public interface IMovieRepository
{
    IQueryable&amp;lt;Movie&amp;gt; GetMovies();
}&lt;/pre&gt;

&lt;p&gt;Теперь можем написать класс, который нам нужен. Следуя принципам IoC будем передавать зависимости через конструктор.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class MovieFinder
{
    IMovieRepository _repository;

    public MovieFinder(IMovieRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable&amp;lt;Movie&amp;gt; FindByTitle(string q)
    {
        return _repository
                .GetMovies()
                .Where(m =&amp;gt; m.Title.Contains(q));
    }
}&lt;/pre&gt;

&lt;p&gt;В целях тестирования напишем простую реализацию IMovieRepository.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class InMemoryMovieRepository : IMovieRepository
{
    public IQueryable&amp;lt;Movie&amp;gt; GetMovies()
    {
        return new[]
        {
            new Movie
            {
                Title = &amp;quot;Гарри Поттер и узник Азкабана&amp;quot;,
                Director = &amp;quot;Альфонсо Куарон&amp;quot;,
                Year = 2004
            },
            new Movie
            {
                Title = &amp;quot;Звездные войны: Эпизод 2 - Атака клонов&amp;quot;,
                Director = &amp;quot;Джордж Лукас&amp;quot;,
                Year = 2002
            },
            new Movie
            {
                Title = &amp;quot;Властелин колец: Братство кольца&amp;quot;,
                Director = &amp;quot;Питер Джексон&amp;quot;,
                Year = 2001
            },
        }.AsQueryable();
    }
}&lt;/pre&gt;

&lt;p&gt;Напишем короткий код, который собирает все вместе.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;//Создаем контейнер
var container = new UnityContainer();

//Помещаем в контейнер реализацию для используемого интерфейса
container.RegisterType&amp;lt;IMovieRepository, InMemoryMovieRepository&amp;gt;();

//Собираем нужный объект
var finder = container.Resolve&amp;lt;MovieFinder&amp;gt;();&lt;/pre&gt;

&lt;p&gt;Обратите внимание, что сам тип MovieFinder необязательно помещать в контейнер.&lt;/p&gt;

&lt;p&gt;Теперь можно заняться базой данных. 
  &lt;br /&gt;Создадим EF модель с одной сущностью Movie, эту сущность будем использовать вместо нашего класса Movie.&lt;/p&gt;

&lt;p&gt;Реализация IMovieRepository для БД будет выглядеть так: &lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;public class EFMovieRepository: IMovieRepository
{
    MoviesContainer _context;

    public EFMovieRepository(MoviesContainer context)
    {
        _context = context;
    }

    public IQueryable&amp;lt;Movie&amp;gt; GetMovies()
    {
        return _context.Movies;
    }
}&lt;/pre&gt;

&lt;p&gt;Где MoviesContainer – тип контекста EF.&lt;/p&gt;

&lt;p&gt;Чтобы использовать этот репозитарий слегка изменим код основной программы.&lt;/p&gt;

&lt;pre class="c-sharp" name="code"&gt;var container = new UnityContainer();
container
    .RegisterType&amp;lt;IMovieRepository, EFMovieRepository&amp;gt;()
    .RegisterType&amp;lt;MoviesContainer&amp;gt;(new InjectionConstructor());

var finder = container.Resolve&amp;lt;MovieFinder&amp;gt;();&lt;/pre&gt;

&lt;p&gt;Параметр new InjectionConstructor() для второго вызова RegisterType говорит контейнеру что надо использовать конструктор без параметров для создания объекта этого класса. Если конструктор в классе всего один, то задавать такой параметр не требуется.&lt;/p&gt;

&lt;p&gt;В приведенном выше коде есть один недостаток. Все объекты создаются каждый раз при вызове Resolve. Если мы не хотим пересоздавать каждый раз контекст EF (это длительная операция), то можем сделать его синглтоном. 
  &lt;br /&gt;Делается это очень просто, достаточно заменить вызов 

  &lt;br /&gt;.RegisterType&amp;lt;MoviesContainer&amp;gt;(new InjectionConstructor()) 

  &lt;br /&gt;на 

  &lt;br /&gt;.RegisterType&amp;lt;MoviesContainer&amp;gt;( 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; new ContainerControlledLifetimeManager(), 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; new InjectionConstructor()) 

  &lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Первый параметр задает LifetimeManager, который, как понятно из названия, управляет временем жизни объекта. ContainerControlledLifetimeManager означает что объект будет жить пока живет контейнер, и для всех вызовов Resolve будет возвращаться один и тот же экземпляр контекста.&lt;/p&gt;

&lt;p&gt;В принципе этого достаточно чтобы начать использовать Unity в своем проекте.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-7537845496532528788?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/lQ3_TzcC_XE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/lQ3_TzcC_XE/ioc-unity.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/ioc-unity.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-6483078555849140357</guid><pubDate>Tue, 20 Jan 2009 09:56:00 +0000</pubDate><atom:updated>2009-01-20T13:10:31.393+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Основная задача IoC-контейнеров</title><description>&lt;p&gt;Многократно встречал мнение что использовать IoC-контейнер стоит только тогда, когда нужно изменение конфигурации без перекомпиляции программы.    &lt;br /&gt;Такое мнение пришло из мира Java, где частью любой крупной программы является множество конфигурационных XML файлов. В других платформах зачастую возможность внешнего конфигурирования используемых компонент избыточна.&lt;/p&gt;  &lt;p&gt;На самом деле основной задачей IoC-контейнеров является &lt;em&gt;декларативное управление временем жизни компонент, и зависимостями между ними&lt;/em&gt;. &lt;/p&gt;  &lt;p&gt;Внешние конфиги, позволяющие менять состав компонент без перекомпиляции, AOP времени исполнения и другие приблуды являются необязательными. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-6483078555849140357?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/QrQWGvyIwwc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/QrQWGvyIwwc/ioc_20.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/ioc_20.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1302241583051203640.post-8568699465278935196</guid><pubDate>Mon, 19 Jan 2009 19:03:00 +0000</pubDate><atom:updated>2009-01-20T00:07:32.727+03:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Unity</category><category domain="http://www.blogger.com/atom/ns#">DL</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">IoC</category><category domain="http://www.blogger.com/atom/ns#">DI</category><category domain="http://www.blogger.com/atom/ns#">IoC-контейнер</category><title>Введение в IoC</title><description>(&lt;a href="http://www.rsdn.ru/forum/message/3243723.1.aspx"&gt;Пост на RSDN&lt;/a&gt;)
&lt;p&gt;Есть класс A и зависит от от класса B (использует его).
&lt;/p&gt;
Например так:
&lt;pre name="code" class="c-sharp"&gt;
public class A
{
   B b = new B();
   void foo()
   { 
      //используем b
   }
}

public class B
{
    ...
}
&lt;/pre&gt;
&lt;p&gt;
В этом коде существует несколько проблем.
&lt;br/&gt;1)Невозможно тестировать класс A в отрыве от B. Если B работает с БД, то для тестов A вам понадобится база.
&lt;br/&gt;2)Временем жизни объекта B управляет А, нельзя например использовать один и тот же объект B в разных местах.
&lt;/p&gt;

Чтобы победить это нам сначало надо выделить интерфейс B и назвать его IB.
&lt;pre name="code" class="c-sharp"&gt;
public interface IB
{
    ...
}

public class A
{
    IB b = new B();    
  
    void foo()  
    {    
        //используем b  
    }
}

public class B:IB
{
    ...
}
&lt;/pre&gt;
Но ни одну из наших проблем это не решило.

&lt;p&gt;Теперь можно применить паттерн Service Locator. Суть этого паттерна состоит в том что имеем фабричный метод который по идентификатору возвращает нужную реализацию интерфейса. В .NET есть IServiceProvider с одном методом GetService, которому параметром передается тип.&lt;/p&gt;

Предположим что мы сделали хорошую реализацию IServiceProvider и у нас получился такой код
&lt;pre name="code" class="c-sharp"&gt;
public interface IB
{
    ...
}
public class A
{
    //serviceLocator - реалзиация IServiceProvider
    //которая по типу IB возвращает B
    IB b = (IB)serviceLocator.GetService(typeof(IB)); 
   
    void foo()  
    {
        //используем b
    }
}

public class B:IB
{
    ...
}
&lt;/pre&gt;
&lt;p&gt;Теперь временем жизни B управляет serviceLocator, A не знает о классе B. Можно через serviceLocator подпихнуть любую реализацию IB.&lt;br/&gt;
Этот подход называет &lt;span style="font-weight:bold;"&gt;Dependency Lookup&lt;/span&gt; — поиск зависимостей, это один из вариантов подхода IoC.&lt;/p&gt;

&lt;p&gt;Но у нас появилась зависимость от serviceLocator, теперь хоть тестировать A без класса B возможно, но это потребует настройки локатора (возможно совсем нетривиальной).&lt;/p&gt;

Если подойти с другой стороны, то можно сделать так чтобы класс A не искал зависимости сам, а получал их извне. Например через конструктор, свойство или метод.
&lt;pre name="code" class="c-sharp"&gt;
public interface IB
{
    ...
}

public class A
{    
    IB b;    
    
    //Например так  
    public A(IB b)
    {
        this.b = b;
    }

    //Или так  
    public IB B { {get {return b;} set {b = value;}}  

    //Или так  
    public void SetB(IB b)
    {
        this.b = b;
    }  
    
    void foo()
    {    
        //используем b
    }
}

public class B:IB
{
    ...
}
&lt;/pre&gt;
Тогда вызывающий код дожен выгядеть примерно так
&lt;pre name="code" class="c-sharp"&gt;
...
var a = new A(new B());
...
&lt;/pre&gt;

&lt;p&gt;Теперь у нас A не зависит ни от чего, а все зависимости можно передать например через конструктор, что значительно облегчает тестирование A.&lt;br/&gt;
Этот подход называется &lt;span style="font-weight:bold;"&gt;Dependency Injection&lt;/span&gt; — инъекция зависимостей, это другой вариант IoC.&lt;/p&gt;

&lt;p&gt;Но в большем масштабе проблемы не решает, только перемещает её на уровень выше, то есть все зависимости перемещаются в вызывающий код.&lt;/p&gt;

&lt;p&gt;Если такой подход применить во всей программе, то в итоге метод main (или другая точка входа) будет выглядеть так:
&lt;pre name="code" class="c-sharp"&gt;new Program(new A(new B(new C(), new D()), new E()......)&lt;/pre&gt;
И это мы еще не рассматривали управление временем жизни объектов.&lt;/p&gt;

&lt;p&gt;На помощь нам приходят IoC-контейнеры.&lt;br/&gt;
Сами IoC-контейнеры похожи на ServiceLocator, только делают чуть больше работы.&lt;br/&gt; При запросе объекта какого-то типа у контейнера он решает объект какого типа вернуть. Для каждого типа, зарегистрированного в IoC-контейнере, есть карта зависимостей, то есть описание какие параметры надо передавать в конструктор, каким свойствам надо присваивать и какие методы вызывать чтобы инъектировать зависимости в объект. Карта зависимостей задается внешне или получается с помощью рефлексии.&lt;br/&gt;
Кроме того контейнер содержит ассоциации для какого запрошенного идентификатора объект какого типа надо вернуть. В качестве идентификатора чаще всего используется сам тип. Для каждой зависимости запрошенного объекта, контейнер создает дургой объект у которого тоже могут быть зависимости, для них эта операция вызывается рекурсивно.&lt;br&gt;
В принципе контейнер не обязан каждый раз создавать объект, он может управлять его временем жизни.&lt;/p&gt;

&lt;p&gt;Хороший IoC контейнер должен возможности чтобы код выше можно было переписать так:
&lt;pre name="code" class="c-sharp"&gt;container.Resolve&amp;lt;program&gt;();&lt;/pre&gt;
А также потребуется где-то задать параметры контейнеру, чтобы он по запросу объекта типа IB возвращал объект типа B, для IA возвращал A и так далее.
&lt;br/&gt;
Обычно параметры контейнера можно задавать как в коде, так и во внешнем конфигурационном файле.&lt;/p&gt;

&lt;p&gt;Кроме того IoC-контейнер можно использовать как Service Locator, но такого надо избегать.&lt;/p&gt;


&lt;p&gt;Для .NET существует множество контейнеров. От MS — &lt;a href="http://www.codeplex.com/unity"&gt;Unity&lt;/a&gt;, даже &lt;a href="http://msdn.microsoft.com/en-us/library/dd362339.aspx"&gt;версия для Silverlight&lt;/a&gt; уже появилась, &lt;a href="http://code.google.com/p/autofac/"&gt;autofac&lt;/a&gt;, &lt;a href="http://www.castleproject.org/container/"&gt;Castle Windsor&lt;/a&gt;, &lt;a href="http://structuremap.sourceforge.net/"&gt;StructureMap&lt;/a&gt; и &lt;a href="http://www.springframework.net/"&gt;Spring.NET&lt;/a&gt;.
Spring.NET — клон java библиотеки со всеми вытекающими. Куча конфигов в XML, огромное число классов в библиотеке, слабое использование возможностей .NET, лично меня от него воротит после использования достаточно легковесного unity.&lt;/p&gt;
&lt;br/&gt;
&lt;p&gt;Кроме того MS ведет разработку библиотеки &lt;a href="http://www.codeplex.com/MEF"&gt;MEF&lt;/a&gt;. В ней применяются принципы IoC, но для более крупных компонент, а также возможности менять набор компонент в Runtime. MEF будет в составе .NET 4.0&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1302241583051203640-8568699465278935196?l=gandjustas.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GandjustasBlog/~4/GtTwU9n5nxE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/GandjustasBlog/~3/GtTwU9n5nxE/ioc.html</link><author>noreply@blogger.com (gandjustas)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://gandjustas.blogspot.com/2009/01/ioc.html</feedburner:origLink></item></channel></rss>
