Dialogfelder in xamarin. MacDialogs in Xamarin.Mac
Wenn Sie mit c# und .net in einer xamarin. Mac-Anwendung arbeiten, haben Sie Zugriff auf dieselben Dialoge und modale Fenster, die ein Entwickler in " Ziel-C " und " Xcode " verwendet.When working with C# and .NET in a Xamarin.Mac application, you have access to the same Dialogs and Modal Windows that a developer working in Objective-C and Xcode does. Da xamarin. Mac direkt in Xcode integriert ist, können Sie die Interface Builder von Xcode verwenden, um Ihre modalen Fenster zu erstellen und zu verwalten (oder Sie optional direkt in c#-Code zu erstellen).Because Xamarin.Mac integrates directly with Xcode, you can use Xcode's Interface Builder to create and maintain your Modal Windows (or optionally create them directly in C# code).
Ein Dialogfeld wird als Reaktion auf eine Benutzeraktion angezeigt und bietet in der Regel Möglichkeiten, wie Benutzer die Aktion durchführen können.A dialog appears in response to a user action and typically provides ways users can complete the action. Ein Dialogfeld erfordert eine Antwort vom Benutzer, bevor es geschlossen werden kann.A dialog requires a response from the user before it can be closed.
Windows kann in einem nicht modalen Zustand (z. b. in einem Text-Editor, in dem mehrere Dokumente gleichzeitig geöffnet werden können) oder Modal verwendet werden (z. b. ein Export Dialogfeld, das vor dem Fortsetzen der Anwendung verworfen werden muss)Windows can be used in a Modeless state (such as a text editor that can have multiple documents open at once) or Modal (such as an Export dialog that must be dismissed before the application can continue).
In diesem Artikel werden die Grundlagen der Arbeit mit Dialogfeldern und modalen Fenstern in einer xamarin. Mac-Anwendung behandelt.In this article, we'll cover the basics of working with Dialogs and Modal Windows in a Xamarin.Mac application. Es wird dringend empfohlen, dass Sie zunächst den Artikel Hello, Mac , insbesondere die Einführung in Xcode und die Abschnitte zu Interface Builder und Outlets und Aktionen , durcharbeiten, da er wichtige Konzepte und Techniken behandelt, die wir in diesem Artikel verwenden werden.It is highly suggested that you work through the Hello, Mac article first, specifically the Introduction to Xcode and Interface Builder and Outlets and Actions sections, as it covers key concepts and techniques that we'll be using in this article.
Lesen Sie ggf. den Abschnitt verfügbar machen von c#-Klassen/-Methoden zu "Ziel-c " im Dokument " xamarin. Mac ". darin werden die Register
-und-Befehle erläutert, die Export
zum Verknüpfen ihrer c#-Klassen mit Ziel-c-Objekten und Benutzeroberflächen Elementen verwendet werden.You may want to take a look at the Exposing C# classes / methods to Objective-C section of the Xamarin.Mac Internals document as well, it explains the Register
and Export
commands used to wire-up your C# classes to Objective-C objects and UI Elements.
Einführung in DialogfelderIntroduction to Dialogs
Ein Dialogfeld wird als Reaktion auf eine Benutzeraktion (z. b. das Speichern einer Datei) angezeigt und bietet Benutzern die Möglichkeit, diese Aktion abzuschließen.A dialog appears in response to a user action (such as saving a file) and provides a way for users to complete that action. Ein Dialogfeld erfordert eine Antwort vom Benutzer, bevor es geschlossen werden kann.A dialog requires a response from the user before it can be closed.
Gemäß Apple gibt es drei Möglichkeiten, einen Dialog anzuzeigen:According to Apple, there are three ways to present a Dialog:
- Modales Dokument : in einem modalen Dialogfeld wird verhindert, dass der Benutzer innerhalb eines bestimmten Dokuments nichts anderes tun kann, bis er verworfen wird.Document Modal - A Document Modal dialog prevents the user from doing anything else within a given document until it is dismissed.
- Modaler App : ein modales App-Dialogfeld verhindert, dass der Benutzer mit der Anwendung interagiert, bis er verworfen wird.App Modal - An App Modal dialog prevents the user from interacting with the application until it is dismissed.
- Ohne Modell Ein nicht modalem Dialogfeld ermöglicht es Benutzern, die Einstellungen im Dialog Feld zu ändern, während Sie weiterhin mit dem Dokument Fenster interagieren.Modeless A Modeless Dialog enables users to change settings in the dialog while still interacting with the document window.
Modales FensterModal Window
Jeder Standard NSWindow
kann als angepasstes Dialogfeld verwendet werden, indem er modal angezeigt wird:Any standard NSWindow
can be used as a customized dialog by displaying it modally:
Modale Dialog Blätter für DokumenteDocument Modal Dialog Sheets
Ein Blatt ist ein modales Dialogfeld, das an ein bestimmtes Dokument Fenster angefügt wird. Dadurch wird verhindert, dass Benutzer mit dem Fenster interagieren, bis Sie das Dialogfeld schließen.A Sheet is a modal dialog that is attached to a given document window, preventing users from interacting with the window until they dismiss the dialog. Ein Blatt wird an das Fenster angefügt, aus dem es hervorgeht, und es kann jeweils nur ein Blatt für ein Fenster geöffnet werden.A Sheet is attached to the window from which it emerges and only one sheet can be open for a window at any one time.
Fenster "Einstellungen"Preferences Windows
Ein Einstellungsfenster ist ein nicht modalem Dialogfeld, das die Einstellungen der Anwendung enthält, die der Benutzer nur selten ändert.A Preferences Window is a modeless dialog that contains the application's settings that the user changes infrequently. In den Einstellungs Fenstern ist häufig eine Symbolleiste enthalten, die es dem Benutzer ermöglicht, zwischen verschiedenen Gruppen von Einstellungen zu wechseln:Preferences Windows often include a Toolbar that allows the user to switch between different groups of settings:
Dialog Feld ÖffnenOpen Dialog
Das Dialog Feld Öffnen bietet Benutzern eine konsistente Möglichkeit, ein Element in einer Anwendung zu suchen und zu öffnen:The Open Dialog gives users a consistent way to find and open an item in an application:
Dialogfelder zum Drucken und Einrichten der SeitePrint and Page Setup Dialogs
macOS bietet Standard Dialogfelder für die Druck-und Seiten Einrichtung, die von Ihrer Anwendung angezeigt werden können, damit Benutzer in jeder verwendeten Anwendung über eine konsistente Druckfunktion verfügen können.macOS provides standard Print and Page Setup Dialogs that your application can display so that users can have a consistent printing experience in every application they use.
Das Dialogfeld Drucken kann als freies Dialogfeld angezeigt werden:The Print Dialog can be displayed as both a free floating dialog box:
Oder es kann als Blatt angezeigt werden:Or it can be displayed as a Sheet:
Das Dialogfeld Seite einrichten kann sowohl als freies Dialogfeld für unverankerte Felder angezeigt werden:The Page Setup Dialog can be displayed as both a free floating dialog box:
Oder es kann als Blatt angezeigt werden:Or it can be displayed as a Sheet:
Dialogfelder speichernSave Dialogs
Das Dialog Feld "Speichern" bietet Benutzern eine konsistente Möglichkeit, ein Element in einer Anwendung zu speichern.The Save Dialog gives users a consistent way to save an item in an application. Das Dialog Feld "Speichern" hat zwei Zustände: Minimal (auch als reduziert bezeichnet):The Save Dialog has two states: Minimal (also known as Collapsed):
Und Erweiterter Status:And the Expanded state:
Das Dialog Feld zum minimalen speichern kann auch als Blatt angezeigt werden:The Minimal Save Dialog can also be displayed as a Sheet:
Wie kann das Erweiterte Dialog Feld "Speichern":As can the Expanded Save Dialog:
Weitere Informationen finden Sie im Abschnitt mit den Dialog Feldern von Apple OS X Human Interface GuidelinesFor more information, see the Dialogs section of Apple's OS X Human Interface Guidelines
Hinzufügen eines modalen Fensters zu einem ProjektAdding a Modal Window to a Project
Abgesehen vom Hauptdokument Fenster muss eine xamarin. Mac-Anwendung möglicherweise andere Windows-Typen für den Benutzer anzeigen, z. b. Einstellungen oder Inspektor-Panels.Aside from the main document window, a Xamarin.Mac application might need to display other types of windows to the user, such as Preferences or Inspector Panels.
Gehen Sie folgendermaßen vor, um ein neues Fenster hinzuzufügen:To add a new window, do the following:
Öffnen Sie im Projektmappen-Explorer die
Main.storyboard
Datei zur Bearbeitung in der Interface Builder von Xcode.In the Solution Explorer, open theMain.storyboard
file for editing in Xcode's Interface Builder.Ziehen Sie einen neuen Ansichts Controller in den Designoberfläche:Drag a new View Controller into the Design Surface:
Geben Sie im Identitäts Inspektor als
CustomDialogController
Klassennamen ein:In the Identity Inspector, enterCustomDialogController
for the Class Name:Wechseln Sie zurück zu Visual Studio für Mac, lassen Sie die Synchronisierung mit Xcode zu, und erstellen Sie die
CustomDialogController.h
Datei.Switch back to Visual Studio for Mac, allow it to sync with Xcode and create theCustomDialogController.h
file.Kehren Sie zu Xcode zurück, und entwerfen Sie Ihre Schnittstelle:Return to Xcode and design your interface:
Erstellen Sie aus dem Hauptfenster der APP auf den neuen Ansichts Controller eine modale Tabelle, indem Sie das Steuerelement aus dem UI-Element ziehen, das das Dialogfeld in das Fenster des Dialog Felds öffnet.Create a Modal Segue from the Main Window of your app to the new View Controller by control-dragging from the UI element that will open the dialog to the dialog's window. Weisen Sie den Bezeichner zu
ModalSegue
:Assign the IdentifierModalSegue
:Richten Sie alle Aktionen und Outlets ein:Wire-up any Actions and Outlets:
Speichern Sie die Änderungen, und kehren Sie zu Visual Studio für Mac zurück, um mit Xcode zu synchronisieren.Save your changes and return to Visual Studio for Mac to sync with Xcode.
Erstellen Sie die CustomDialogController.cs
Datei wie folgt:Make the CustomDialogController.cs
file look like the following:
using System;
using Foundation;
using AppKit;
namespace MacDialog
{
public partial class CustomDialogController : NSViewController
{
#region Private Variables
private string _dialogTitle = "Title";
private string _dialogDescription = "Description";
private NSViewController _presentor;
#endregion
#region Computed Properties
public string DialogTitle {
get { return _dialogTitle; }
set { _dialogTitle = value; }
}
public string DialogDescription {
get { return _dialogDescription; }
set { _dialogDescription = value; }
}
public NSViewController Presentor {
get { return _presentor; }
set { _presentor = value; }
}
#endregion
#region Constructors
public CustomDialogController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewWillAppear ()
{
base.ViewWillAppear ();
// Set initial title and description
Title.StringValue = DialogTitle;
Description.StringValue = DialogDescription;
}
#endregion
#region Private Methods
private void CloseDialog() {
Presentor.DismissViewController (this);
}
#endregion
#region Custom Actions
partial void AcceptDialog (Foundation.NSObject sender) {
RaiseDialogAccepted();
CloseDialog();
}
partial void CancelDialog (Foundation.NSObject sender) {
RaiseDialogCanceled();
CloseDialog();
}
#endregion
#region Events
public EventHandler DialogAccepted;
internal void RaiseDialogAccepted() {
if (this.DialogAccepted != null)
this.DialogAccepted (this, EventArgs.Empty);
}
public EventHandler DialogCanceled;
internal void RaiseDialogCanceled() {
if (this.DialogCanceled != null)
this.DialogCanceled (this, EventArgs.Empty);
}
#endregion
}
}
Dieser Code macht einige Eigenschaften verfügbar, um den Titel und die Beschreibung des Dialog Felds festzulegen, und einige Ereignisse, um darauf zu reagieren, dass das Dialogfeld abgebrochen oder akzeptiert wird.This code exposes a few properties to set the title and the description of the dialog and a few events to react to the dialog being canceled or accepted.
Bearbeiten Sie als nächstes die ViewController.cs
Datei, überschreiben PrepareForSegue
Sie die-Methode, und führen Sie Sie wie folgt aus:Next, edit the ViewController.cs
file, override the PrepareForSegue
method and make it look like the following:
public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
// Take action based on the segue name
switch (segue.Identifier) {
case "ModalSegue":
var dialog = segue.DestinationController as CustomDialogController;
dialog.DialogTitle = "MacDialog";
dialog.DialogDescription = "This is a sample dialog.";
dialog.DialogAccepted += (s, e) => {
Console.WriteLine ("Dialog accepted");
DismissViewController (dialog);
};
dialog.Presentor = this;
break;
}
}
Dieser Code initialisiert den segue, den wir in der Interface Builder von Xcode definiert haben, in unserem Dialogfeld und richtet den Titel und die Beschreibung ein.This code initializes the segue that we defined in Xcode's Interface Builder to our dialog and sets up the title and description. Außerdem wird die Auswahl behandelt, die der Benutzer im Dialogfeld vornimmt.It also handles the choice the user makes in the dialog box.
Wir können die Anwendung ausführen und das benutzerdefinierte Dialogfeld anzeigen:We can run our application and display the custom dialog:
Weitere Informationen zur Verwendung von Windows in einer xamarin. Mac-Anwendung finden Sie in unserer Dokumentation zum Arbeiten mit Windows .For more information about using windows in a Xamarin.Mac application, please see our Working with Windows documentation.
Erstellen eines benutzerdefinierten BlattsCreating a Custom Sheet
Ein Blatt ist ein modales Dialogfeld, das an ein bestimmtes Dokument Fenster angefügt wird. Dadurch wird verhindert, dass Benutzer mit dem Fenster interagieren, bis Sie das Dialogfeld schließen.A Sheet is a modal dialog that is attached to a given document window, preventing users from interacting with the window until they dismiss the dialog. Ein Blatt wird an das Fenster angefügt, aus dem es hervorgeht, und es kann jeweils nur ein Blatt für ein Fenster geöffnet werden.A Sheet is attached to the window from which it emerges and only one sheet can be open for a window at any one time.
Gehen Sie folgendermaßen vor, um ein benutzerdefiniertes Blatt in xamarin. Mac zu erstellen:To create a Custom Sheet in Xamarin.Mac, let's do the following:
Öffnen Sie im Projektmappen-Explorer die
Main.storyboard
Datei zur Bearbeitung in der Interface Builder von Xcode.In the Solution Explorer, open theMain.storyboard
file for editing in Xcode's Interface Builder.Ziehen Sie einen neuen Ansichts Controller in den Designoberfläche:Drag a new View Controller into the Design Surface:
Entwerfen Sie die Benutzeroberfläche:Design your user interface:
Erstellen Sie eine Blatt Tabelle aus dem Hauptfenster für den neuen Ansichts Controller:Create a Sheet Segue from your Main Window to the new View Controller:
Benennen Sie im Identitäts Inspektor die Klasse des Ansichts Controllers
SheetViewController
:In the Identity Inspector, name the View Controller's ClassSheetViewController
:Definieren Sie alle erforderlichen Outlets und Aktionen:Define any needed Outlets and Actions:
Speichern Sie die Änderungen, und kehren Sie zur Synchronisierung Visual Studio für Mac zurück.Save your changes and return to Visual Studio for Mac to sync.
Bearbeiten Sie anschließend die SheetViewController.cs
Datei, und führen Sie Sie wie folgt aus:Next, edit the SheetViewController.cs
file and make it look like the following:
using System;
using Foundation;
using AppKit;
namespace MacDialog
{
public partial class SheetViewController : NSViewController
{
#region Private Variables
private string _userName = "";
private string _password = "";
private NSViewController _presentor;
#endregion
#region Computed Properties
public string UserName {
get { return _userName; }
set { _userName = value; }
}
public string Password {
get { return _password;}
set { _password = value;}
}
public NSViewController Presentor {
get { return _presentor; }
set { _presentor = value; }
}
#endregion
#region Constructors
public SheetViewController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewWillAppear ()
{
base.ViewWillAppear ();
// Set initial values
NameField.StringValue = UserName;
PasswordField.StringValue = Password;
// Wireup events
NameField.Changed += (sender, e) => {
UserName = NameField.StringValue;
};
PasswordField.Changed += (sender, e) => {
Password = PasswordField.StringValue;
};
}
#endregion
#region Private Methods
private void CloseSheet() {
Presentor.DismissViewController (this);
}
#endregion
#region Custom Actions
partial void AcceptSheet (Foundation.NSObject sender) {
RaiseSheetAccepted();
CloseSheet();
}
partial void CancelSheet (Foundation.NSObject sender) {
RaiseSheetCanceled();
CloseSheet();
}
#endregion
#region Events
public EventHandler SheetAccepted;
internal void RaiseSheetAccepted() {
if (this.SheetAccepted != null)
this.SheetAccepted (this, EventArgs.Empty);
}
public EventHandler SheetCanceled;
internal void RaiseSheetCanceled() {
if (this.SheetCanceled != null)
this.SheetCanceled (this, EventArgs.Empty);
}
#endregion
}
}
Bearbeiten Sie als nächstes die ViewController.cs
Datei, bearbeiten PrepareForSegue
Sie die-Methode, und führen Sie Sie wie folgt aus:Next, edit the ViewController.cs
file, edit the PrepareForSegue
method and make it look like the following:
public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
// Take action based on the segue name
switch (segue.Identifier) {
case "ModalSegue":
var dialog = segue.DestinationController as CustomDialogController;
dialog.DialogTitle = "MacDialog";
dialog.DialogDescription = "This is a sample dialog.";
dialog.DialogAccepted += (s, e) => {
Console.WriteLine ("Dialog accepted");
DismissViewController (dialog);
};
dialog.Presentor = this;
break;
case "SheetSegue":
var sheet = segue.DestinationController as SheetViewController;
sheet.SheetAccepted += (s, e) => {
Console.WriteLine ("User Name: {0} Password: {1}", sheet.UserName, sheet.Password);
};
sheet.Presentor = this;
break;
}
}
Wenn wir die Anwendung ausführen und das Blatt öffnen, wird es an das Fenster angefügt:If we run our application and open the Sheet, it will be attached to the window:
Erstellen eines Einstellungs DialogfeldsCreating a Preferences Dialog
Bevor wir die Einstellungs Ansicht in Interface Builder aufstellen, müssen wir einen benutzerdefinierten Typ "*" hinzufügen, um das Auslagern der Einstellungen zu behandeln.Before we lay out the Preference View in Interface Builder, we'll need to add a custom segue type to handle switching out the preferences. Fügen Sie dem Projekt eine neue Klasse hinzu, und nennen Sie Sie ReplaceViewSeque
.Add a new class to your project and call it ReplaceViewSeque
. Bearbeiten Sie die-Klasse, und sehen Sie Sie wie folgt aus:Edit the class and make it look like the following:
using System;
using AppKit;
using Foundation;
namespace MacWindows
{
[Register("ReplaceViewSeque")]
public class ReplaceViewSeque : NSStoryboardSegue
{
#region Constructors
public ReplaceViewSeque() {
}
public ReplaceViewSeque (string identifier, NSObject sourceController, NSObject destinationController) : base(identifier,sourceController,destinationController) {
}
public ReplaceViewSeque (IntPtr handle) : base(handle) {
}
public ReplaceViewSeque (NSObjectFlag x) : base(x) {
}
#endregion
#region Override Methods
public override void Perform ()
{
// Cast the source and destination controllers
var source = SourceController as NSViewController;
var destination = DestinationController as NSViewController;
// Is there a source?
if (source == null) {
// No, get the current key window
var window = NSApplication.SharedApplication.KeyWindow;
// Swap the controllers
window.ContentViewController = destination;
// Release memory
window.ContentViewController?.RemoveFromParentViewController ();
} else {
// Swap the controllers
source.View.Window.ContentViewController = destination;
// Release memory
source.RemoveFromParentViewController ();
}
}
#endregion
}
}
Wenn Sie den benutzerdefinierten segue erstellt haben, können Sie ein neues Fenster in der Interface Builder von Xcode hinzufügen, um unsere Einstellungen zu verarbeiten.With the custom segue created, we can add a new window in Xcode's Interface Builder to handle our preferences.
Gehen Sie folgendermaßen vor, um ein neues Fenster hinzuzufügen:To add a new window, do the following:
Öffnen Sie im Projektmappen-Explorer die
Main.storyboard
Datei zur Bearbeitung in der Interface Builder von Xcode.In the Solution Explorer, open theMain.storyboard
file for editing in Xcode's Interface Builder.Ziehen Sie einen neuen Fenster Controller in den Designoberfläche:Drag a new Window Controller into the Design Surface:
Ordnen Sie das Fenster in der Nähe des Menü leisten-Designers an:Arrange the Window near the Menu Bar designer:
Erstellen Sie Kopien des angefügten Ansichts Controllers, da in Ihrer Einstellungs Ansicht Registerkarten angezeigt werden:Create copies of the attached View Controller as there will be tabs in your preference view:
Ziehen Sie einen neuen Symbolleisten Controller aus der Bibliothek:Drag a new Toolbar Controller from the Library:
Und legen Sie Sie im Fenster im Designoberfläche ab:And drop it on the Window in the Design Surface:
Layout des Entwurfs der Symbolleiste:Layout the design of your toolbar:
Control-Click und ziehen Sie von jeder Symbolleisten- Schaltfläche auf die oben erstellten Ansichten.Control-Click and drag from each Toolbar Button to the Views you created above. Wählen Sie einen benutzerdefinierten Typ "Typ" aus:Select a Custom segue type:
Wählen Sie den neuen segue aus, und legen Sie die Klasse auf fest
ReplaceViewSegue
:Select the new Segue and set the Class toReplaceViewSegue
:Wählen Sie im MenuBar-Designer auf der Designoberfläche im Menü Anwendung die Option Einstellungen... aus, und klicken Sie dann auf das Fenster "Einstellungen", um einen anzeigen -segue zu erstellen:In the Menubar Designer on the Design Surface, from the Application Menu select Preferences..., control-click and drag to the Preferences Window to create a Show segue:
Speichern Sie die Änderungen, und kehren Sie zur Synchronisierung Visual Studio für Mac zurück.Save your changes and return to Visual Studio for Mac to sync.
Wenn wir den Code ausführen und die Einstellungen im Anwendungsmenü auswählen, wird das Fenster angezeigt:If we run the code and select the Preferences... from the Application Menu, the window will be displayed:
Weitere Informationen zum Arbeiten mit Fenstern und Symbolleisten finden Sie in unserer Windows -und Toolbars -Dokumentation.For more information on working with Windows and Toolbars, please see our Windows and Toolbars documentation.
Speichern und Laden von EinstellungenSaving and Loading Preferences
Wenn der Benutzer in einer typischen macOS-App Änderungen an den Benutzereinstellungen der APP vornimmt, werden diese Änderungen automatisch gespeichert.In a typical macOS App, when the user makes changes to any of the App's User Preferences, those changes are saved automatically. Die einfachste Möglichkeit, dies in einer xamarin. Mac-app zu behandeln, besteht darin, eine einzelne Klasse zu erstellen, um alle Benutzereinstellungen zu verwalten und Sie systemweit freizugeben.The easiest way to handle this in a Xamarin.Mac app, is to create a single class to manage all of the user's preferences and share it system-wide.
Fügen Sie zunächst dem Projekt eine neue Klasse hinzu, AppPreferences
und erben Sie von NSObject
.First, add a new AppPreferences
class to the project and inherit from NSObject
. Die Einstellungen werden für die Verwendung von Datenbindung und Key-Value Codierung entwickelt, die das Erstellen und Verwalten von Einstellungs Formularen erheblich vereinfachen.The preferences will be designed to use Data Binding and Key-Value Coding which will make the process of creating and maintaining the preference forms much simpler. Da die Einstellungen aus einer kleinen Menge an einfachen Datentypen bestehen, verwenden Sie das integrierte, NSUserDefaults
um Werte zu speichern und abzurufen.Since the Preferences will consist of a small amount of simple datatypes, use the built in NSUserDefaults
to store and retrieve values.
Bearbeiten AppPreferences.cs
Sie die Datei, und führen Sie Sie wie folgt aus:Edit the AppPreferences.cs
file and make it look like the following:
using System;
using Foundation;
using AppKit;
namespace SourceWriter
{
[Register("AppPreferences")]
public class AppPreferences : NSObject
{
#region Computed Properties
[Export("DefaultLanguage")]
public int DefaultLanguage {
get {
var value = LoadInt ("DefaultLanguage", 0);
return value;
}
set {
WillChangeValue ("DefaultLanguage");
SaveInt ("DefaultLanguage", value, true);
DidChangeValue ("DefaultLanguage");
}
}
[Export("SmartLinks")]
public bool SmartLinks {
get { return LoadBool ("SmartLinks", true); }
set {
WillChangeValue ("SmartLinks");
SaveBool ("SmartLinks", value, true);
DidChangeValue ("SmartLinks");
}
}
// Define any other required user preferences in the same fashion
...
[Export("EditorBackgroundColor")]
public NSColor EditorBackgroundColor {
get { return LoadColor("EditorBackgroundColor", NSColor.White); }
set {
WillChangeValue ("EditorBackgroundColor");
SaveColor ("EditorBackgroundColor", value, true);
DidChangeValue ("EditorBackgroundColor");
}
}
#endregion
#region Constructors
public AppPreferences ()
{
}
#endregion
#region Public Methods
public int LoadInt(string key, int defaultValue) {
// Attempt to read int
var number = NSUserDefaults.StandardUserDefaults.IntForKey(key);
// Take action based on value
if (number == null) {
return defaultValue;
} else {
return (int)number;
}
}
public void SaveInt(string key, int value, bool sync) {
NSUserDefaults.StandardUserDefaults.SetInt(value, key);
if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
}
public bool LoadBool(string key, bool defaultValue) {
// Attempt to read int
var value = NSUserDefaults.StandardUserDefaults.BoolForKey(key);
// Take action based on value
if (value == null) {
return defaultValue;
} else {
return value;
}
}
public void SaveBool(string key, bool value, bool sync) {
NSUserDefaults.StandardUserDefaults.SetBool(value, key);
if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
}
public string NSColorToHexString(NSColor color, bool withAlpha) {
//Break color into pieces
nfloat red=0, green=0, blue=0, alpha=0;
color.GetRgba (out red, out green, out blue, out alpha);
// Adjust to byte
alpha *= 255;
red *= 255;
green *= 255;
blue *= 255;
//With the alpha value?
if (withAlpha) {
return String.Format ("#{0:X2}{1:X2}{2:X2}{3:X2}", (int)alpha, (int)red, (int)green, (int)blue);
} else {
return String.Format ("#{0:X2}{1:X2}{2:X2}", (int)red, (int)green, (int)blue);
}
}
public NSColor NSColorFromHexString (string hexValue)
{
var colorString = hexValue.Replace ("#", "");
float red, green, blue, alpha;
// Convert color based on length
switch (colorString.Length) {
case 3 : // #RGB
red = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(0, 1)), 16) / 255f;
green = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(1, 1)), 16) / 255f;
blue = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(2, 1)), 16) / 255f;
return NSColor.FromRgba(red, green, blue, 1.0f);
case 6 : // #RRGGBB
red = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
green = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
blue = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
return NSColor.FromRgba(red, green, blue, 1.0f);
case 8 : // #AARRGGBB
alpha = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
red = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
green = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
blue = Convert.ToInt32(colorString.Substring(6, 2), 16) / 255f;
return NSColor.FromRgba(red, green, blue, alpha);
default :
throw new ArgumentOutOfRangeException(string.Format("Invalid color value '{0}'. It should be a hex value of the form #RBG, #RRGGBB or #AARRGGBB", hexValue));
}
}
public NSColor LoadColor(string key, NSColor defaultValue) {
// Attempt to read color
var hex = NSUserDefaults.StandardUserDefaults.StringForKey(key);
// Take action based on value
if (hex == null) {
return defaultValue;
} else {
return NSColorFromHexString (hex);
}
}
public void SaveColor(string key, NSColor color, bool sync) {
// Save to default
NSUserDefaults.StandardUserDefaults.SetString(NSColorToHexString(color,true), key);
if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
}
#endregion
}
}
Diese Klasse enthält einige Hilfsroutinen, wie z. b SaveInt
LoadInt
.,, SaveColor
, LoadColor
usw., um die Arbeit mit zu NSUserDefaults
vereinfachen.This class contains a few helper routines such as SaveInt
, LoadInt
, SaveColor
, LoadColor
, etc. to make working with NSUserDefaults
easier. Da darüber hinaus NSUserDefaults
nicht über eine integrierte Methode zum Verarbeiten von verfügt NSColors
, werden die NSColorToHexString
-Methode und die-Methode verwendet, um Farben in webbasierte hexadezimal Zeichenfolgen NSColorFromHexString
( #RRGGBBAA
wobei AA
die Alpha Transparenz ist) zu konvertieren, die problemlos gespeichert und abgerufen werden können.Also, since NSUserDefaults
does not have a built-in way to handle NSColors
, the NSColorToHexString
and NSColorFromHexString
methods are used to convert colors to web-based hex strings (#RRGGBBAA
where AA
is the alpha transparency) that can be easily stored and retrieved.
Erstellen Sie in der AppDelegate.cs
Datei eine Instanz des apppreferences -Objekts, das App-weit verwendet wird:In the AppDelegate.cs
file, create an instance of the AppPreferences object that will be used app-wide:
using AppKit;
using Foundation;
using System.IO;
using System;
namespace SourceWriter
{
[Register ("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
#region Computed Properties
public int NewWindowNumber { get; set;} = -1;
public AppPreferences Preferences { get; set; } = new AppPreferences();
#endregion
#region Constructors
public AppDelegate ()
{
}
#endregion
...
Verdrahtung von Voreinstellungen in Einstellungs AnsichtenWiring Preferences to Preference Views
Verbinden Sie als nächstes die Preference-Klasse mit den Benutzeroberflächen Elementen im Einstellungsfenster und den oben erstellten Ansichten.Next, connect Preference class to UI elements on the Preference Window and Views created above. Wählen Sie in Interface Builder einen Ansichts Controller aus, und wechseln Sie zum Identitäts Inspektor, und erstellen Sie eine benutzerdefinierte Klasse für den Controller:In Interface Builder, select a Preference View Controller and switch to the Identity Inspector, create a custom class for the controller:
Wechseln Sie zurück zu Visual Studio für Mac, um die Änderungen zu synchronisieren und die neu erstellte Klasse zum Bearbeiten zu öffnen.Switch back to Visual Studio for Mac to sync your changes and open the newly created class for editing. Legen Sie die Klasse wie folgt an:Make the class look like the following:
using System;
using Foundation;
using AppKit;
namespace SourceWriter
{
public partial class EditorPrefsController : NSViewController
{
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
#region Computed Properties
[Export("Preferences")]
public AppPreferences Preferences {
get { return App.Preferences; }
}
#endregion
#region Constructors
public EditorPrefsController (IntPtr handle) : base (handle)
{
}
#endregion
}
}
Beachten Sie, dass diese Klasse zwei Dinge durchgeführt hat: Erstens gibt es eine App
Hilfseigenschaft, um den Zugriff auf den appdelegaten zu vereinfachen.Notice that this class has done two things here: First, there is a helper App
property to make accessing the AppDelegate easier. Zweitens macht die- Preferences
Eigenschaft die globale apppreferences -Klasse für die Datenbindung mit allen in dieser Ansicht platzierten UI-Steuerelementen verfügbar.Second, the Preferences
property exposes the global AppPreferences class for data binding with any UI controls placed on this View.
Doppelklicken Sie dann auf die Storyboard-Datei, um Sie erneut in Interface Builder zu öffnen (und sehen Sie sich die oben beschriebenen Änderungen an).Next, double click the Storyboard file to re-open it in Interface Builder (and see the changes just made above). Ziehen Sie alle UI-Steuerelemente, die zum Erstellen der Einstellungs Schnittstelle in die Ansicht erforderlich sind.Drag any UI controls required to build the preferences interface into the View. Wechseln Sie für jedes Steuerelement zum Bindungs Inspektor , und binden Sie an die einzelnen Eigenschaften der apppreference -Klasse:For each control, switch to the Binding Inspector and bind to the individual properties of the AppPreference class:
Wiederholen Sie die obigen Schritte für alle Panels (Ansichts Controller) und bevorzugte Eigenschaften.Repeat the above steps for all of the panels (View Controllers) and Preference Properties required.
Anwenden von Voreinstellungs Änderungen auf alle geöffneten FensterApplying Preference Changes to All Open Windows
Wie oben erwähnt, werden die Änderungen in einer typischen macOS-APP, wenn der Benutzer Änderungen an den Benutzereinstellungen der APP vornimmt, automatisch gespeichert und auf alle Windows-Benutzer angewendet, die der Benutzer möglicherweise in der Anwendung geöffnet hat.As stated above, in a typical macOS App, when the user makes changes to any of the App's User Preferences, those changes are saved automatically and applied to any windows the user might have open in the application.
Die sorgfältige Planung und der Entwurf der Einstellungen Ihrer APP ermöglicht die reibungslose und transparente Ausführung dieses Prozesses für den Endbenutzer mit minimalem Codierungsaufwand.Careful planning and design of your app's preferences and windows will allow this process to happen smoothly and transparently to the end user, with a minimal amount of coding work.
Fügen Sie für jedes Fenster, das die App-Einstellungen verbraucht, dem Content View Controller die folgende Hilfseigenschaft hinzu, um den Zugriff auf den appdelegaten zu vereinfachen:For any Window that will be consuming App Preferences, add the following helper property to its Content View Controller to make accessing our AppDelegate easier:
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
Fügen Sie als nächstes eine Klasse hinzu, um den Inhalt oder das Verhalten auf der Grundlage der Benutzereinstellungen zu konfigurieren:Next, add a class to configure the contents or behavior based on the user's preferences:
public void ConfigureEditor() {
// General Preferences
TextEditor.AutomaticLinkDetectionEnabled = App.Preferences.SmartLinks;
TextEditor.AutomaticQuoteSubstitutionEnabled = App.Preferences.SmartQuotes;
...
}
Wenn das Fenster zum ersten Mal geöffnet wird, müssen Sie die Konfigurations Methode aufzurufen, um sicherzustellen, dass Sie den Einstellungen des Benutzers entspricht:You need to call the configuration method when the Window is first opened to make sure it conforms to the user's preferences:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Configure editor from user preferences
ConfigureEditor ();
...
}
Bearbeiten Sie anschließend die AppDelegate.cs
Datei, und fügen Sie die folgende Methode hinzu, um alle vorgenommenen Änderungen auf alle geöffneten Fenster anzuwenden:Next, edit the AppDelegate.cs
file and add the following method to apply any preference changes to all open windows:
public void UpdateWindowPreferences() {
// Process all open windows
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
if (content != null ) {
// Reformat all text
content.ConfigureEditor ();
}
}
}
Fügen Sie PreferenceWindowDelegate
dem Projekt eine Klasse hinzu, und machen Sie Sie wie folgt:Next, add a PreferenceWindowDelegate
class to the project and make it look like the following:
using System;
using AppKit;
using System.IO;
using Foundation;
namespace SourceWriter
{
public class PreferenceWindowDelegate : NSWindowDelegate
{
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
#region Computed Properties
public NSWindow Window { get; set;}
#endregion
#region constructors
public PreferenceWindowDelegate (NSWindow window)
{
// Initialize
this.Window = window;
}
#endregion
#region Override Methods
public override bool WindowShouldClose (Foundation.NSObject sender)
{
// Apply any changes to open windows
App.UpdateWindowPreferences();
return true;
}
#endregion
}
}
Dadurch werden alle Voreinstellungs Änderungen an alle geöffneten Fenster gesendet, wenn das Einstellungsfenster geschlossen wird.This will cause any preference changes to be sent to all open Windows when the preference Window closes.
Bearbeiten Sie schließlich den Preference Window-Controller, und fügen Sie den oben erstellten Delegaten hinzu:Finally, edit the Preference Window Controller and add the delegate created above:
using System;
using Foundation;
using AppKit;
namespace SourceWriter
{
public partial class PreferenceWindowController : NSWindowController
{
#region Constructors
public PreferenceWindowController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void WindowDidLoad ()
{
base.WindowDidLoad ();
// Initialize
Window.Delegate = new PreferenceWindowDelegate(Window);
Toolbar.SelectedItemIdentifier = "General";
}
#endregion
}
}
Wenn alle diese Änderungen vorhanden sind und der Benutzer die Einstellungen der APP bearbeitet und das Einstellungsfenster schließt, werden die Änderungen auf alle geöffneten Fenster angewendet:With all these changes in place, if the user edits the App's Preferences and closes the Preference Window, the changes will be applied to all open Windows:
Dialog Feld "Öffnen"The Open Dialog
Das Dialog Feld Öffnen bietet Benutzern eine konsistente Möglichkeit, ein Element in einer Anwendung zu suchen und zu öffnen.The Open Dialog gives users a consistent way to find and open an item in an application. Verwenden Sie den folgenden Code, um ein Dialog Feld Öffnen in einer xamarin. Mac-Anwendung anzuzeigen:To display an Open Dialog in a Xamarin.Mac application, use the following code:
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = true;
dlg.CanChooseDirectories = false;
dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };
if (dlg.RunModal () == 1) {
// Nab the first file
var url = dlg.Urls [0];
if (url != null) {
var path = url.Path;
// Create a new window to hold the text
var newWindowController = new MainWindowController ();
newWindowController.Window.MakeKeyAndOrderFront (this);
// Load the text into the window
var window = newWindowController.Window as MainWindow;
window.Text = File.ReadAllText(path);
window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
window.RepresentedUrl = url;
}
}
Im obigen Code wird ein neues Dokument Fenster geöffnet, in dem der Inhalt der Datei angezeigt wird.In the above code, we are opening a new document window to display the contents of the file. Sie müssen diesen Code durch die Funktionalität ersetzen, die für Ihre Anwendung erforderlich ist.You'll need to replace this code with functionality is required by your application.
Die folgenden Eigenschaften sind beim Arbeiten mit einem verfügbar NSOpenPanel
:The following properties are available when working with a NSOpenPanel
:
- Canchoosefiles : gibt an, ob
true
der Benutzer Dateien auswählen kann.CanChooseFiles - Iftrue
the user can select files. - Canchoogdirectories : Wenn
true
der Benutzerverzeichnisse auswählen kann.CanChooseDirectories - Iftrue
the user can select directories. - Allowsmultipleselection : gibt
true
an, ob der Benutzer mehrere Dateien gleichzeitig auswählen kann.AllowsMultipleSelection - Iftrue
the user can select more than one file at a time. - Resolvealiases : Wenn
true
Sie und Alias auswählen, wird Sie in den Pfad der ursprünglichen Datei aufgelöst.ResolveAliases - Iftrue
selecting and alias, resolves it to the original file's path. - "" Ist ein Zeichen folgen Array von Dateitypen, die der Benutzer als Erweiterung oder " UTI" auswählen kann.AllowedFileTypes - Is a string array of file types that the user can select as either an extension or UTI. Der Standardwert ist
null
, wodurch eine beliebige Datei geöffnet werden kann.The default value isnull
, which allows any file to be opened.
Die RunModal ()
-Methode zeigt das Dialog Feld Öffnen an und ermöglicht dem Benutzer die Auswahl von Dateien oder Verzeichnissen (wie von den Eigenschaften angegeben) und gibt zurück, 1
Wenn der Benutzer auf die Schaltfläche Öffnen klickt.The RunModal ()
method displays the Open Dialog and allow the user to select files or directories (as specified by the properties) and returns 1
if the user clicks the Open button.
Im Dialog Feld Öffnen werden die ausgewählten Dateien oder Verzeichnisse des Benutzers als Array von URLs in der-Eigenschaft zurückgegeben URL
.The Open Dialog returns the user's selected files or directories as an array of URLs in the URL
property.
Wenn Sie das Programm ausführen und im Menü Datei das Element Öffnen auswählen, wird Folgendes angezeigt:If we run the program and select the Open... item from the File menu, the following is displayed:
Dialogfelder zum Drucken und Seite einrichtenThe Print and Page Setup Dialogs
macOS bietet Standard Dialogfelder für die Druck-und Seiten Einrichtung, die von Ihrer Anwendung angezeigt werden können, damit Benutzer in jeder verwendeten Anwendung über eine konsistente Druckfunktion verfügen können.macOS provides standard Print and Page Setup Dialogs that your application can display so that users can have a consistent printing experience in every application they use.
Der folgende Code zeigt das Standard Dialogfeld "Drucken":The following code will show the standard Print Dialog:
public bool ShowPrintAsSheet { get; set;} = true;
...
[Export ("showPrinter:")]
void ShowDocument (NSObject sender) {
var dlg = new NSPrintPanel();
// Display the print dialog as dialog box
if (ShowPrintAsSheet) {
dlg.BeginSheet(new NSPrintInfo(),this,this,null,new IntPtr());
} else {
if (dlg.RunModalWithPrintInfo(new NSPrintInfo()) == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to print the document here...",
MessageText = "Print Document",
};
alert.RunModal ();
}
}
}
Wenn ShowPrintAsSheet
Sie die-Eigenschaft auf festlegen false
, die Anwendung ausführen und das Dialogfeld Drucken anzeigen, wird Folgendes angezeigt:If we set the ShowPrintAsSheet
property to false
, run the application and display the print dialog, the following will be displayed:
Wenn Sie die ShowPrintAsSheet
-Eigenschaft auf festlegen true
, die Anwendung ausführen und das Dialogfeld Drucken anzeigen, wird Folgendes angezeigt:If set the ShowPrintAsSheet
property to true
, run the application and display the print dialog, the following will be displayed:
Im folgenden Code wird das Dialog Feld "Seiten Layout" angezeigt:The following code will display the Page Layout Dialog:
[Export ("showLayout:")]
void ShowLayout (NSObject sender) {
var dlg = new NSPageLayout();
// Display the print dialog as dialog box
if (ShowPrintAsSheet) {
dlg.BeginSheet (new NSPrintInfo (), this);
} else {
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to print the document here...",
MessageText = "Print Document",
};
alert.RunModal ();
}
}
}
Wenn die- ShowPrintAsSheet
Eigenschaft auf festgelegt false
ist, führen Sie die Anwendung aus, und zeigen Sie das Dialogfeld zum Drucken des Layouts an. Folgendes wird angezeigt:If we set the ShowPrintAsSheet
property to false
, run the application and display the print layout dialog, the following will be displayed:
Wenn Sie die ShowPrintAsSheet
-Eigenschaft auf festlegen true
, die Anwendung ausführen und das Dialogfeld "Drucklayout" anzeigen, wird Folgendes angezeigt:If set the ShowPrintAsSheet
property to true
, run the application and display the print layout dialog, the following will be displayed:
Weitere Informationen zum Arbeiten mit den Dialogfeldern "Drucken" und "Seite einrichten" finden Sie in der Dokumentation zu " nsprintpanel " und " nspagelayout " von Apple.For more information about working with the Print and Page Setup Dialogs, please see Apple's NSPrintPanel and NSPageLayout documentation.
Dialog Feld "Speichern"The Save Dialog
Das Dialog Feld "Speichern" bietet Benutzern eine konsistente Möglichkeit, ein Element in einer Anwendung zu speichern.The Save Dialog gives users a consistent way to save an item in an application.
Im folgenden Code wird das Standard Dialogfeld Speichern angezeigt:The following code will show the standard Save Dialog:
public bool ShowSaveAsSheet { get; set;} = true;
...
[Export("saveDocumentAs:")]
void ShowSaveAs (NSObject sender)
{
var dlg = new NSSavePanel ();
dlg.Title = "Save Text File";
dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };
if (ShowSaveAsSheet) {
dlg.BeginSheet(mainWindowController.Window,(result) => {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to save the document here...",
MessageText = "Save Document",
};
alert.RunModal ();
});
} else {
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to save the document here...",
MessageText = "Save Document",
};
alert.RunModal ();
}
}
}
Die- AllowedFileTypes
Eigenschaft ist ein Zeichen folgen Array von Dateitypen, die der Benutzer auswählen kann, um die Datei zu speichern.The AllowedFileTypes
property is a string array of file types that the user can select to save the file as. Der Dateityp kann entweder als eine Erweiterung oder eine " UTI" angegeben werden.The file type can be either specified as an extension or UTI. Der Standardwert ist null
, wodurch ein beliebiger Dateityp verwendet werden kann.The default value is null
, which allows any file type to be used.
Wenn ShowSaveAsSheet
Sie die-Eigenschaft auf festlegen false
, führen Sie die Anwendung aus, und wählen Sie im Menü Datei die Option Speichern unter... aus. Folgendes wird angezeigt:If we set the ShowSaveAsSheet
property to false
, run the application and select Save As... from the File menu, the following will be displayed:
Der Benutzer kann das Dialogfeld erweitern:The user can expand the dialog:
Wenn ShowSaveAsSheet
Sie die-Eigenschaft auf festlegen true
, führen Sie die Anwendung aus, und wählen Sie im Menü Datei die Option Speichern unter... aus. Folgendes wird angezeigt:If we set the ShowSaveAsSheet
property to true
, run the application and select Save As... from the File menu, the following will be displayed:
Der Benutzer kann das Dialogfeld erweitern:The user can expand the dialog:
Weitere Informationen zum Arbeiten mit dem Dialog Feld "Speichern" finden Sie in der Dokumentation zu nssavepanel von Apple.For more information on working with the Save Dialog, please see Apple's NSSavePanel documentation.
ZusammenfassungSummary
In diesem Artikel wurde erläutert, wie Sie mit modalen Fenstern, Blättern und den standardmäßigen System Dialogfeldern in einer xamarin. Mac-Anwendung arbeiten.This article has taken a detailed look at working with Modal Windows, Sheets and the standard system Dialog Boxes in a Xamarin.Mac application. Wir haben die verschiedenen Typen und Verwendungsmöglichkeiten von modalen Fenstern, Blättern und Dialogfeldern, das Erstellen und Verwalten von modalen Fenstern und Blättern in der Interface Builder von Xcode und das Arbeiten mit modalen Fenstern, Blättern und Dialogfeldern in c#-Code gesehen.We saw the different types and uses of Modal Windows, Sheets and Dialogs, how to create and maintain Modal Windows and Sheets in Xcode's Interface Builder and how to work with Modal Windows, Sheets and Dialogs in C# code.
Ähnliche ThemenRelated Links
- Macwindows (Beispiel)MacWindows (sample)
- Hello, Mac (Hallo Mac)Hello, Mac
- MenüsMenus
- WindowsWindows
- SymbolleistenToolbars
- Eingaberichtlinien für OS XOS X Human Interface Guidelines
- Einführung in WindowsIntroduction to Windows
- Einführung in BlätterIntroduction to Sheets
- Einführung in den DruckIntroduction to Printing