Metainformationen zur Seite
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
start:visualstudio2017:programmieren:dotnetgrundlagen:tipps_tricks [2022/09/22 19:07] wikiadmin [Working with Checkboxes in the WPF TreeView / Arbeiten mit Kontrollkästchen in der WPF TreeView] |
start:visualstudio2017:programmieren:dotnetgrundlagen:tipps_tricks [2022/09/23 07:04] (aktuell) wikiadmin [Working with Checkboxes in the WPF TreeView / Arbeiten mit Kontrollkästchen in der WPF TreeView] |
||
---|---|---|---|
Zeile 227: | Zeile 227: | ||
{{: | {{: | ||
+ | |||
+ | Now let’s see what the functional requirements are: | ||
+ | |||
+ | - Requirement : Each item in the tree must display a checkbox that displays the text and check state of an underlying data object. | ||
+ | - Requirement : Upon an item being checked or unchecked, all of its child items should be checked or unchecked, respectively. | ||
+ | - Requirement : If an item’s descendants do not all have the same check state, that item’s check state must be ‘indeterminate.’ | ||
+ | - Requirement : Navigating from item to item should require only one press of an arrow key. | ||
+ | - Requirement : Pressing the Spacebar or Enter keys should toggle the check state of the selected item. | ||
+ | - Requirement : Clicking on an item’s checkbox should toggle its check state, but not select the item. | ||
+ | - Requirement : Clicking on an item’s display text should select the item, but not toggle its check state. | ||
+ | - Requirement : All items in the tree should be in the expanded state by default. | ||
+ | |||
+ | I suggest you copy those requirements and paste them into your favorite text editor, such as Notepad, because we will reference them throughout the rest of the article by number. | ||
+ | |||
+ | === Putting the Smarts in a ViewModel === | ||
+ | As explained in my ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article, the TreeView was practically designed to be used in conjunction with a ViewModel. This article takes that idea further, and shows how we can use a ViewModel to encapsulate application-specific logic related to the check state of items in the tree. In this article, we will examine my FooViewModel class, which the following interface describes: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | interface IFooViewModel : INotifyPropertyChanged | ||
+ | { | ||
+ | List< | ||
+ | bool? IsChecked { get; set; } | ||
+ | bool IsInitiallySelected { get; } | ||
+ | string Name { get; } | ||
+ | } | ||
+ | </ | ||
+ | The most interesting aspect of this ViewModel class is the logic behind the IsChecked property. This logic satisfies Requirements 2 and 3, seen previously. The FooViewModel’s IsChecked logic is below: | ||
+ | <code C# [enable_line_numbers=" | ||
+ | /// < | ||
+ | /// Gets/sets the state of the associated UI toggle (ex. CheckBox). | ||
+ | /// The return value is calculated based on the check state of all | ||
+ | /// child FooViewModels. | ||
+ | /// will set all children to the same check state, and setting it | ||
+ | /// to any value will cause the parent to verify its check state. | ||
+ | /// </ | ||
+ | public bool? IsChecked | ||
+ | { | ||
+ | get { return _isChecked; } | ||
+ | set { this.SetIsChecked(value, | ||
+ | } | ||
+ | |||
+ | void SetIsChecked(bool? | ||
+ | { | ||
+ | if (value == _isChecked) | ||
+ | return; | ||
+ | |||
+ | _isChecked = value; | ||
+ | |||
+ | if (updateChildren && _isChecked.HasValue) | ||
+ | this.Children.ForEach(c => c.SetIsChecked(_isChecked, | ||
+ | |||
+ | if (updateParent && _parent != null) | ||
+ | _parent.VerifyCheckState(); | ||
+ | |||
+ | this.OnPropertyChanged(" | ||
+ | } | ||
+ | |||
+ | void VerifyCheckState() | ||
+ | { | ||
+ | bool? state = null; | ||
+ | for (int i = 0; i < this.Children.Count; | ||
+ | { | ||
+ | bool? current = this.Children[i].IsChecked; | ||
+ | if (i == 0) | ||
+ | { | ||
+ | state = current; | ||
+ | } | ||
+ | else if (state != current) | ||
+ | { | ||
+ | state = null; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | this.SetIsChecked(state, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This strategy is specific to the functional requirements I imposed upon myself. If you have different rules regarding how and when items should update their check state, simply adjust the logic in those methods to suit your needs. | ||
+ | |||
+ | === TreeView Configuration === | ||
+ | Now it is time to see how the TreeView is able to display checkboxes and bind to the ViewModel. This is entirely accomplished in XAML. The TreeView declaration is actually quite simple, as seen below: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | < | ||
+ | x: | ||
+ | ItemContainerStyle=" | ||
+ | ItemsSource=" | ||
+ | ItemTemplate=" | ||
+ | /> | ||
+ | </code | ||
+ | The TreeView’s ItemsSource property is implicitly bound to its DataContext, | ||
+ | |||
+ | TreeViewItem is a container of visual elements generated by the ItemTemplate. In this demo, we assign the following HierarchicalDataTemplate to the tree's ItemTemplate property: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | < | ||
+ | x: | ||
+ | ItemsSource=" | ||
+ | > | ||
+ | < | ||
+ | <!-- These elements are bound to a FooViewModel object. --> | ||
+ | < | ||
+ | Focusable=" | ||
+ | IsChecked=" | ||
+ | VerticalAlignment=" | ||
+ | /> | ||
+ | < | ||
+ | Content=" | ||
+ | Margin=" | ||
+ | /> | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | There are several points of interest in that template. The template includes a CheckBox whose Focusable property is set to false. This prevents the CheckBox from ever receiving input focus, which assists in meeting Requirement 4. You might be wondering how we will be able to satisfy Requirement 5 if the CheckBox never has input focus. We will address that issue later in this article, when we examine how to attach the behavior of a ToggleButton to a TreeViewItem. | ||
+ | |||
+ | The CheckBox’s IsChecked property is bound to the IsChecked property of a FooViewModel object, but notice that its Content property is not set to anything. Instead, there is a ContentPresenter directly next to it, whose Content is bound to the Name property of a FooViewModel object. By default, clicking anywhere on a CheckBox causes it to toggle its check state. By using a separate ContentPresenter, | ||
+ | |||
+ | We will examine the TreeView’s ItemContainerStyle in the next section. | ||
+ | |||
+ | === Turning a TreeViewItem into a ToggleButton === | ||
+ | In the previous section, we quickly considered an interesting question. If the CheckBox in the TreeViewItem has its Focusable property set to false, how can it toggle its check state in response to the Spacebar or Enter key? Since an element only receives keystrokes if it has keyboard focus, it seems impossible for Requirement 5 to be satisfied. Keep in mind; we had to set the CheckBox’s Focusable property to false so that navigating from item to item in the tree does not require multiple keystrokes. | ||
+ | |||
+ | This is a tricky problem: we cannot let the CheckBox ever have input focus because it negatively affects keyboard navigation, yet, when its containing item is selected, it must somehow toggle its check state in response to certain keystrokes. These seem to be mutually exclusive requirements. When I hit this brick wall, I decided to seek geek from the WPF Disciples, and started this thread. Not to my surprise, Dr. WPF had already encountered this type of problem and devised a brilliant-approaching-genius solution that was easy to plug into my application. The good Doctor sent me the code for a VirtualToggleButton class, and was kind enough to allow me to publish it in this article. | ||
+ | |||
+ | The Doctor’s solution uses what John Gossman refers to as “attached behavior.” The idea is that you set an attached property on an element so that you can gain access to the element from the class that exposes the attached property. Once that class has access to the element, it can hook events on it and, in response to those events firing, make the element do things that it normally would not do. It is a very convenient alternative to creating and using subclasses, and is very XAML-friendly. | ||
+ | |||
+ | In this article, we see how to give a TreeViewItem an attached IsChecked property that toggles when the user presses the Spacebar or Enter key. That attached IsChecked property binds to the IsChecked property of a FooViewModel object, which is also bound to the IsChecked property of the CheckBox in the TreeViewItem. This solution gives the appearance that a CheckBox is toggling its check state in response to the Spacebar or Enter key, but in reality, its IsChecked property updates in response to a TreeViewItem pushing a new value to the ViewModel’s IsChecked property via data binding. | ||
+ | |||
+ | Before going any further, I should point out that I fully recognize that this is crazy. The fact that this is the cleanest way to implement a TreeView of checkboxes in WPF v3.5 indicates, to me, that Microsoft needs to simplify this aspect of the platform. However, until they do, this is probably the best way to implement the feature. | ||
+ | |||
+ | In this demo, we do not make use of all features in Dr. WPF’s VirtualToggleButton class. It has support for several things that we do not need, such as handling mouse clicks and providing tri-state checkboxes. We only need to make use of its support for the attached IsVirtualToggleButton and IsChecked properties and the keyboard interaction behavior it provides. | ||
+ | |||
+ | Here is the property-changed callback method for the attached IsVirtualToggleButton property, which is what enables this class to gain access to TreeViewItems in the tree: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | /// < | ||
+ | /// Handles changes to the IsVirtualToggleButton property. | ||
+ | /// </ | ||
+ | private static void OnIsVirtualToggleButtonChanged( | ||
+ | DependencyObject d, DependencyPropertyChangedEventArgs e) | ||
+ | { | ||
+ | IInputElement element = d as IInputElement; | ||
+ | if (element != null) | ||
+ | { | ||
+ | if ((bool)e.NewValue) | ||
+ | { | ||
+ | element.MouseLeftButtonDown += OnMouseLeftButtonDown; | ||
+ | element.KeyDown += OnKeyDown; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | element.MouseLeftButtonDown -= OnMouseLeftButtonDown; | ||
+ | element.KeyDown -= OnKeyDown; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | When a TreeViewItem raises its KeyDown event, this logic executes: | ||
+ | <code C# [enable_line_numbers=" | ||
+ | private static void OnKeyDown(object sender, KeyEventArgs e) | ||
+ | { | ||
+ | if (e.OriginalSource == sender) | ||
+ | { | ||
+ | if (e.Key == Key.Space) | ||
+ | { | ||
+ | // ignore alt+space which invokes the system menu | ||
+ | if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) | ||
+ | return; | ||
+ | |||
+ | UpdateIsChecked(sender as DependencyObject); | ||
+ | e.Handled = true; | ||
+ | } | ||
+ | else if (e.Key == Key.Enter && | ||
+ | | ||
+ | | ||
+ | { | ||
+ | UpdateIsChecked(sender as DependencyObject); | ||
+ | e.Handled = true; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private static void UpdateIsChecked(DependencyObject d) | ||
+ | { | ||
+ | Nullable< | ||
+ | if (isChecked == true) | ||
+ | { | ||
+ | SetIsChecked(d, | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | SetIsChecked(d, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | The UpdateIsChecked method sets the attached IsChecked property on an element, which is a TreeViewItem in this demo. Setting an attached property on a TreeViewItem has no effect by itself. In order to have the application use that property value, it must be bound to something. In this application, | ||
+ | <code XML [enable_line_numbers=" | ||
+ | <Style x: | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | This piece ties the entire puzzle together. Note that the attached KeyboardNavigation.AcceptsReturn property is set to true on each TreeViewItem so that the VirtualToggleButton will toggle its check state in response to the Enter key. The first Setter in the Style, which sets the initial value of each item's IsExpanded property to true, ensures that Requirement 8 is met. | ||
+ | |||
+ | === CheckBox Bug in Aero Theme === | ||
+ | I must point out one strange, and disappointing, | ||
+ | |||
+ | {{: | ||
</ | </ | ||
Zeile 247: | Zeile 463: | ||
{{: | {{: | ||
+ | Schauen wir uns nun die funktionalen Anforderungen an: | ||
+ | - Anforderung : Jedes Element in der Baumstruktur muss ein Kontrollkästchen enthalten, das den Text und den Kontrollstatus eines zugrunde liegenden Datenobjekts anzeigt. | ||
+ | - Anforderung : Wenn ein Element angekreuzt oder nicht angekreuzt wird, sollten alle seine untergeordneten Elemente angekreuzt bzw. nicht angekreuzt werden. | ||
+ | - Anforderung : Wenn die Nachkommen eines Eintrags nicht alle den gleichen Prüfstatus haben, muss der Prüfstatus dieses Eintrags " | ||
+ | - Anforderung : Das Navigieren von Element zu Element sollte nur einen einzigen Druck auf eine Pfeiltaste erfordern. | ||
+ | - Anforderung : Das Drücken der Leertaste oder der Eingabetaste sollte den Prüfstatus des ausgewählten Eintrags umschalten. | ||
+ | - Anforderung : Ein Klick auf das Kontrollkästchen eines Eintrags soll den Kontrollstatus umschalten, aber den Eintrag nicht auswählen. | ||
+ | - Anforderung : Das Anklicken des Anzeigetextes eines Eintrags soll den Eintrag auswählen, aber nicht seinen Markierungsstatus umschalten. | ||
+ | - Anforderung : Alle Elemente in der Baumstruktur sollten sich standardmäßig im erweiterten Zustand befinden. | ||
+ | Ich schlage vor, Sie kopieren diese Anforderungen und fügen sie in Ihren bevorzugten Texteditor ein, z. B. in Notepad, da wir sie im weiteren Verlauf des Artikels nummerisch referenzieren werden. | ||
+ | |||
+ | === Die Intelligenz in ein ViewModel packen === | ||
+ | Wie in meinem Artikel ' | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | interface IFooViewModel : INotifyPropertyChanged | ||
+ | { | ||
+ | List< | ||
+ | bool? IsChecked { get; set; } | ||
+ | bool IsInitiallySelected { get; } | ||
+ | string Name { get; } | ||
+ | } | ||
+ | </ | ||
+ | Der interessanteste Aspekt dieser ViewModel-Klasse ist die Logik hinter der Eigenschaft IsChecked. Diese Logik erfüllt die Anforderungen 2 und 3, die zuvor gesehen wurden. Die IsChecked-Logik des FooViewModel ist unten dargestellt: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | ///Ruft den Zustand des zugehörigen UI-Toggles (z.B. CheckBox) ab bzw. setzt ihn. | ||
+ | ///Der Rückgabewert wird auf der Grundlage des Prüfstatus aller untergeordneten | ||
+ | /// | ||
+ | ///erhalten alle untergeordneten Modelle den gleichen Prüfstatus, | ||
+ | ///und wenn sie auf einen beliebigen Wert gesetzt wird, überprüft das übergeordnete Modell seinen Prüfstatus. | ||
+ | public bool? IsChecked | ||
+ | { | ||
+ | get { return _isChecked; } | ||
+ | set { this.SetIsChecked(value, | ||
+ | } | ||
+ | |||
+ | void SetIsChecked(bool? | ||
+ | { | ||
+ | if (value == _isChecked) | ||
+ | return; | ||
+ | |||
+ | _isChecked = value; | ||
+ | |||
+ | if (updateChildren && _isChecked.HasValue) | ||
+ | this.Children.ForEach(c => c.SetIsChecked(_isChecked, | ||
+ | |||
+ | if (updateParent && _parent != null) | ||
+ | _parent.VerifyCheckState(); | ||
+ | |||
+ | this.OnPropertyChanged(" | ||
+ | } | ||
+ | |||
+ | void VerifyCheckState() | ||
+ | { | ||
+ | bool? state = null; | ||
+ | for (int i = 0; i < this.Children.Count; | ||
+ | { | ||
+ | bool? current = this.Children[i].IsChecked; | ||
+ | if (i == 0) | ||
+ | { | ||
+ | state = current; | ||
+ | } | ||
+ | else if (state != current) | ||
+ | { | ||
+ | state = null; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | this.SetIsChecked(state, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Diese Strategie ist spezifisch für die funktionalen Anforderungen, | ||
+ | |||
+ | === TreeView Konfiguration === | ||
+ | Nun ist es an der Zeit zu sehen, wie die TreeView in der Lage ist, Kontrollkästchen anzuzeigen und an das ViewModel zu binden. Dies wird vollständig in XAML realisiert. Die TreeView-Deklaration ist eigentlich recht einfach, wie unten zu sehen ist: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | < | ||
+ | x: | ||
+ | ItemContainerStyle=" | ||
+ | ItemsSource=" | ||
+ | ItemTemplate=" | ||
+ | /> | ||
+ | </code | ||
+ | The TreeView’s ItemsSource property is implicitly bound to its DataContext, | ||
+ | |||
+ | TreeViewItem is a container of visual elements generated by the ItemTemplate. In this demo, we assign the following HierarchicalDataTemplate to the tree's ItemTemplate property: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | < | ||
+ | x: | ||
+ | ItemsSource=" | ||
+ | > | ||
+ | < | ||
+ | <!-- These elements are bound to a FooViewModel object. --> | ||
+ | < | ||
+ | Focusable=" | ||
+ | IsChecked=" | ||
+ | VerticalAlignment=" | ||
+ | /> | ||
+ | < | ||
+ | Content=" | ||
+ | Margin=" | ||
+ | /> | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | In dieser Vorlage gibt es mehrere interessante Punkte. Die Vorlage enthält eine CheckBox, deren Eigenschaft Focusable auf false gesetzt ist. Dadurch wird verhindert, dass die CheckBox jemals den Eingabefokus erhält, was zur Erfüllung von Anforderung 4 beiträgt. Sie fragen sich vielleicht, wie wir die Anforderung 5 erfüllen können, wenn die CheckBox nie den Eingabefokus erhält. Wir werden dieses Problem später in diesem Artikel behandeln, wenn wir untersuchen, | ||
+ | |||
+ | Die IsChecked-Eigenschaft der CheckBox ist an die IsChecked-Eigenschaft eines FooViewModel-Objekts gebunden, aber beachten Sie, dass die Content-Eigenschaft nicht auf irgendetwas gesetzt ist. Stattdessen befindet sich direkt daneben ein ContentPresenter, | ||
+ | |||
+ | Wir werden den ItemContainerStyle des TreeViews im nächsten Abschnitt untersuchen. | ||
+ | |||
+ | === Einen TreeViewItem in einen ToggleButton verwandeln === | ||
+ | Im vorigen Abschnitt haben wir uns schnell eine interessante Frage gestellt. Wenn die CheckBox im TreeViewItem ihre Focusable-Eigenschaft auf false gesetzt hat, wie kann sie dann als Reaktion auf die Leertaste oder die Eingabetaste ihren Prüfstatus umschalten? Da ein Element nur dann Tastendrücke empfängt, wenn es den Tastaturfokus hat, scheint es unmöglich zu sein, die Anforderung 5 zu erfüllen. Denken Sie daran, dass wir die Eigenschaft Focusable der CheckBox auf false setzen mussten, damit die Navigation von Element zu Element in der Baumstruktur nicht mehrere Tastendrücke erfordert. | ||
+ | |||
+ | Dies ist ein kniffliges Problem: Wir können nicht zulassen, dass die CheckBox jemals den Eingabefokus hat, da dies die Navigation über die Tastatur negativ beeinflusst, | ||
+ | |||
+ | Die Lösung des Doktors verwendet das, was John Gossman als " | ||
+ | |||
+ | In diesem Artikel sehen wir, wie man einem TreeViewItem eine angehängte IsChecked-Eigenschaft gibt, die umschaltet, wenn der Benutzer die Leertaste oder die Eingabetaste drückt. Diese angehängte IsChecked-Eigenschaft ist an die IsChecked-Eigenschaft eines FooViewModel-Objekts gebunden, das wiederum an die IsChecked-Eigenschaft der CheckBox im TreeViewItem gebunden ist. Diese Lösung erweckt den Anschein, dass eine CheckBox ihren Prüfstatus als Reaktion auf die Leertaste oder die Eingabetaste umschaltet, aber in Wirklichkeit wird ihre IsChecked-Eigenschaft als Reaktion auf ein TreeViewItem aktualisiert, | ||
+ | |||
+ | Bevor ich fortfahre, sollte ich darauf hinweisen, dass mir völlig klar ist, dass dies verrückt ist. Die Tatsache, dass dies der sauberste Weg ist, eine TreeView von Kontrollkästchen in WPF v3.5 zu implementieren, | ||
+ | |||
+ | In dieser Demo machen wir nicht von allen Funktionen der VirtualToggleButton-Klasse von Dr. WPF Gebrauch. Sie bietet Unterstützung für mehrere Dinge, die wir nicht benötigen, wie z. B. die Verarbeitung von Mausklicks und die Bereitstellung von Checkboxen mit drei Zuständen. Wir brauchen nur die Unterstützung für die angehängten Eigenschaften IsVirtualToggleButton und IsChecked sowie das Verhalten bei der Tastaturinteraktion, | ||
+ | |||
+ | Hier ist die Callback-Methode für die angehängte IsVirtualToggleButton-Eigenschaft, | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | /// < | ||
+ | /// Behandelt Änderungen an der Eigenschaft IsVirtualToggleButton. | ||
+ | /// </ | ||
+ | private static void OnIsVirtualToggleButtonChanged( | ||
+ | DependencyObject d, DependencyPropertyChangedEventArgs e) | ||
+ | { | ||
+ | IInputElement element = d as IInputElement; | ||
+ | if (element != null) | ||
+ | { | ||
+ | if ((bool)e.NewValue) | ||
+ | { | ||
+ | element.MouseLeftButtonDown += OnMouseLeftButtonDown; | ||
+ | element.KeyDown += OnKeyDown; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | element.MouseLeftButtonDown -= OnMouseLeftButtonDown; | ||
+ | element.KeyDown -= OnKeyDown; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Wenn ein TreeViewItem sein KeyDown Ereignis auslöst, wird diese Logik ausgeführt: | ||
+ | |||
+ | <code C# [enable_line_numbers=" | ||
+ | private static void OnKeyDown(object sender, KeyEventArgs e) | ||
+ | { | ||
+ | if (e.OriginalSource == sender) | ||
+ | { | ||
+ | if (e.Key == Key.Space) | ||
+ | { | ||
+ | // ignore alt+space which invokes the system menu | ||
+ | if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) | ||
+ | return; | ||
+ | |||
+ | UpdateIsChecked(sender as DependencyObject); | ||
+ | e.Handled = true; | ||
+ | } | ||
+ | else if (e.Key == Key.Enter && | ||
+ | | ||
+ | | ||
+ | { | ||
+ | UpdateIsChecked(sender as DependencyObject); | ||
+ | e.Handled = true; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private static void UpdateIsChecked(DependencyObject d) | ||
+ | { | ||
+ | Nullable< | ||
+ | if (isChecked == true) | ||
+ | { | ||
+ | SetIsChecked(d, | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | SetIsChecked(d, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Die UpdateIsChecked-Methode setzt die angehängte IsChecked-Eigenschaft auf ein Element, das in dieser Demo ein TreeViewItem ist. Das Setzen einer angehängten Eigenschaft auf einem TreeViewItem hat selbst keinen Effekt. Damit die Anwendung diesen Eigenschaftswert verwenden kann, muss er an etwas gebunden sein. In dieser Anwendung ist sie an die IsChecked Eigenschaft eines FooViewModel Objekts gebunden. Der folgende Style wird der ItemContainerStyle Eigenschaft des TreeViews zugewiesen. Er bindet ein TreeViewItem an ein FooViewModel Objekt und fügt das virtuelle ToggleButton Verhalten hinzu, das wir gerade untersucht haben. | ||
+ | |||
+ | <code XML [enable_line_numbers=" | ||
+ | <Style x: | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | <Setter Property=" | ||
+ | </ | ||
+ | </ | ||
+ | Dieses Teil fügt das gesamte Puzzle zusammen. Beachten Sie, dass die angehängte Eigenschaft KeyboardNavigation.AcceptsReturn für jedes TreeViewItem auf true gesetzt ist, so dass der VirtualToggleButton seinen Prüfstatus als Reaktion auf die Enter-Taste umschaltet. Der erste Setter im Style, der den Anfangswert der IsExpanded-Eigenschaft jedes Elements auf true setzt, stellt sicher, dass Anforderung 8 erfüllt ist. | ||
+ | |||
+ | === CheckBox-Fehler im Aero-Design === | ||
+ | Ich muss auf ein seltsames und enttäuschendes Problem hinweisen. Das Aero-Thema für das CheckBox-Steuerelement von WPF hat ein Problem in .NET 3.5. Wenn es vom Zustand " | ||
+ | |||
+ | {{: | ||
</ | </ | ||
</ | </ | ||
+ | [[https:// | ||