Xamarin.Mac의 대화 상자

Xamarin.Mac 애플리케이션에서 C# 및 .NET을 사용하는 경우 개발자와 Xcode에서 작업하는 것과 동일한 대화 상자 및 모달 Windows에 Objective-C 액세스할 수 있습니다. Xamarin.Mac은 Xcode와 직접 통합되므로 Xcode의 인터페이스 작성기를 사용하여 모달 Windows를 만들고 기본(또는 필요에 따라 C# 코드에서 직접 만들기)할 수 있습니다.

대화 상자는 사용자 작업에 대한 응답으로 나타나며 일반적으로 사용자가 작업을 완료할 수 있는 방법을 제공합니다. 대화 상자를 닫기 전에 사용자의 응답이 필요합니다.

Windows는 모덜리스 상태(예: 여러 문서를 한 번에 열 수 있는 텍스트 편집기) 또는 모달(예: 애플리케이션을 계속하기 전에 해제해야 하는 내보내기 대화 상자)에서 사용할 수 있습니다.

An open dialog box

이 문서에서는 Xamarin.Mac 애플리케이션에서 Dialogs 및 Modal Windows를 사용하는 기본 사항을 설명합니다. 이 문서에서 사용할 주요 개념과 기술을 다루므로 Hello, Mac 문서, 특히 Xcode 및 인터페이스 작성기 및 콘센트 및 작업 소개 섹션을 통해 작업하는 것이 좋습니다.

Xamarin.Mac Internals 문서의 섹션에 Objective-C 대한 C# 클래스/메서드 노출을 살펴보고, Register C# 클래스 Objective-C 를 개체 및 UI 요소에 연결하는 데 사용되는 명령과 Export 설명합니다.

대화 상자 소개

사용자 작업(예: 파일 저장)에 대한 응답으로 대화 상자가 나타나고 사용자가 해당 작업을 완료할 수 있는 방법을 제공합니다. 대화 상자를 닫기 전에 사용자의 응답이 필요합니다.

Apple에 따르면 다음과 같은 세 가지 방법으로 대화 상자를 표시할 수 있습니다.

  • 문서 모달 - 문서 모달 대화 상자는 사용자가 해제될 때까지 지정된 문서 내에서 다른 작업을 수행하지 못하게 합니다.
  • 앱 모달 - 앱 모달 대화 상자는 사용자가 해제될 때까지 애플리케이션과 상호 작용하지 못하게 합니다.
  • 모덜리스 모덜리스 대화 상자를 사용하면 사용자가 문서 창과 상호 작용하면서 대화 상자의 설정을 변경할 수 있습니다.

모든 표준 NSWindow 은 모듈식으로 표시하여 사용자 지정된 대화 상자로 사용할 수 있습니다.

An example modal window

문서 모달 대화 상자 시트

시트는 지정된 문서 창에 연결된 모달 대화 상자로, 사용자가 대화 상자를 해제할 때까지 창과 상호 작용할 수 없습니다. 시트가 나타나는 창에 첨부되며 한 번에 하나의 시트만 창에 열 수 있습니다.

An example modal sheet

기본 설정 창

기본 설정 창은 사용자가 자주 변경하지 않는 애플리케이션의 설정을 포함하는 모덜리스 대화 상자입니다. 기본 설정 Windows에는 사용자가 여러 설정 그룹 간에 전환할 수 있는 도구 모음이 포함되어 있는 경우가 많습니다.

An example preference window

대화 상자 열기

열기 대화 상자는 사용자에게 애플리케이션에서 항목을 찾고 여는 일관된 방법을 제공합니다.

A open dialog box

macOS는 사용자가 사용하는 모든 애플리케이션에서 일관된 인쇄 환경을 가질 수 있도록 애플리케이션에서 표시할 수 있는 표준 인쇄 및 페이지 설정 대화 상자를 제공합니다.

인쇄 대화 상자는 모두 자유 부동 대화 상자로 표시될 수 있습니다.

A print dialog box

또는 시트로 표시할 수 있습니다.

A print sheet

페이지 설정 대화 상자는 모두 자유 부동 대화 상자로 표시될 수 있습니다.

A page setup dialog

또는 시트로 표시할 수 있습니다.

A page setup sheet

대화 상자 저장

