NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C# .NET Core 매크로 프로그램 만들기. (저장하기와 닫기)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 업무 자동화 매크로 프로그램은 사용자가 작성한 스크립트 또는 프로세스를 기록한 파일이 필요합니다. 1회성으로 끝나는 작업이 아니다보니 연속성을 가져야 합니다. 그리고, 매크로 스크립트뿐만 아니라 옵션, 콘피그, 단축키, 클라이언트 상태등등... 파일로 저장해야 할 것들이 많습니다. 지금까지 스크립트와 옵션을 만들었는데요. 이 둘은 모두 내용을 수정할 수 있고, 저장하고 불러와야 합니다.

     

    스크립트와 옵션은 서로 다른 내용을 가지지만, 프로그램 입장에서 보면 동일한 기능을 많이 공유하고 있습니다. 앞서 설계 및 디자인에서 언급했듯이 에디터에서의 스크립트와 옵션뿐만 아니라 플레이어에서도 동일한 디자인을 유지해야 합니다. 그래서, 공통적인 작업은 모두 Ai.Common.Client 라이브러리에서 처리하고 있습니다.

    jKcPnru.png

     

     

    에디터와 플레이어간 데이타를 통신하기 위해 인터페이스를 추가 했습니다. 인터페이스는 최상위의 클라이언트와 관련된 것들과, 컨트롤간 데이타 통신을 위한 Common 라이브러리의 인터페이스로 구분되어 있습니다. 인터페이스를 어떻게 구성해야 할지 고민이 많았는데요. 너무 시간을 오래 지체하기 보다는 일단 만들고 나중에 리팩토링하기로 했습니다. 엔지엠 매크로를 이미 1부터 6까지 만들어 오면서 생각해두었던 것들을 토대로요^^

     

    추가한 인터페이스는 IClose와 ISave입니다.

    aRlL2Oc.png

     

     

    ISave 인터페이스는 저장 기능을 구현하는 모든 컨트롤 또는 뷰가 구현해야 하는 필수 인터페이스입니다. 중요한 코드는 실제 구현부인데요. 이건 나중에 알아보죠^^

    namespace Ai.Interface
    {
        public interface ISave
        {
            void Save();
        }
    }

     

    IClose 인터페이스는 Close 메소드 하나와 IsChanged 속성 하나로 이루어져 있습니다.

    namespace Ai.Interface
    {
        public interface IClose
        {
            bool IsChanged { get; }
    
            void Close();
        }
    }

     

    스크립트는 내용을 편집하고, 저장해야 합니다. 그리고, 닫을 때 변경 내용을 잃어버리지 않도록 IsChanged로 변경점을 파악해야 합니다.

    public partial class Script : UserControl, IScriptView, ISave, IClose

     

    스크립트 저장 메소드의 구현은 간단합니다.

            public void Save()
            {
                SaveFileDialog dialog = new SaveFileDialog();
    
                if (dialog.ShowDialog() == DialogResult.OK)
                    new Ai.Common.File(dialog.FileName).Serialize(treeView.Nodes);
            }

     

    대부분의 작업은 공통 라이브러리에서 처리하고 있습니다. 이 부분은 나중에 변경될 내용이라서 지금 중요하지는 않습니다. 스크립트 편집기를 트리뷰에서 WPF의 Workflow로 만들 생각이거든요. 우선, 매크로가 갖춰야 할 대부분의 내용을 빠르게 개발하고 테스트하기 위해 일단은 TreeView로 스크립트 편집기를 구성했습니다.

     

    옵션은 내용이 좀 복잡합니다. 옵션에 속성에 추가될 때마다 동기화는 번거로움을 회피하기 위해서 Synchronization 메소드를 사용합니다. 이 메소드는 닷넷의 리플랙션을 이용해서 타입을 유추하고, 속성(Property)를 검사합니다. 공개 속성에 대해서 모델과 동기화 해줍니다.

            public void Save()
            {
                try
                {
                    Ai.Model.ModelManager.Synchronization(this, _optionModel);
                    Ai.Model.ModelManager.Save(_optionModel, Ai.Common.Environment.OptionFullName);
    
                    if (_client != null)
                        _client.Option = _optionModel;
    
                    MessageBox.Show(string.Format(_client.ResxMessage.GetString("SaveSuccessPH1"), 
                                    _client.ResxCaption.GetString("Option")), 
                                    "Information", 
                                    MessageBoxButtons.OK, 
                                    MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

     

    MessageBox는 옵션을 저장할 때 성공허거나 실패하는 내용을 알려줍니다. 이 부분은 각자 알아서 처리하면 될거 같아요. Option 콘트롤은 에디터와 플레이어의 디자인이 다르기 때문에 공통으로 사용하는 콘트롤입니다. 따라서, 인터페이스를 상속 받지 않았습니다. 옵션을 담는 그릇인 컨테이너에서 인터페이스를 만들고, 호출하도록 구성해야 합니다. 컨테이너는 에디터용과 플레이어용으로 나뉘어져 있습니다.

    LxoRDiA.png

     

     

    View는 에디터용이고, Popup은 플레이어용입니다. 엔지엠 매크로 6을 사용해보신 분들은 이 차이점을 이해할 수 있을겁니다. 에디터는 프로그램 내에서 모든 처리가 이루어집니다. 반면에 플레이어는 가볍게 만들기 위해 대부분의 뷰들이 숨겨져 있습니다. 사용자의 필요에 의해 Popup되는 방식입니다.

     

    컨테이너는 동적으로 옵션을 추가하고, 아래와 같이 인터페이스를 호출해줍니다.

    public bool IsChanged { get { return this._option.IsChanged; } }
    
    public void Close()
    {
        this._option.Close();
    }

     

    실제로 저장하는 기능과 닫을 때 변경 사항이 있는지 체크하고, 사용자에게 메세지를 표시하는건 위 메소드에서 구현해야 합니다. 각자 만드는 프로그램에 따라서 처리해야 할 내용들이 다를텐데요. 만약, TreeView를 사용한다면 메모리 릭을 방지하기 위해 TreeView에서 Dispose를 호출해줘야 합니다.

     

    크립톤 프레임워크를 사용하는 에디터에서 DockingManager의 PageCloseRequest 이벤트 핸들러를 연결하세요.

            private void DockingManager_PageCloseRequest(object? sender, CloseRequestEventArgs e)
            {
                var page = DockingManager.PageForUniqueName(e.UniqueName);
    
                if (page != null)
                {
                    _dockHelper.PageClosing(page);
                }
            }

     

    DockHelper 클래스의 PageClosing 메소드 내용은 아래와 같습니다. 여기서 IClose 인터페이스를 상속 받은 컨트롤들만 동작한다는걸 알 수 있습니다. 따라서, 뷰 또는 독이나 옵션과 같은 컨트롤을 닫을 때 꼭 IClose를 상속 받아서 구현해줘야 합니다. 그래야, 정상적으로 동작합니다. view는 IClose를 상속 받았기 때문에 Close 메소드에서 IsChanged 상태에 따라서 사용자에게 저장 후 닫을지 또는 저장하지 않고 닫을지 물어보는 로직을 추가해야 합니다.

            internal bool PageClosing(KryptonPage? page)
            {
                var view = page?.Controls.OfType<Ai.Interface.IClose>().FirstOrDefault();
    
                if (view != null)
                    view.Close();
    
                page?.Dispose();
                return true;
            }

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.