Ja, es gibt den traditionellen Ansatz, den Sie beschreiben, wo Sie Funktionen programmatisch aktivieren/deaktivieren, aber WPF eröffnet auch einige neue Möglichkeiten, die in WinForms und älteren Technologien wirklich nicht möglich waren.
werde ich vier WPF-spezifische Weise erklären, dies zu tun:
Sie heimlich und automatisch einen Fensterinhalt mit einem Bild ersetzen seines Inhalts ein Rechteck mit einem VisualBrush verwendet wird, wodurch effektiv zu deaktivieren es. Für den Benutzer sieht es so aus, als ob das Fenster unverändert ist, aber der tatsächliche Inhalt wird unter dem Bild angezeigt, so dass Sie es für das Testen von Treffern verwenden und ausgewählte Ereignisse sogar dorthin weiterleiten können.
Sie können ein MergedDictionary zum ResourceDictionary Ihres Fensters hinzufügen, das bewirkt, dass alle TextBoxen zu TextBlocks werden, alle Buttons deaktiviert werden usw., außer dass explizit benutzerdefinierte angefügte Eigenschaften überschrieben werden. Anstatt alle Benutzeroberflächen selektiv durchzuschalten oder zu deaktivieren, fügen Sie einfach ein Objekt aus der MergedDictionaries-Auflistung hinzu oder entfernen es.
Sie können den InputManager verwenden, um echte Mausereignisse in bestimmten Teilen eines deaktivierten Fensters programmgesteuert zu generieren und zu verarbeiten. Mausereignisse, die keinen Hit-Test durchführen, werden nicht genehmigt."
Verwendung der Datenbindung und Stile aktivieren/deaktivieren einzelne Steuerelemente, statt durch sie Iterieren
Details zu
Für diese Lösung Fenster mit dem Bild der Fenster zu ersetzen, wiederholen Sie Ihre App Windows und ersetzen Sie jeden Inhalt durch ein Grid, das den ursprünglichen Inhalt und ein Rechteck enthält, wie folgt:
<Window ...>
<Grid>
<ContentPresenter x:Name="OriginalContent" />
<Rectangle>
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=OriginalContent}" />
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
Dies kann programmgesteuert oder mithilfe einer Vorlage im Fenster erfolgen, aber ich möchte ein benutzerdefiniertes Steuerelement erstellen und die obige Struktur mithilfe seiner Vorlage erstellen. Wenn dies geschehen ist, können Sie Ihre Fenster, wie einfach diesen Code:
<Window ...>
<my:SelectiveDisabler>
<Grid x:Name="LayoutRoot"> ... </Grid> <!-- Original content -->
</my:SelectiveDisabler>
</Window>
Durch das Hinzufügen Maus-Event-Handler auf das Rechteck und riefen VisualTreeHelper.HitTest
auf dem Content, um zu bestimmen, welches Objekt wurde in dem ursprünglichen Inhalt angeklickt. Von diesem Punkt aus können Sie wählen, das Mausereignis zu ignorieren, es an den ursprünglichen Inhalt zur Verarbeitung weiterzuleiten oder im Falle einer Pipettensteuerung oder einer Objektauswahlfunktion einfach die gewünschten Objekte/Informationen zu extrahieren.
Details zu MergedDictionary Ansatz
Natürlich können Sie Ihre ganze UI restyle ein Resource verschmolzen in die Fenster der Ressourcen.
Eine einfache Möglichkeit besteht darin, einfach implizite Stile im zusammengeführten ResourceDictionary zu erstellen, damit alle TextBoxen als TextBlocks erscheinen, alle Schaltflächen als Rahmen usw. Dies funktioniert nicht sehr gut, da jede TextBox mit einem eigenen Stil oder ControlTemplate, das explizit festgelegt wird, kann die Updates verpassen. Darüber hinaus erhalten Sie möglicherweise nicht alle Objekte wie gewünscht, und es gibt keine Möglichkeit zum einfachen Entfernen der Befehle oder Klicken Sie auf Ereignisse von Schaltflächen, da sie explizit angegeben sind und der Stil diese nicht überschreibt.
Eine bessere Möglichkeit, dies zu erreichen, ist, dass die Stile im zusammengeführten ResourceDictionary eine angefügte Eigenschaft festlegen und anschließend CodeHinding im PropertyChangedCallback verwenden, um die Eigenschaften zu aktualisieren, die Sie wirklich ändern möchten. Wenn Sie die Eigenschaft "ModalMode" als Eigenschaft festlegen, werden alle lokalen Werte und Bindungen für eine Reihe von Eigenschaften (Template, Command, Click, IsEnabled usw.) in einer privaten DependencyProperty für das Objekt gespeichert und dann mit Standardwerten überschrieben . Zum Beispiel würde die Eigenschaft Command einer Schaltfläche vorübergehend auf null gesetzt. Wenn die angehängte Eigenschaft "ModalMode" auf "false" gesetzt wird, werden alle ursprünglichen lokalen Werte und Bindungen aus dem temporären Speicher zurückkopiert, und der temporäre Speicher wird gelöscht.
Diese Methode bietet eine praktische Möglichkeit, Teile Ihrer Benutzeroberfläche selektiv zu aktivieren/deaktivieren, indem einfach eine weitere angefügte Eigenschaft "IgnoreModalMode" hinzugefügt wird. Sie können dies für alle UIElements, für die die ModalMode-Änderungen nicht gelten sollen, manuell auf True setzen. Ihr ModalMode PropertyChangedCallback prüft das dann und wenn es wahr ist, tut es nichts.
Details zu Inputmanager Ansatz
Wenn Sie die Maus erfassen, Sie Mauskoordinaten egal bekommen kann, wo es bewegt wird. Übersetzen Sie diese in Bildschirmkoordinaten mit CompositionTarget.TransformToDevice(), und verwenden Sie dann CompositionTarget.TransformFromDevice() für jedes Kandidatenfenster.Wenn sich die Mauskoordinaten in Grenzen befinden, testen Sie das deaktivierte Fenster (dies kann auch dann noch durchgeführt werden, wenn ein Fenster deaktiviert ist). Wenn Sie das Objekt, auf das der Benutzer geklickt hat, verwenden möchten, verwenden Sie InputManager.ProcesInput, um das Mausereignis zu verursachen im anderen Fenster genauso verarbeitet, als ob es nicht deaktiviert wäre.
Einzelheiten zur Verwendung von Daten
Sie Bindung kann ein Stile verwenden, um die IsEnabled Eigenschaft von Buttons zu binden, MenuItems, usw. auf einen statischen Wert wie folgt aus:
<Setter Property="IsEnabled" Value="{Binding NonModal, Source={x:Static local:ModalModeTracker.Instance}}" />
nun standardmäßig alle Elemente Mit diesen Stilen wird automatisch deaktiviert, wenn Ihre NonModal-Eigenschaft falsch wird. Jedes einzelne Steuerelement kann jedoch mit IsEnabled="true"
überschrieben werden, um auch im modalen Modus aktiviert zu bleiben. Komplexere Bindungen können mit MultiBinding und EDF ExpressionBinding erstellt werden, um die gewünschten Regeln festzulegen.
Keiner dieser Ansätze durch Ihre visuelle Schnittstelle erfordern laufen, Aktivieren und Deaktivieren von Funktionen. Welche dieser Optionen tatsächlich ausgewählt wird, hängt davon ab, welche Funktionalität Sie im modalen Modus tatsächlich bereitstellen möchten und wie der Rest der Benutzeroberfläche gestaltet ist.
In jedem Fall macht WPF das viel einfacher als in WinForms Tagen. Waffst du nicht einfach WPF's Power?
Ich glaube, Sie auf dem richtigen Weg sind. Sie können die Steuerelemente in CodeBehind entweder deaktivieren/aktivieren, wenn das Fenster geöffnet/geschlossen wird, oder Sie können DataTriggers in Ihrem XAML verwenden und die Eigenschaft 'IsEnabled' für die verschiedenen Steuerelemente festlegen, abhängig davon, ob das neue Fenster geöffnet ist oder nicht. –