저장 대화 상자는 사용자에게 애플리케이션에서 항목을 저장하는 일관된 방법을 제공합니다. 저장 대화 상자에는 최소(축소됨이라고도 함)의 두 가지 상태가 있습니다.

A save dialog

확장된 상태:

An expanded save dialog

최소 저장 대화 상자를 시트로 표시할 수도 있습니다.

A minimal save sheet

확장된 저장 대화 상자는 다음과 같습니다.

An expanded save sheet

자세한 내용은 Apple OS X 휴먼 인터페이스 지침의 대화 상자 섹션을 참조하세요.

프로젝트에 모달 창 추가

기본 문서 창 외에도 Xamarin.Mac 애플리케이션은 기본 설정 또는 검사기 패널과 같은 다른 유형의 창을 사용자에게 표시해야 할 수 있습니다.

새 창을 추가하려면 다음을 수행합니다.

  1. 솔루션 탐색기 Xcode의 인터페이스 작성기에서 편집할 파일을 엽니다Main.storyboard.

  2. 뷰 컨트롤러 를 디자인 화면으로 끌어옵니다.

    Selecting a View Controller from the Library

  3. ID 검사에서 클래스 이름을 입력 CustomDialogController합니다.

    Setting the class name to CustomDialogController.

  4. Mac용 Visual Studio 다시 전환하여 Xcode와 동기화하고 파일을 만들 CustomDialogController.h 수 있습니다.

  5. Xcode로 돌아가서 인터페이스를 디자인합니다.

    Designing the UI in Xcode

  6. 대화 상자를 대화 상자 창으로 여는 UI 요소에서 컨트롤을 끌어 앱의 주 창에서 새 뷰 컨트롤러로 모달 Segue를 만듭니다. 식별자 ModalSegue할당:

    A modal segue

  7. 모든 작업콘센트를 연결합니다.

    Configuring an Action

  8. 변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.

CustomDialogController.cs 파일을 다음과 같이 표시합니다.

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
    }
}

이 코드는 제목과 대화에 대한 설명을 설정하는 몇 가지 속성과 취소되거나 수락되는 대화에 반응할 몇 가지 이벤트를 표시합니다.

다음으로 파일을 편집하고 ViewController.cs 메서드를 재정의 PrepareForSegue 한 다음 다음과 같이 표시합니다.

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;
    }
}

이 코드는 Xcode의 인터페이스 작성기에서 정의한 segue를 대화 상자로 초기화하고 제목 및 설명을 설정합니다. 또한 사용자가 대화 상자에서 선택한 항목도 처리합니다.

애플리케이션을 실행하고 사용자 지정 대화 상자를 표시할 수 있습니다.

An example dialog

Xamarin.Mac 애플리케이션에서 창을 사용하는 방법에 대한 자세한 내용은 Windows 작업 설명서를 참조하세요.

사용자 지정 시트 만들기

시트는 지정된 문서 창에 연결된 모달 대화 상자로, 사용자가 대화 상자를 해제할 때까지 창과 상호 작용할 수 없습니다. 시트가 나타나는 창에 첨부되며 한 번에 하나의 시트만 창에 열 수 있습니다.

Xamarin.Mac에서 사용자 지정 시트를 만들려면 다음을 수행해 보겠습니다.

  1. 솔루션 탐색기 Xcode의 인터페이스 작성기에서 편집할 파일을 엽니다Main.storyboard.

  2. 뷰 컨트롤러 를 디자인 화면으로 끌어옵니다.

    Selecting a View Controller from the Library

  3. 사용자 인터페이스를 디자인합니다.

    The UI design

  4. 주 창에서 새 뷰 컨트롤러로 시트 Segue를 만듭니다.

    Selecting the Sheet segue type

  5. ID 검사에서 뷰 컨트롤러의 클래스SheetViewController 이름을 지정합니다.

    Setting the class name to SheetViewController.

  6. 필요한 콘센트 및 작업을 정의합니다.

    Defining the required Outlets and Actions

  7. 변경 내용을 저장하고 동기화할 Mac용 Visual Studio 돌아갑니다.

다음으로 파일을 편집 SheetViewController.cs 하고 다음과 같이 표시합니다.

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
    }
}

그런 다음 파일을 편집 ViewController.cs 하고, 메서드를 PrepareForSegue 편집하고, 다음과 같이 표시합니다.

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;
    }
}

애플리케이션을 실행하고 시트를 열면 해당 시트가 창에 연결됩니다.

