반응형

DevExpress의 DropDownButton을 사용하는 방법 입니다.

1. 도구상자에서 PopupMenu를 찾아서 폼에 추가

추가된 PopupMenu control의 우측상단 삼각형을 클릭한 후 Customise를 선택 한다.

만약 BarManager를 자동으로 생성하고 PopupMenu를 바인딩시킬 지 물어보면 Yes를 클릭한다.

PopupMenu Editor에서 메뉴를 Add 한다.

2. 도구상자에서 DropDownButton를 찾아서 폼에 추가

DropDownControl 속성 값으로 PopupMenu control을 선택한다.

DropDownButton 우측의 삼각표시 누르면 리스트가 나오는데 삼각표시 대신 버튼 누를때도 동일한 효과를 주려면 아래와 같이 Click 이벤트 처리 한다.

private void dropDownButton1_Click(object sender, EventArgs e) {
   dropDownButton1.ShowDropDown();
}

메뉴를 클릭 했을때 처리는 ItemClick 이벤트를 이용한다. 

만약 menu item 마다 개별 처리하지 않고 하나의 이벤트 핸들러에서 처리하려면 다음과 같이 ItemClickEventArgs 인자값의 Item을 이용하여 각 메뉴를 구분할 수 있다.

private void barButtonItem1_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) 
{
  Action<bool> dlg = null; 
 
  if (e.Item == barButtonItem1) 
    dlg = ShowDialog1;
  else if (e.Item == barButtonItem2) 
    dlg = ShowDialog2;
  else if (e.Item == barButtonItem3) 
    dlg = ShowDialog3; 
 
  dlg?.Invoke(false);
}
반응형

인터페이스 다중 상속 시 메소드명이 동일한 경우에는 해당 메소드는 명시적 구현 즉 '인터페이스명.메소드명' 형식으로 해야 합니다.

명시적 구현인 경우 private만 가능하기 때문에 클래스의 인스턴스로는 메소드 호출을 할 수 없고 인터페이스 타입으로 호출해야 합니다.

using System;
using System.Collections.Generic;
using System.Linq;

interface SamsungPhone {
    void TurnOn();
    void TurnOff();
    void Fold();
}

interface LGPhone {
    void TurnOn();
    void TurnOff();
    void Roll();
}

class IntegratedPhone: SamsungPhone, LGPhone {
    void SamsungPhone.TurnOn() { Console.WriteLine("Samsung Trun On"); }
    void SamsungPhone.TurnOff() { Console.WriteLine("Samsung Trun Off"); }
    void LGPhone.TurnOn() { Console.WriteLine("LG Trun On"); }
    void LGPhone.TurnOff() { Console.WriteLine("LG Trun Off"); }
    
    public void Fold() { Console.WriteLine("Folding"); }
    public void Roll() { Console.WriteLine("Rolling"); }
}

class MainClass {
    static void Main() {
        SamsungPhone samsung = new IntegratedPhone();
        samsung.TurnOn();
        samsung.Fold();
        
        LGPhone lg = new IntegratedPhone();
        lg.TurnOn();
        lg.Roll();
    }
}

실행결과

반응형

 

Electron으로 화면에 hello world를 표기하는 초간단 프로그램을 개발하는 과정입니다.
저처럼 Electron 초보자 수준에서 작성합니다.
또한 저는 윈도우즈 10 환경이므로 Mac이나 Linux에서는 일부 다를 수 있습니다.

1. Node.js 설치

Electron은 Node.js 기반으로 개발을 하기 때문에 제일 먼저 Node.js를 설치해야 합니다.
Node.js 공식 사이트에서 운영체체에 맞는 설치 파일을 내려받아 설치합니다.

nodejs.org/ko/

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

Current Version은 안정성에 문제가 있을 수 있으므로 가능한 LTS버전 설치를 권장합니다. 물론 Live환경이 아닌 학습 목적의 새로운 기능을 적극적으로 시험해 보려면 Current Version을 사용하면 됩니다.

2. package.json 만들기

cmd(terminal) 창에서 임의의 작업용 폴더를 생성하고 해당 폴더로 이동 후 npm init를 하면 초기 상태의 package.json 파일이 생성됩니다 (나오는 질문에 모두 enter를 치면 default value로 답변되어 작성됩니다). 

mkdir helloworld
cd helloworld
npm init

생성된 package.json은 다음과 같이 되어 있습니다.

package.json

3. Electron 설치하기

npm을 이용하여 electron을 설치합니다.

npm install electron --save-dev

4. index.html, index.js 작성 하기

index.html 파일

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello world</title>
</head>
<body>
<h1>Hello world</h1>
</body>
</html>

