{"id":3674,"date":"2013-02-27T16:27:57","date_gmt":"2013-02-27T14:27:57","guid":{"rendered":"http:\/\/www.centigrade.de\/blog\/en\/?p=3674"},"modified":"2013-02-28T17:10:33","modified_gmt":"2013-02-28T15:10:33","slug":"selektionen-in-wpf","status":"publish","type":"blog","link":"https:\/\/www.centigrade.de\/de\/blog\/selektionen-in-wpf\/","title":{"rendered":"Selektionen in WPF"},"content":{"rendered":"<p>Es gibt Funktionalit\u00e4ten, die man fr\u00fcher oder sp\u00e4ter immer mal wieder braucht. Das korrekte Behandeln von Selektionen in WPF ist hierf\u00fcr ein gutes Beispiel. Durchsucht man allerdings das Internet nach L\u00f6sungsans\u00e4tzen, so l\u00e4sst sich feststellen, dass die meisten Vorschl\u00e4ge zu Br\u00fcchen mit dem MVVM-Pattern oder unerw\u00fcnschten Seiteneffekten f\u00fchren. Dieser Artikel setzt sich zum Ziel, geeignete Methoden zur Arbeit mit Selektionen in der WPF vorzustellen.<!--more--><\/p>\n<h2>Selektion innerhalb einer ListBox<\/h2>\n<p>Die Selektion innerhalb einer ListBox ist eigentlich eine einfache Sache. Die Eintr\u00e4ge einer ListBox werden durch ihre ItemsSource-Eigenschaft an eine ObservableCollection des ViewModels gebunden. Durch den DisplayMemberPath wird das innerhalb einer ListBox-Zeile auszugebende Property  bestimmt, im vorliegenden Beispiel <em>Name<\/em>. Weiterhin wird die aktuelle Selektion der ListBox an das Property SelectedItem gebunden. Somit ergibt sich als ViewModel die folgende Klasse:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> SingleViewModel \r\n{\r\n  ObservableCollection&lt;ItemViewModel&gt; _items;\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ObservableCollection&lt;ItemViewModel&gt; Items\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> _items ?? (_items = CollectionCreator.CreateItems(200)); } \r\n  }\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ItemViewModel SelectedItem { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n}\r\n<\/pre>\n<p>Ein Eintrag in der OberservableCollection ist wiederum ein ViewModel. Es besteht nur aus dem Property Name:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> ItemViewModel\r\n{\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ItemViewModel(<span class=\"kwrd\" style=\"color: #0000ff;\">string<\/span> name)\r\n  {\r\n    Name = name;\r\n  }\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">string<\/span> Name { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">private<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n}\r\n<\/pre>\n<p>Die View stellt zu Demonstrationszwecken das ausgew\u00e4hlte Element der ListBox als Text dar. <\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">DockPanel<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DataContext<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{StaticResource ViewModel}\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TextBlock<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DockPanel<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Dock<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Bottom\"<\/span> \r\n    <span class=\"attr\" style=\"color: #ff0000;\">Text<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedItem.Name}\"<\/span> \r\n    <span class=\"attr\" style=\"color: #ff0000;\">Height<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"25\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ListBox<\/span> <span class=\"attr\" style=\"color: #ff0000;\">ItemsSource<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding Items}\"<\/span> \r\n      <span class=\"attr\" style=\"color: #ff0000;\">DisplayMemberPath<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Name\"<\/span>\r\n      <span class=\"attr\" style=\"color: #ff0000;\">SelectedItem<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedItem}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">DockPanel<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n<\/pre>\n<p>Nachdem wir dem Datenkontext der View eine Instanz seines SingleViewModels zugewiesen haben, sehen wir, dass der Zugriff auf die aktuelle Selektion auf diese Weise bereits funktioniert.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.centigrade.de\/wordpress\/wp-content\/uploads\/ListViewSelectionSample.png\" alt=\"Beispiel: Selektion einer ListBox\" \/><\/p>\n<h2>Selektion innerhalb einer TreeView<\/h2>\n<p>Das bisherige Beispiel bedient sich einer einfachen Liste.  Was aber, wenn unsere Daten  in einer zweistufigen Hierarchie ohne Gruppierung vorliegen? F\u00fcr diesen Fall erweitern wir unser ItemViewModel um ein umschlie\u00dfendes NodeViewModel:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> NodeViewModel\r\n{\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> NodeViewModel(<span class=\"kwrd\" style=\"color: #0000ff;\">string<\/span> name)\r\n  {\r\n    Name = name;\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">string<\/span> Name { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">internal<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ObservableCollection&lt;ItemViewModel&gt; Leafs { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n}\r\n<\/pre>\n<p>Wir erweitern das ViewModel unserer TreeView um eine zweite Selektionsm\u00f6glichkeit:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> TreeViewModel \r\n{\r\n  ObservableCollection&lt;NodeViewModel&gt; _nodes;\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ObservableCollection&lt;NodeViewModel&gt; Nodes\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> _nodes ?? (_nodes = CollectionCreator.CreateNodes(100, 5)); }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> NodeViewModel SelectedNode { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ItemViewModel SelectedLeaf { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> _selectedItem;\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> SelectedItem\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> _selectedItem; }\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>\r\n    {\r\n      SelectedLeaf = value <span class=\"kwrd\" style=\"color: #0000ff;\">as<\/span> ItemViewModel;\r\n      SelectedNode = value <span class=\"kwrd\" style=\"color: #0000ff;\">as<\/span> NodeViewModel;\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p>Beim Versuch, den selektierten Eintrag einfach per Bindung zu setzen, werden wir herb entt\u00e4uscht: das Property SelectedItem ist schreibgesch\u00fctzt und kann nicht ge\u00e4ndert werden. Durchsuchen wir das Internet nach diesem Problem, finden wir den Hinweis, das IsSelected-Property eines jeden TreeViewItems auf ein entsprechendes Property im jeweils zugeh\u00f6rigen NodeViewModel abzubilden. Hiervon ist jedoch aus mehreren Gr\u00fcnden abzuraten:<\/p>\n<ul>\n<li>Falls ein Command von dieser Selektion abh\u00e4ngig ist und sein CanExecute auf einen SelectionChangedEvent reagieren soll, m\u00fcssten alle Items dem ViewModel eine \u00c4nderung ihres Status mitteilen.\n  <\/li>\n<li>Die Selektionseigenschaft sollte schon konzeptuell nicht am Item h\u00e4ngen. Dies verbietet es mitunter, zwei Listen mit identischen Item-ViewModels zu f\u00fcllen, die Selektionen jedoch separat zu halten, wie dies beispielsweise in einer Implementierung des <a href=\"http:\/\/stackoverflow.com\/questions\/2852995\/whats-this-ui-pattern-called\">List-Builder Patterns<\/a> sinnvoll sein kann.\n  <\/li>\n<li>Wie man weiter unten bei der Mehrfachselektion selbst probieren kann, zerst\u00f6rt man entweder die <a href=\"http:\/\/msdn.microsoft.com\/de-de\/library\/windows\/apps\/xaml\/hh780657.aspx\">Virtualisierung<\/a> der Liste, was zu enormen Performance-Einbu\u00dfen f\u00fchren kann, oder man erh\u00e4lt unzuverl\u00e4ssige Ergebnisse. Beispielsweise werden bei einem SelectAll-Kommando die ersten 20 Items als selektiert gemeldet, obwohl die Liste 100 Items beinhaltet. Erst beim Herunterscrollen werden wirklich alle Items als selektiert gemeldet. Der Grund ist, dass die \u00fcbrigen 80 Items aufgrund der Virtualisierung ja noch gar nicht instanziiert worden sind, die Bindings an das IsSelected-Property demnach auch noch gar nicht etabliert worden sind.\n  <\/li>\n<\/ul>\n<p>Man <em>kann<\/em> durchaus diese M\u00f6glichkeit w\u00e4hlen, wenn man sich der Konsequenzen bewusst ist. Allerdings ist die Alternativm\u00f6glichkeit gar nicht so kompliziert: man implementiert ein Behavior, welches sich um die Synchronisation des Selektionsstatus der TreeViewItems k\u00fcmmert und zwischen TreeView und deren ViewModel vermittelt, statt auf ein direktes Binding zur\u00fcckzugreifen. <\/p>\n<p>Einziger Nachteil ist eine zus\u00e4tzliche  Abh\u00e4ngigkeit zur Assembly System.Windows.Interactivity.dll.<\/p>\n<p>Im XAML Code selbst werden f\u00fcr diesen Ansatz lediglich 3 zus\u00e4tzliche Zeilen ben\u00f6tigt:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">DockPanel<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DataContext<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{StaticResource ViewModel}\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">StackPanel<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DockPanel<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Dock<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Bottom\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Orientation<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Horizontal\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Height<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"25\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TextBlock<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Text<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedNode.Name}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TextBlock<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Text<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedLeaf.Name}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">StackPanel<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TreeView<\/span> <span class=\"attr\" style=\"color: #ff0000;\">ItemsSource<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding Nodes}\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TreeView.ItemTemplate<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">HierarchicalDataTemplate<\/span> <span class=\"attr\" style=\"color: #ff0000;\">ItemsSource<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding Leafs}\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n        <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TextBlock<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Text<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding Path=Name}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">HierarchicalDataTemplate<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>         \r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">TreeView.ItemTemplate<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #1BAFE0;\">i:Interaction.Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #1BAFE0;\">behaviors:SelectedItemBehavior<\/span> <span class=\"attr\" style=\"color: #ff0000;\">SelectedItem<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedItem}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #1BAFE0;\">i:Interaction.Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">TreeView<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">DockPanel<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n<\/pre>\n<p>Das Behavior selbst registriert lediglich das DependencyProperty SelectedItem und registriert sich selbst auf die \u00c4nderungen des selektierten Items der TreeView. Der entsprechende Event-Handler k\u00fcmmert sich darum, das selektierte Item zwischen TreeView und  TreeViewModel zu synchronisieren:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> SelectedItemBehavior : Behavior&lt;TreeView&gt;\r\n{\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">static<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">readonly<\/span> DependencyProperty SelectedItemProperty =\r\n    DependencyProperty.Register(<span class=\"str\" style=\"color: #a31515;\">\"SelectedItem\"<\/span>\r\n      , <span class=\"kwrd\" style=\"color: #0000ff;\">typeof<\/span>(<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span>)\r\n      , <span class=\"kwrd\" style=\"color: #0000ff;\">typeof<\/span>(SelectedItemBehavior)\r\n      , <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> FrameworkPropertyMetadata(<span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">static<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> item = e.NewValue <span class=\"kwrd\" style=\"color: #0000ff;\">as<\/span> TreeViewItem;\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (item != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n      item.SetValue(TreeViewItem.IsSelectedProperty, <span class=\"kwrd\" style=\"color: #0000ff;\">true<\/span>);\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> SelectedItem\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span>)GetValue(SelectedItemProperty); }\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span> { SetValue(SelectedItemProperty, value); }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">protected<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">override<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnAttached()\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">base<\/span>.OnAttached();\r\n    AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">protected<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">override<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnDetaching()\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">base<\/span>.OnDetaching();\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (AssociatedObject != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n    {\r\n      AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;\r\n    }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnTreeViewSelectedItemChanged(<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> sender, RoutedPropertyChangedEventArgs&lt;<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span>&gt; e)\r\n  {\r\n    SelectedItem = e.NewValue;\r\n  }\r\n}\r\n<\/pre>\n<p>Wir sehen, je nach Art des selektierten Items, den entsprechenden Text in der Fu\u00dfzeile:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.centigrade.de\/wordpress\/wp-content\/uploads\/TreeViewSelectionSample.png\" alt=\"Beispiel: Selektion eines TreeViews\" \/><\/p>\n<h2>Mehrfachselektion<\/h2>\n<p>Wir konnten bereits bei Einfachselektionen eine Redundanz bei der Haltung von Selektionsinformationen feststellen \u2013 dieser Nachteil f\u00e4llt bei Mehrfachselektionen noch st\u00e4rker ins Gewicht. Denn bei jeder Abfrage, ob eine Selektion vorliegt, muss der Selektionsstatus jedes einzelnen Items abgefragt werden, beispielsweise durch <em>Items.Any(x=>x.IsSelected)<\/em>. Sobald ein Kommando auf der Selektion ausgef\u00fchrt werden soll, ist wiederum eine Suche  nach denjenigen Items n\u00f6tig,die als selektiert markiert wurden, beispielsweise mittels <em>var selection = Items.Where(x=>x.IsSelected)<\/em>. Hinzu kommt die oben erw\u00e4hnte Problematik der Virtualisierung \u2013 das Selektions-Property wird entweder \u201eunzuverl\u00e4ssig\u201c, oder die TreeView b\u00fc\u00dft Performance ein.<\/p>\n<p>Auch hier hilf ein Behavior weiter. Es verbindet sich mit dem SelectionChanged-Event der ListBox und synchronisiert so die Liste der selektierten Items der ListBox  mit dem ViewModel. Dies sollte nat\u00fcrlich in beide Richtungen geschehen: wenn das ViewModel die Selektion \u00e4ndert, muss sich dies auch in der ListBox widerspiegeln und umgekehrt.<\/p>\n<p>Das hier gezeigte SynchronizeSelectedItems-Behavior stammt inklusive des WeakEventHandlers aus der Referenzimplementation von <a href=\"http:\/\/compositewpf.codeplex.com\/releases\/view\/55576\">Prism, MVVM RI<\/a>. Man mag sich fragen, warum diese Klasse nicht zum Lieferumfang der WPF geh\u00f6rt. Andererseits k\u00f6nnen wir froh sein, dass sie es nicht tut, sind doch in der aktuellen Version von Prism zwei Fehler enthalten. Die \u00c4nderungen der Selektion im ViewModel beeinflussen eben nicht die verbundene ListBox. Bei einem Update wird die aktuelle Selektion gel\u00f6scht und alle \u00c4nderungen der Selektionsliste neu eingespielt \u2013 was bei einem SelectAll zu langer Laufzeit und fehlerhaftem Verhalten f\u00fchrt. Au\u00dferdem wird beim Detach der Eventhandler ein zweites Mal an das SelectionChanged gebunden, statt den Handler abzumelden.<\/p>\n<p>Es folgt eine Version des SynchronizeSelectedItems-Behavior, die diese Problematik beseitigt:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> SynchronizeSelectedItems : Behavior&lt;ListBox&gt;\r\n{\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">static<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">readonly<\/span> DependencyProperty SelectionsProperty =\r\n    DependencyProperty.Register(\r\n      <span class=\"str\" style=\"color: #a31515;\">\"Selections\"<\/span>,\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">typeof<\/span>(IList),\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">typeof<\/span>(SynchronizeSelectedItems),\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> PropertyMetadata(<span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>, OnSelectionsPropertyChanged));\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">bool<\/span> _updating;\r\n  WeakEventHandler&lt;SynchronizeSelectedItems, <span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span>, NotifyCollectionChangedEventArgs&gt; _currentWeakHandler;\r\n \r\n  [SuppressMessage(<span class=\"str\" style=\"color: #a31515;\">\"Microsoft.Usage\"<\/span>, <span class=\"str\" style=\"color: #a31515;\">\"CA2227:CollectionPropertiesShouldBeReadOnly\"<\/span>, Justification = <span class=\"str\" style=\"color: #a31515;\">\"Dependency property\"<\/span>)]\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> IList Selections\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> (IList)GetValue(SelectionsProperty); }\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span> { SetValue(SelectionsProperty, value); }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">protected<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">override<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnAttached()\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">base<\/span>.OnAttached();\r\n    AssociatedObject.SelectionChanged += OnSelectedItemsChanged;\r\n    UpdateSelectedItems();\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">protected<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">override<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnDetaching()\r\n  {\r\n    AssociatedObject.SelectionChanged -= OnSelectedItemsChanged;\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">base<\/span>.OnDetaching();\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">static<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnSelectionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> behavior = d <span class=\"kwrd\" style=\"color: #0000ff;\">as<\/span> SynchronizeSelectedItems;\r\n \r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (behavior != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n    {\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (behavior._currentWeakHandler != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n      {\r\n        behavior._currentWeakHandler.Detach();\r\n        behavior._currentWeakHandler = <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>;\r\n      }\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (e.NewValue != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n      {\r\n        <span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> notifyCollectionChanged = e.NewValue <span class=\"kwrd\" style=\"color: #0000ff;\">as<\/span> INotifyCollectionChanged;\r\n        <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (notifyCollectionChanged != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n        {\r\n          behavior._currentWeakHandler =\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> WeakEventHandler&lt;SynchronizeSelectedItems, <span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span>, NotifyCollectionChangedEventArgs&gt;(\r\n              behavior,\r\n              (instance, sender, args) =&gt; instance.OnSelectionsCollectionChanged(sender, args),\r\n              listener =&gt; notifyCollectionChanged.CollectionChanged -= listener.OnEvent);\r\n              notifyCollectionChanged.CollectionChanged += behavior._currentWeakHandler.OnEvent;\r\n        }\r\n        behavior.UpdateSelectedItems();\r\n      }\r\n    }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnSelectedItemsChanged(<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> sender, SelectionChangedEventArgs e)\r\n  {\r\n    UpdateSelections(e);\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> UpdateSelections(SelectionChangedEventArgs e)\r\n  {\r\n    ExecuteIfNotUpdating(\r\n      () =&gt;\r\n        {\r\n          <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (Selections != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n          {\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">foreach<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> item <span class=\"kwrd\" style=\"color: #0000ff;\">in<\/span> e.AddedItems)\r\n            {\r\n              Selections.Add(item);\r\n            }\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">foreach<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> item <span class=\"kwrd\" style=\"color: #0000ff;\">in<\/span> e.RemovedItems)\r\n            {\r\n              Selections.Remove(item);\r\n            }\r\n          }\r\n        });\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> OnSelectionsCollectionChanged(<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> sender, NotifyCollectionChangedEventArgs e)\r\n  {\r\n    UpdateSelectedItems(e);\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> UpdateSelectedItems()\r\n  {\r\n    ExecuteIfNotUpdating(\r\n      () =&gt;\r\n        {\r\n          <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (AssociatedObject != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n          {\r\n            AssociatedObject.SelectedItems.Clear();\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">foreach<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> item <span class=\"kwrd\" style=\"color: #0000ff;\">in<\/span> Selections ?? <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span>[0])\r\n            {\r\n              AssociatedObject.SelectedItems.Add(item);\r\n            }\r\n          }\r\n        });\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> UpdateSelectedItems(NotifyCollectionChangedEventArgs e)\r\n  {\r\n    ExecuteIfNotUpdating(\r\n      () =&gt;\r\n        {\r\n          <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (AssociatedObject != <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n          {\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (e.Action == NotifyCollectionChangedAction.Reset)\r\n            {\r\n              AssociatedObject.SelectedItems.Clear();\r\n              <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span>;\r\n            }\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (e.Action == NotifyCollectionChangedAction.Add)\r\n            {\r\n              <span class=\"kwrd\" style=\"color: #0000ff;\">foreach<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> item <span class=\"kwrd\" style=\"color: #0000ff;\">in<\/span> e.NewItems)\r\n              {\r\n                AssociatedObject.SelectedItems.Add(item);\r\n              }\r\n            }\r\n            <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (e.Action == NotifyCollectionChangedAction.Remove)\r\n            {\r\n              <span class=\"kwrd\" style=\"color: #0000ff;\">foreach<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> item <span class=\"kwrd\" style=\"color: #0000ff;\">in<\/span> e.OldItems)\r\n              {\r\n                AssociatedObject.SelectedItems.Remove(item);\r\n              }\r\n            }\r\n          }\r\n        });\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> ExecuteIfNotUpdating(Action execute)\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (_updating) <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span>;\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">try<\/span>\r\n    {\r\n      _updating = <span class=\"kwrd\" style=\"color: #0000ff;\">true<\/span>;\r\n      execute();\r\n    }\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">finally<\/span>\r\n    {\r\n      _updating = <span class=\"kwrd\" style=\"color: #0000ff;\">false<\/span>;\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p>Damit haben wir eine ListView, bei der nicht nur die Selektion funktioniert, sondern auch z.B. das Filtern. Es folgt das zugeh\u00f6rige ViewModel:<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">class<\/span> MultiViewModel\r\n{\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> MultiViewModel()\r\n  {\r\n    SelectAllCommand = <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> DelegateCommand(SelectAll);\r\n    ClearSelectionCommand = <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> DelegateCommand(ClearSelection, CanClearSelection);\r\n    SelectedItems = <span class=\"kwrd\" style=\"color: #0000ff;\">new<\/span> ObservableCollection&lt;ItemViewModel&gt;();\r\n    SelectedItems.CollectionChanged += (sender, args) =&gt; ClearSelectionCommand.RaiseCanExecuteChanged();\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> DelegateCommand SelectAllCommand { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">private<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> DelegateCommand ClearSelectionCommand { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">private<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> SelectAll()\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (SelectedItems.Any())\r\n    {\r\n      SelectedItems.Clear();\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span>;\r\n    }\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">foreach<\/span> (<span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> entry <span class=\"kwrd\" style=\"color: #0000ff;\">in<\/span> Items)\r\n    {\r\n      SelectedItems.Add(entry);\r\n    }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">bool<\/span> CanClearSelection()\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> SelectedItems.Any();\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">void<\/span> ClearSelection()\r\n  {\r\n    SelectedItems.Clear();\r\n  }\r\n \r\n  ObservableCollection&lt;ItemViewModel&gt; _items;\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ObservableCollection&lt;ItemViewModel&gt; Items\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> _items ?? (_items = CollectionCreator.CreateItems(200)); }\r\n  }\r\n \r\n  ICollectionView _itemsViewSource;\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ICollectionView ItemsViewSource\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>\r\n    {\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (_itemsViewSource == <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>)\r\n      {\r\n        _itemsViewSource = CollectionViewSource.GetDefaultView(Items);\r\n        _itemsViewSource.Filter = ItemFilter;\r\n      }\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> _itemsViewSource;\r\n    }\r\n  }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> ObservableCollection&lt;ItemViewModel&gt; SelectedItems { <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span>; <span class=\"kwrd\" style=\"color: #0000ff;\">private<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>; }\r\n \r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">string<\/span> _search;\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">public<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">string<\/span> SearchItem\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">get<\/span> { <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> _search; }\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">set<\/span>\r\n    {\r\n      _search = value;\r\n      ItemsViewSource.Refresh();\r\n    }\r\n  }\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">bool<\/span> ItemFilter(<span class=\"kwrd\" style=\"color: #0000ff;\">object<\/span> o)\r\n  {\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">if<\/span> (_search == <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span>) <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">true<\/span>;\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">var<\/span> resource = (ItemViewModel)o;\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">return<\/span> resource.Name == <span class=\"kwrd\" style=\"color: #0000ff;\">null<\/span> || resource.Name.ToLower().Contains(_search.ToLower());\r\n  }\r\n}\r\n<\/pre>\n<p>Die View dazu visualisiert die Selektion in einer eigenen ListBox. Zu beachten ist, dass ausgefilterte Eintr\u00e4ge aus der Auswahl verschwinden.<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">DockPanel<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DataContext<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{StaticResource ViewModel}\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Grid<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DockPanel<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Dock<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Top\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Grid.ColumnDefinitions<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ColumnDefinition<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Width<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Auto\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ColumnDefinition<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">Grid.ColumnDefinitions<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TextBlock<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Text<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Filter:\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">TextBox<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Grid<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Column<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"1\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Text<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SearchItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">Grid<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">StackPanel<\/span> <span class=\"attr\" style=\"color: #ff0000;\">DockPanel<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Dock<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Bottom\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Orientation<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Horizontal\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Button<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Content<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Clear\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Command<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding ClearSelectionCommand}\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Width<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"60\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Button<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Content<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Select all\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Command<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectAllCommand}\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Width<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"60\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">StackPanel<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Grid<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Grid.ColumnDefinitions<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ColumnDefinition<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ColumnDefinition<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">Grid.ColumnDefinitions<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ListView<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Grid<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Column<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"0\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">ItemsSource<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding ItemsViewSource}\"<\/span> \r\n      <span class=\"attr\" style=\"color: #ff0000;\">DisplayMemberPath<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Name\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">HorizontalAlignment<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Stretch\"<\/span>\r\n      <span class=\"attr\" style=\"color: #ff0000;\">SelectionMode<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Multiple\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #1BAFE0;\">i:Interaction.Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n        <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #1BAFE0;\">Behaviors:SynchronizeSelectedItems<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Selections<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedItems}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #1BAFE0;\">i:Interaction.Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">ListView<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">ListBox<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Grid<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Column<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"1\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">ItemsSource<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedItems}\"<\/span> \r\n      <span class=\"attr\" style=\"color: #ff0000;\">DisplayMemberPath<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Name\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">HorizontalAlignment<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Stretch\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">Grid<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">DockPanel<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n<\/pre>\n<p>Damit ergibt sich folgende Ausgabe:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.centigrade.de\/wordpress\/wp-content\/uploads\/MultiSelectionSample.png\" alt=\"Beispiel: Multiselektion einer ListBox\" \/><\/p>\n<h2>Selektion in der SurfaceListBox<\/h2>\n<p>Nun sind eine ListBox oder eine ListView im Rahmen des Windows 8-Hypes  nicht wirklich elegant per Touch-Bedienung zu steuern. Von iOS bekannte Techniken, wie beispielsweise \u201eSwipe to scroll\u201c, sind in diesen Klassen gar nicht vorgesehen.<\/p>\n<p>Mit dem <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/details.aspx?id=26716\">Surface SDK<\/a> von Microsoft wird eine SurfaceListBox mitgeliefert, die genau diese Funktionalit\u00e4t bereitstellt. Und da sie von ListBox abgeleitet ist, kann das oben genannte Behavior ebenso eingesetzt werden. Hier zeigt sich wieder, welch enormen Vorteil Behaviors gegen\u00fcber einer simplen Vererbung haben. H\u00e4tten wir von ListBox abgeleitet, um die Selektionssynchronisation zu realisieren, h\u00e4tten wir die SurfaceListBox nur mit Hilfe einer weiteren Vererbung (also letztendlich mit Hilfe von Code-Verdopplung) erweitern k\u00f6nnen.<\/p>\n<p>Nach Deklaration des entsprechenden Surface-Namespaces und der \u00c4nderung der ListView-Definition aus dem MultiSelect-Beispiel haben wir eine Windows 8-\u00e4hnliche Ansicht.<\/p>\n<pre class=\"csharpcode\" style='font-size: small;\r\n\tcolor: black;\r\n\tfont-family: Consolas, \"Courier New\", Courier, Monospace;\r\n\tbackground-color: #ffffff;'>\r\n<span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">UserControl<\/span> <span class=\"attr\" style=\"color: #ff0000;\">x:Class<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Selections.Views.SurfaceView\"<\/span>\r\n             ...\r\n             <span class=\"attr\" style=\"color: #ff0000;\">xmlns:i<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"http:\/\/schemas.microsoft.com\/expression\/2010\/interactivity\"<\/span>\r\n             <span class=\"attr\" style=\"color: #ff0000;\">xmlns:s<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"clr-namespace:Microsoft.Surface.Presentation.Controls;assembly=Microsoft.Surface.Presentation\"<\/span>\r\n             <span class=\"attr\" style=\"color: #ff0000;\">xmlns:Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"clr-namespace:Selections.Behaviors\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">mc:Ignorable<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"d\"<\/span> \r\n             ...\r\n \r\n  <span class=\"attr\" style=\"color: #ff0000;\">&lt;<\/span><span class=\"attr\" style=\"color: #ff0000;\">s:SurfaceListBox<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Grid<\/span>.<span class=\"attr\" style=\"color: #ff0000;\">Column<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"0\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">ItemsSource<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding ItemsViewSource}\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Background<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Brown\"<\/span>\r\n    <span class=\"attr\" style=\"color: #ff0000;\">DisplayMemberPath<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Name\"<\/span> <span class=\"attr\" style=\"color: #ff0000;\">HorizontalAlignment<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Stretch\"<\/span>\r\n    <span class=\"attr\" style=\"color: #ff0000;\">SelectionMode<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"Multiple\"<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">i:Interaction.Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n      <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;<\/span><span class=\"html\" style=\"color: #800000;\">Behaviors:SynchronizeSelectedItems<\/span> <span class=\"attr\" style=\"color: #ff0000;\">Selections<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">=\"{Binding SelectedItems}\"<\/span> <span class=\"kwrd\" style=\"color: #0000ff;\">\/&gt;<\/span>\r\n    <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">i:Interaction.Behaviors<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n  <span class=\"kwrd\" style=\"color: #0000ff;\">&lt;\/<\/span><span class=\"html\" style=\"color: #800000;\">s:SurfaceListBox<\/span><span class=\"kwrd\" style=\"color: #0000ff;\">&gt;<\/span>\r\n \r\n  ...\r\n<\/pre>\n<p>Aber Vorsicht: das Surface SDK muss hierf\u00fcr installiert werden. Ein einfaches Einbinden der Assemblies reicht nicht aus, die Elemente der ListBox werden mitunter nicht angezeigt.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.centigrade.de\/wordpress\/wp-content\/uploads\/SurfaceSelectionSample.png\" alt=\"Beispiel: Selektion einer Surface-ListBox\" \/><\/p>\n<h2>Fazit<\/h2>\n<p>Auch wenn sich auf den ersten Blick das Abbilden der Selektionseigenschaft auf die einzelnen Eintr\u00e4ge anbietet, sollte sie aus mehr als einem Grund heraus nicht am Item h\u00e4ngen.<\/p>\n<p>Die gezeigte Herangehensweise \u00fcber ein Behavior erm\u00f6glicht es, die Ausf\u00fchrbarkeit von Commands von einer ListBox-Auswahl abh\u00e4ngig zu machen, ohne bei jedem Aufruf von CanExecute die Selektion \u00fcberpr\u00fcfen zu m\u00fcssen. Da die aktuelle Auswahl der ListBox als Eigenschaft des ViewModels bereitgestellt wird, ist dar\u00fcber hinaus die Bindung an diese Selektion m\u00f6glich.<\/p>\n<p>Das komplette Beispiel kann <a href=\"https:\/\/www.centigrade.de\/wordpress\/wp-content\/uploads\/Selections.zip\">hier<\/a> geladen werden.<\/p>\n","protected":false},"author":33,"featured_media":0,"template":"","tags":[251],"class_list":["post-3674","blog","type-blog","status-publish","hentry","tag-selektion"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/blog\/3674","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/blog"}],"about":[{"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/users\/33"}],"version-history":[{"count":0,"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/blog\/3674\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/media?parent=3674"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.centigrade.de\/de\/wp-json\/wp\/v2\/tags?post=3674"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}