An example sheet

기본 설정 대화 상자 만들기

인터페이스 작성기에서 기본 설정 보기를 배치하기 전에 기본 설정 전환을 처리하기 위해 사용자 지정 segue 형식을 추가해야 합니다. 프로젝트에 새 클래스를 추가하고 호출합니다 ReplaceViewSeque. 클래스를 편집하고 다음과 같이 표시합니다.

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

    }

}

사용자 지정 segue를 만들면 Xcode의 인터페이스 작성기에서 기본 설정을 처리하는 새 창을 추가할 수 있습니다.

새 창을 추가하려면 다음을 수행합니다.

  1. 솔루션 탐색기 Xcode의 인터페이스 작성기에서 편집할 파일을 엽니다Main.storyboard.

  2. 창 컨트롤러 를 디자인 화면으로 끌어옵니다.

    Select a Window Controller from the Library

  3. 메뉴 모음 디자이너 근처에서 창을 정렬합니다.

    Adding the new Window

  4. 기본 설정 보기에 탭이 있으므로 연결된 뷰 컨트롤러의 복사본을 만듭니다.

    Adding the required View Controllers

  5. 라이브러리에서 새 도구 모음 컨트롤러끕니다.

    Select a Toolbar Controller from the Library

  6. 디자인 화면의 창에 놓습니다.

    Adding a new Toolbar Controller

  7. 도구 모음의 디자인을 레이아웃합니다.

    Layout the toolbar

  8. Control 키를 누른 채로 각 도구 모음 단추 에서 위에서 만든 보기로 끌어다 옵니다. 사용자 지정 segue 유형을 선택합니다.

    Setting a Custom segue type.

  9. 새 Segue를 선택하고 클래스다음으로 ReplaceViewSegue설정합니다.

    Setting the segue class

  10. 디자인 화면의 메뉴 모음 디자이너에서 애플리케이션 메뉴에서 기본 설정을 선택합니다. 컨트롤을 클릭하고 기본 설정 창으로 끌어서 표시 segue를 만듭니다.

    Setting the segue type by dragging Preferences to the Preferences Window.

  11. 변경 내용을 저장하고 동기화할 Mac용 Visual Studio 돌아갑니다.

코드를 실행하고 애플리케이션 메뉴에서 기본 설정을 선택하면 창이 표시됩니다.

An example preferences window displaying the word Profile.

Windows 및 도구 모음 작업에 대한 자세한 내용은 Windows도구 모음 설명서를 참조하세요.

기본 설정 저장 및 로드

일반적인 macOS 앱에서는 사용자가 앱의 사용자 기본 설정을 변경하면 해당 변경 내용이 자동으로 저장됩니다. Xamarin.Mac 앱에서 이를 처리하는 가장 쉬운 방법은 단일 클래스를 만들어 사용자의 모든 기본 설정을 관리하고 시스템 전체에서 공유하는 것입니다.

먼저 프로젝트에 새 AppPreferences 클래스를 추가하고 상속합니다 NSObject. 기본 설정은 데이터 바인딩 및 키-값 코딩을 사용하도록 설계되어 기본 설정 양식을 훨씬 간단하게 만들고 기본 만드는 프로세스를 만듭니다. 기본 설정은 소량의 간단한 데이터 형식으로 구성되므로 기본 제공 NSUserDefaults 을 사용하여 값을 저장하고 검색합니다.

AppPreferences.cs 파일을 편집하고 다음과 같이 표시합니다.

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
    }
}

이 클래스에는 작업을 더 쉽게 수행할 수 있도록 NSUserDefaults , LoadIntSaveColor, LoadColor등과 같은 SaveInt몇 가지 도우미 루틴이 포함되어 있습니다. NSUserDefaults 또한 처리 NSColorsNSColorToHexString 할 수 있는 기본 제공 방법이 없으므로 색을 쉽게 저장하고 NSColorFromHexString 검색할 수 있는 AA 웹 기반 16진수 문자열(#RRGGBBAA알파 투명도)로 변환하는 데 사용됩니다.

AppDelegate.cs 파일에서 앱 전체에서 사용할 AppPreferences 개체의 인스턴스를 만듭니다.

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
        
        ...

기본 설정 보기에 배선 기본 설정

다음으로, 위에서 만든 기본 설정 창 및 뷰의 UI 요소에 기본 설정 클래스를 연결합니다. 인터페이스 작성기에서 기본 설정 보기 컨트롤러를 선택하고 ID 검사기로 전환하고 컨트롤러에 대한 사용자 지정 클래스를 만듭니다.

The Identity Inspector

Mac용 Visual Studio 다시 전환하여 변경 내용을 동기화하고 새로 만든 클래스를 열어 편집합니다. 클래스를 다음과 같이 표시합니다.

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
    }
}