index.js 파일

const {app, BrowserWindow} = require("electron");

let win;

function createWindow() {
  win = new BrowserWindow({width: 800, height: 600});
  win.loadURL('file://' + __dirname + '/index.html');
  win.on("closed", () => {win = null;});
}

app.on("ready", createWindow);
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
  }
});

app.on("activate", () => {
  if (win === null) {
    createWindow();
  }
});

5. 작성된 프로그램 실행

cmd 창에서 작성된 프로그램을 실행합니다.

node_modules\.bin\electron .

이상 Electron으로 만들어 본 첫번째 저의 프로그램 작성기 였습니다.

 

 

반응형

■ Visual Studio 2010, SQL Server 2014 기준으로 개발

SQL 서버에 OpenAPI 호출하는 CLR을 등록하여, 거래처가 특정 조건을 만족하는지 검사하는 기능을 개발하게 되었는데 이 과정의 일부를 남깁니다.

CLR 프로젝트 생성

우선 Visual Studio에서 다음과 같이 SQL Server > Visual C# SQL CLR 데이터베이스 프로젝트를 시작합니다.

개발할 CLR이 SQL Sever에서 참조할 구성요소를 선택할 필요가 없으면 데이터베이스 참조는 취소해도 됩니다.

저의 경우 JSON을 처리하는 다른 DLL의 참조가 필요하여 DB 연결 정보를 입력하였습니다.
만약 연결된 DB 정보가 없이 참조 추가하려고 하면 다음과 같이 추가가 불가능합니다.

Visual Studio에서 기본적으로 생성되는 프로젝트 구성은 다음과 같습니다.

클래스 파일 추가

이제 실제 로직을 C#으로 구현하기 위하여 새로운 클래스 파일을 추가합니다 (이름은 NIMS.cs로 하였습니다).

추가된 빈 클래스 파일의 모습입니다.

API 스펙 확인

사용하려는 OpenAPI의 스펙은 다음과 같습니다.

JSON for. NET

위 OpenAPI의 호출 결과는 JSON으로 전달되기 때문에 JSON을 파싱 하기 위하여 JSON for. NET이라는 오픈 라이브러리를 사용합니다.

다음 사이트에서 파일을 내려받습니다.
sourceforge.net/projects/csjson/

내려받은 json-for-dotnet-1.2.zip 파일의 압축을 풀어서 System.Net.Json.dll 파일을 SQL 서버에 복사합니다.

제가 만든 CLR에서 이 System.Net.Json.dll을 참조해야 하므로 다음과 같이 SQL 서버에 어셈블리 등록을 합니다.

EXEC SP_CONFIGURE 'clr enabled', 1
RECONFIGURE WITH OVERRIDE 

ALTER DATABASE [MyDB] SET 
TRUSTWORTHY ON

CREATE ASSEMBLY Json
FROM 'D:\System.Net.Json.dll'    
WITH PERMISSION_SET = UNSAFE

그런데 JSON for. NET에서 제공해주는 기본 파서 기능이 좀 빈약하여, OpenAPI 결과를 처리할 수 있도록 기능 보완된 파서를 만들기 위하여 클래스 파일을 하나 더 추가합니다 (이름은 MyJsonParser.cs으로 했습니다).

그리고 다음과 같이 로직을 작성하였습니다.

MyJsonParser.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Json;

namespace NIMSAPI
{
    class MyJsonParser
    {
        private JsonTextParser parser;
        private JsonObjectCollection joc;

        public MyJsonParser(string planText = "")
        {
            parser = new JsonTextParser();
            if (!string.IsNullOrEmpty(planText))
            {
                joc = (JsonObjectCollection)parser.Parse(planText);
            }
        }

        // 생성자에서 주어진 초기 문자열 외에 추가적으로 파싱할 때 사용
        public void Parse(string planText)
        {
            planText = planText.Replace("\r", "").Replace("\n", "").Replace("\t", "");    // 파싱 오류방지를 위해 tab 및 개행문자열 제거

            joc = (JsonObjectCollection)parser.Parse(planText);
        }

        public object GetValue(string namePath, char split = '/', bool returnEmptyWhenNull = true)
        {
            string[] names = namePath.Split(split);
            JsonObjectCollection obj = null;

            if (names.Length == 1)
            {
                if (joc[namePath] is JsonObjectCollection)
                    obj = (JsonObjectCollection)joc[namePath];

                if (joc[namePath] is JsonArrayCollection)
                    obj = joc;
            }
            else
            {
                for (int i = 0; i < names.Length - 1; i++)
                {
                    if (i == 0)
                        obj = (JsonObjectCollection)joc[names[i]];
                    else
                        obj = (JsonObjectCollection)obj[names[i]];
                }
            }