이 클래스는 여기서 두 가지 작업을 수행했습니다. 첫째, AppDelegate에 더 쉽게 액세스할 수 있도록 도우미 App 속성이 있습니다. 둘째, 이 속성은 Preferences 이 보기에 배치된 모든 UI 컨트롤을 사용하여 데이터 바인딩에 대한 전역 AppPreferences 클래스를 노출합니다.

그런 다음 Storyboard 파일을 두 번 클릭하여 인터페이스 작성기에서 다시 엽니다(위에서 변경한 내용을 확인). 기본 설정 인터페이스를 빌드하는 데 필요한 UI 컨트롤을 뷰로 끌어옵니다. 각 컨트롤에 대해 바인딩 검사기로 전환하고 AppPreference 클래스의 개별 속성에 바인딩합니다.

The Binding Inspector

필요한 모든 패널(컨트롤러 보기) 및 기본 설정 속성에 대해 위의 단계를 반복합니다.

열려 있는 모든 창에 기본 설정 변경 내용 적용

위에서 설명한 것처럼 일반적인 macOS 앱에서 사용자가 앱의 사용자 기본 설정을 변경하면 해당 변경 내용이 자동으로 저장되고 사용자가 애플리케이션에서 열었을 수 있는 모든 창에 적용됩니다.

앱의 기본 설정과 창을 신중하게 계획하고 디자인하면 최소한의 코딩 작업으로 이 프로세스를 최종 사용자에게 원활하고 투명하게 수행할 수 있습니다.

앱 기본 설정을 사용하는 모든 창의 경우 AppDelegate에 더 쉽게 액세스할 수 있도록 콘텐츠 보기 컨트롤러에 다음 도우미 속성을 추가합니다.

#region Application Access
public static AppDelegate App {
    get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion

다음으로, 클래스를 추가하여 사용자의 기본 설정에 따라 콘텐츠 또는 동작을 구성합니다.

public void ConfigureEditor() {

    // General Preferences
    TextEditor.AutomaticLinkDetectionEnabled = App.Preferences.SmartLinks;
    TextEditor.AutomaticQuoteSubstitutionEnabled = App.Preferences.SmartQuotes;
    ...

}

창이 처음 열릴 때 구성 메서드를 호출하여 사용자의 기본 설정을 준수하는지 확인해야 합니다.

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // Configure editor from user preferences
    ConfigureEditor ();
    ...
}

다음으로 파일을 편집 AppDelegate.cs 하고 다음 메서드를 추가하여 열려 있는 모든 창에 기본 설정 변경 내용을 적용합니다.

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 ();
        }
    }

}

다음으로, 프로젝트에 클래스를 추가하고 PreferenceWindowDelegate 다음과 같이 표시합니다.

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
    }
}

이렇게 하면 기본 설정 창이 닫힐 때 열려 있는 모든 Windows에 기본 설정 변경 내용이 전송됩니다.

마지막으로 기본 설정 창 컨트롤러를 편집하고 위에서 만든 대리자를 추가합니다.

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
    }
}

이러한 모든 변경 내용이 적용되면 사용자가 앱의 기본 설정을 편집하고 기본 설정 창을 닫으면 열려 있는 모든 Windows에 변경 내용이 적용됩니다.

An example Preferences Window, displayed with several other open windows.

열기 대화 상자

열기 대화 상자는 사용자에게 애플리케이션에서 항목을 찾고 여는 일관된 방법을 제공합니다. Xamarin.Mac 애플리케이션에서 열기 대화 상자를 표시하려면 다음 코드를 사용합니다.

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;

    }
}

위의 코드에서는 파일의 내용을 표시하는 새 문서 창을 엽니다. 이 코드를 애플리케이션에 필요한 기능으로 바꾸어야 합니다.