            if (obj != null)
            {
                if (obj[names[names.Length - 1]] is JsonArrayCollection)
                    return obj[names[names.Length - 1]];
                else
                    return obj[names[names.Length - 1]].GetValue();
            }
            else
            {
                object retObj = null;

                if ((joc as JsonObjectCollection)[namePath] != null)
                {
                    retObj = (joc as JsonObjectCollection)[namePath].GetValue();
                }

                if (retObj == null && returnEmptyWhenNull)
                {
                    retObj = string.Empty;
                }

                return retObj;
            }
        }
    }
}

참조 추가

솔루션 탐색기에서 참조 추가를 눌러서 

앞에서 등록한 Json 어셈블리를 선택합니다.

정상적으로 추가되면 다음과 같이 참조 하위에 포함되게 됩니다.

이제 NIMS.cs에 OpenAPI를 호출하고 결과를 MyJsonParser로 파싱 처리하는 소스를 작성하였습니다.

NIMS.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.Net.Json;

namespace NIMSAPI
{
    public class NIMS
    {
        // 상대 마약류취급자식별번호가 존재하는지 체크
        //      BizNo: 매출거래처의 사업자번호
        public static bool IsExistsBsshCode(string BizNo)
        {
            string Url = "https://www.nims.or.kr/api/bsshinfo.do";
            bool result = false;

            try
            {
                WebClient webClient = new WebClient();

                NameValueCollection formData = new NameValueCollection();
                formData["k"] = "XXXXXXXXX"; //인증키
                formData["fg"] = "1";
                formData["pg"] = "1";
                formData["bi"] = BizNo;

                byte[] responseBytes = webClient.UploadValues(Url, "POST", formData);
                string Result = Encoding.UTF8.GetString(responseBytes);

                MyJsonParser myParser = new MyJsonParser(Result);

                string ResultCode = myParser.GetValue("response/header/RESULT_CODE").ToString();
                int CustCount = int.Parse(myParser.GetValue("response/body/TOTAL_COUNT").ToString());

                JsonArrayCollection lists = (JsonArrayCollection)myParser.GetValue("response/body/list"); 

                int count = 0;
                foreach (JsonObjectCollection collection in lists)
                {
                    myParser.Parse(collection.ToString());  // 배열 요소를 다시 파싱 시킨다

                    string OppNarcoticManagerID = myParser.GetValue("BSSH_CD").ToString();        // 상대 마약류취급자식별번호
                    result = !string.IsNullOrEmpty(OppNarcoticManagerID);

                    if (++count == 1) break;    // 동일 거래처가 여러개 조회되는 경우가 있어서 한 번만 처리되게 함
                }
            }
            catch
            {
                result = false;
            }

            return result;
        }
    }

}

F6을 눌러서 빌드합니다.

빌드된 NIMSAPI.dll 파일을 SQL 서버에 복사하고 어셈블리 등록을 합니다.

CREATE ASSEMBLY NIMS
FROM 'D:\NIMSAPI.dll'    
WITH PERMISSION_SET = UNSAFE

참고로 다음과 같이 소유자 SID 문제로 어셈블리 등록이 되지 않는 경우 ALTER AUTHORIZATION로 해당 DB의 소유자를 변경해야 합니다.

/*
master 데이터베이스에 기록된 데이터베이스 소유자 SID가 데이터베이스 'MyDB'에 기록된 
데이터베이스 소유자 SID와 다릅니다. 
ALTER AUTHORIZATION 문을 사용하여 데이터베이스 'MyDB'의 소유자를 다시 설정하여 
이 문제를 해결해야 합니다.
*/

-- 현 소유자 확인
SELECT name AS [Database],
       SUSER_SNAME(owner_sid) AS [Owner]
FROM sys.databases
WHERE [name] = 'MyDB'

-- 소유자 변경
ALTER AUTHORIZATION ON DATABASE:: MyDB TO sa ;

사용자 함수 생성

등록된 CLR 어셈블리를 SQL 서버에서 함수 형태로 호출할 수 있도록 함수를 생성합니다.

CREATE FUNCTION FN_NIMS_CUST_EXISTS
(
    @p_BizNo NVARCHAR(20)
)
RETURNS BIT AS EXTERNAL NAME NIMS.[NIMSAPI.NIMS].IsExistsBsshCode


테스트

이 글이 도움이 되셨으면 공감(하트)과 댓글을 부탁 드려요!

+ Recent posts