다음 속성은 다음으로 작업할 때 사용할 수 있습니다.NSOpenPanel

  • CanChooseFiles - 사용자가 파일을 선택할 수 있는 경우 true
  • CanChooseDirectories - 사용자가 디렉터리를 선택할 수 있는 경우 true
  • AllowsMultipleSelection - 사용자가 한 번에 둘 이상의 파일을 선택할 수 있는 경우 true
  • ResolveAliases - 선택하고 별칭을 지정하는 경우 true 원래 파일의 경로로 확인합니다.
  • AllowedFileTypes - 사용자가 확장명 또는 UTI로 선택할 수 있는 파일 형식의 문자열 배열입니다. 기본값은 null파일을 열 수 있는 값입니다.

이 메서드는 RunModal () 열기 대화 상자를 표시하고 사용자가 파일 또는 디렉터리(속성에 지정된 대로)를 선택할 수 있도록 허용하고 사용자가 열기 단추를 클릭하면 반환 1 됩니다.

열기 대화 상자는 사용자가 선택한 파일 또는 디렉터리를 속성의 URL URL 배열로 반환합니다.

프로그램을 실행하고 파일 메뉴에서 열기... 항목을 선택하면 다음이 표시됩니다.

An open dialog box

인쇄 및 페이지 설정 대화 상자

macOS는 사용자가 사용하는 모든 애플리케이션에서 일관된 인쇄 환경을 가질 수 있도록 애플리케이션에서 표시할 수 있는 표준 인쇄 및 페이지 설정 대화 상자를 제공합니다.

다음 코드는 표준 인쇄 대화 상자를 보여 줍니다.

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 ();
        }
    }
}

속성을 설정 ShowPrintAsSheet 하여 false애플리케이션을 실행하고 인쇄 대화 상자를 표시하면 다음이 표시됩니다.

A print dialog box

속성을 설정 ShowPrintAsSheet 하여 true애플리케이션을 실행하고 인쇄 대화 상자를 표시하면 다음이 표시됩니다.

A print sheet

다음 코드는 페이지 레이아웃 대화 상자를 표시합니다.

[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 ();
        }
    }
}

속성을 설정 ShowPrintAsSheet 하여 false애플리케이션을 실행하고 인쇄 레이아웃 대화 상자를 표시하면 다음이 표시됩니다.

A page setup dialog

속성을 설정 ShowPrintAsSheet 하여 true애플리케이션을 실행하고 인쇄 레이아웃 대화 상자를 표시하면 다음이 표시됩니다.

A page setup sheet

인쇄 및 페이지 설정 대화 상자 작업에 대한 자세한 내용은 Apple의 NSPrintPanelNSPageLayout 설명서를 참조하세요.

저장 대화 상자

저장 대화 상자는 사용자에게 애플리케이션에서 항목을 저장하는 일관된 방법을 제공합니다.

다음 코드는 표준 저장 대화 상자를 보여 줍니다.

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 ();
        }
    }

}

AllowedFileTypes 속성은 사용자가 파일을 저장하기 위해 선택할 수 있는 파일 형식의 문자열 배열입니다. 파일 형식을 확장 프로그램 또는 UTI로 지정할 수 있습니다. 기본값은 null모든 파일 형식을 사용할 수 있는 값입니다.

속성을 설정 ShowSaveAsSheet 하여 false애플리케이션을 실행하고 파일 메뉴에서 다른 이름으로 저장...을 선택하면 다음이 표시됩니다.

A save dialog box

사용자는 대화 상자를 확장할 수 있습니다.

An expanded save dialog box

속성을 설정 ShowSaveAsSheet 하여 true애플리케이션을 실행하고 파일 메뉴에서 다른 이름으로 저장...을 선택하면 다음이 표시됩니다.

A save sheet

사용자는 대화 상자를 확장할 수 있습니다.

An expanded save sheet

저장 대화 상자 작업에 대한 자세한 내용은 Apple의 NSSavePanel 설명서를 참조하세요.

요약

이 문서에서는 Xamarin.Mac 애플리케이션에서 모달 Windows, 시트 및 표준 시스템 대화 상자 작업을 자세히 살펴보았습니다. 모달 Windows, 시트 및 대화 상자의 다양한 유형과 사용, Xcode의 인터페이스 작성기에서 모달 Windows 및 시트를 만들고 기본 방법 및 C# 코드의 모달 Windows, 시트 및 대화 상자로 작업하는 방법을 알아보았습니다.