2015. 4. 17. 15:07




요구사항:

예) 외출 시간이 10:00 ~ 14:00 인 경우, 원래 4시간 이나, 점심시간 (12:00 ~ 13:00) 이 포함되면 해당 범위만큼 제외한 시간을 산정하도록 한다.


입력은 분단위 환산된 정수 값이다.


한계)

- 당일 시간단위의 계산에 한함. 일자가 변경되는 것 까지 고려하지는 않는다.


        /* -----------------------------
         * 2015.04.17
         * Haebi, http://haebi.kr
         * ----------------------------- */
        /// <summary>
        /// 특정 시간간격을 제외한 총 분단위 간격을 구한다.
        /// </summary>
        /// <param name="fromMin">시작분</param>
        /// <param name="toMin">종료분</param>
        /// <param name="exFromMin">제외시작분</param>
        /// <param name="exToMin">제외종료분</param>
        /// <returns></returns>
        private int checkDurMinutes(int fromMin , int toMin, int exFromMin, int exToMin)
        {
            if (toMin < fromMin) throw new Exception("checkDurMinutes() : 종료시간이 시작시간보다 작을 수 없습니다.");
            if (exToMin < exFromMin) throw new Exception("checkDurMinutes() : 제외종료시간이 제외시작시간보다 작을 수 없습니다.");

            /**
             * 제외시킬 기간과 겹쳐질수 있는 4가지 케이스에 대하여 처리하고 있으며,
             * 해당없는 경우(겹쳐지지 않는경우) 그냥 to - from 처리로 끝난다.
             */
            int _retMin = 0;

            try
            {
                // -----<------|---------------|------>------
                if (fromMin <= exFromMin && toMin >= exToMin)
                {
                    _retMin = toMin - fromMin - (exToMin - exFromMin);
                }
                // ------------|---<------>----|-------------
                else if (fromMin >= exFromMin && toMin <= exToMin)
                {
                    _retMin = 0;
                }
                // ------<-----|---------->----|-------------
                else if ((fromMin <= exFromMin)
                    && (toMin >= exFromMin && toMin <= exToMin))
                {
                    _retMin = exFromMin - fromMin;
                }
                // ------------|----------<----|------>------
                else if ((fromMin >= exFromMin && fromMin <= exToMin)
                    && toMin >= exToMin)
                {
                    _retMin = toMin - exToMin;
                }
                // ---<----->--|---------------|-------------
                // ------------|---------------|---<----->---
                else
                {
                    _retMin = toMin - fromMin;
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

            return _retMin;
        }




그리고 검증을 위한 테스트 코드

               int min1 = 600; // 10:00
                int min2 = 630; // 10:30
                int dmin = 0;

                for (int i = 0; i < 300;i++)
                {
                    min2++; dmin = checkDurMinutes(min1, min2, 720, 780); printLog(i + 1, dmin, min1, min2);
                }



printLog() 부분

        private void printLog(int count, int dmin, int min1, int min2)
        {
            // 테스트 결과 출력 (단위:분)
            // 테스트 넘버    from ~ to 기간    from 분    to 분
            textBox3.Text = textBox3.Text + "\r\n" + count.ToString() + "\t"
                + string.Format("{0:D2}", (dmin / 60)) + ":" + string.Format("{0:D2}", (dmin % 60))
                + "\t" + string.Format("{0:D2}", (min1 / 60)) + ":" + string.Format("{0:D2}", (min1 % 60))
                + "\t" + string.Format("{0:D2}", (min2 / 60)) + ":" + string.Format("{0:D2}", (min2 % 60));
        }

- textBox3 하나 추가해 주면 된다.




테스트 결과(샘플)

카운트 - (외출)시간 - 시작시간 - 종료시간

88    01:58    10:00    11:58
89    01:59    10:00    11:59
90    02:00    10:00    12:00

91    02:00    10:00    12:01
92    02:00    10:00    12:02
93    02:00    10:00    12:03

...(생략)
148    02:00    10:00    12:58
149    02:00    10:00    12:59
150    02:00    10:00    13:00

151    02:01    10:00    13:01
152    02:02    10:00    13:02
153    02:03    10:00    13:03

- 1분단위로 종료시간을 더해가면서 실제 반영되는 시간이 얼마인지를 확인 한다.

- 테스트는 720(12:00) ~ 780(13:00) 까지의 범위에 대해 계산을 하지 않는 것으로 설정하였다.




Posted by 해비
2015. 4. 10. 09:41

Datatable 사용해서 처리하는 구문이 있었는데 의도하지 않은 방향으로 흘러가 버렸네요...


일단 의도는 다음과 같습니다.


1. 테이블에서 Select 하여 대상 데이터를 선택합니다.

2. 원 테이블을 클리어 시킵니다.

3. 선택된 데이터를 집어 넣습니다.



위 의도대로 아래와 같이 코드를 작성하였습니다

            // 테이블 정의
            DataTable srcDT = new DataTable();
            srcDT.Columns.Add("A1");
            srcDT.Columns.Add("A2");
            srcDT.Columns.Add("A3");
            srcDT.Columns.Add("A4");

            // 데이터 입력
            srcDT.Rows.Add(new object[] { "AAA", "111", "A11", "1AA" });
            srcDT.Rows.Add(new object[] { "BBB", "222", "B11", "1BB" });
            srcDT.Rows.Add(new object[] { "CCC", "333", "C11", "1CC" });

            // 커밋
            srcDT.AcceptChanges();

            // 쿼리
            DataRow[] fDR = srcDT.Select("A1 like '" + "A" + "%'");

            srcDT.Clear();
            foreach (DataRow dr in fDR)
            {
                srcDT.Rows.Add(dr);
            }

            for (int i = 0; i < srcDT.Rows.Count; i++)
            {
                textBox1.AppendText(srcDT.Rows[i][0].ToString() + " | "
                    + srcDT.Rows[i][1] + " | "
                    + srcDT.Rows[i][2] + " | "
                    + srcDT.Rows[i][3] + " | " + "\r\n");
            }


이 소스코드가 잘 돌아갈 것 같이 보입니까?



여기에는 치명적인 문제가 있습니다.

원 테이블을 클리어 시켜 버렸다는 것.


선택된 목록만을 가져오기 위해서 Clear 후 다시 선택된 값을 집어 넣고 있지만...;;


정작 빈 껍데기 row만 들어갑니다.


Select 할 때, 값을 복사해 오는것이 아니고 해당 데이터의 위치주소 값을 가져오는 듯 합니다.

그래서, 테이블 클리어 때 값이 다 지워졌고, 해당 주소값의 row를 다시 추가해 봤자 이미 다 지워진 빈 데이터 만 추가될 뿐이고...




이런 경우 제가 찾은 해결책은 2가지 입니다.


1번째는 목록을 not like 로 가져 온 다음, 해당 하는 행을 remove 로 지워 나가는 것.

2번째는 like 로 가져온 행을 새 테이블에 넣어주는 방법 (넣기 전에 원 테이블이 Clear 되면 안됩니다.)



1번째 소스코드

            // 쿼리
            DataRow[] fDR = srcDT.Select("A1 not like '" + "A%" + "%'");

            //srcDT.Clear();
            foreach (DataRow dr in fDR)
            {
                srcDT.Rows.Remove(dr);
            }

            for (int i = 0; i < srcDT.Rows.Count; i++)
            {
                textBox1.AppendText(srcDT.Rows[i][0].ToString() + " | "
                    + srcDT.Rows[i][1] + " | "
                    + srcDT.Rows[i][2] + " | "
                    + srcDT.Rows[i][3] + " | " + "\r\n");
            } 




2번째 소스코드

            // 쿼리
            DataRow[] fDR = srcDT.Select("A1 like '" + "A" + "%'");

            //srcDT.Clear();
            DataTable srcDT2 = new DataTable();
            srcDT2 = srcDT.Clone();
            foreach (DataRow dr in fDR)
            {
                srcDT2.Rows.Add(dr.ItemArray);
            }

            for (int i = 0; i < srcDT2.Rows.Count; i++)
            {
                textBox1.AppendText(srcDT2.Rows[i][0].ToString() + " | "
                    + srcDT2.Rows[i][1] + " | "
                    + srcDT2.Rows[i][2] + " | "
                    + srcDT2.Rows[i][3] + " | " + "\r\n");
            }






Posted by 해비
2015. 3. 15. 23:59


비트 연산자


&

 AND 연산

|

 OR 연산

^

 XOR 연산

~

 비트 반전 0 <-> 1

<<

 왼쪽으로 비트열 이동

>>

 *오른쪽으로 비트열 이동

* 음수의 경우 오른쪽으로 비트열 이동시 MSB가 바뀌어지는 경우가 있으므로 주의.




AND 연산

둘 다 1이면 1, 그렇지 않으면 0



OR 연산

둘 중 하나가 1이면 1, 둘다 0이면 0



XOR 연산(*)

다르면 1, 같으면 0



~ 연산

0 이면 1, 1이면 0




이게 뭐 ? 라고 생각할 수도 있겠지만...

간단히 말하자면 1 Byte 공간을 예로 들면 1 Byte = 8 Bit 이다.


예를들어 저 8개의 0 또는 1 조합을 이용하면, 우리는 8개의 True / False 값을 저장하는 변수를 단지 1 Byte 로 해결 볼 수 있다는 의미이다.





Posted by 해비
2015. 3. 15. 23:24



초기화 하지 않은 변수의 값을 출력하면 쓰레기 값이 출력된다고 알고 있었는데...


vs2013 에서는 변수가 초기화 되지 않았다고 빌드 자체를 거부한다.

1>------ Build started: Project: ex211, Configuration: Debug Win32 ------

1>  ex211.c

1>d:\vs2013\ex211\ex211\ex211.c(10): error C4700: uninitialized local variable 'num2' used

1>d:\vs2013\ex211\ex211\ex211.c(10): error C4700: uninitialized local variable 'num1' used

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

 




<< 소스코드 >>

#include <stdio.h>

int main(void)

{


int num1;

int num2;


printf("%d %d", num1, num2);


return 0;

} 

- 초기화 하지 않은 변수 num1, num2를 사용하는 소스코드




vs2013 에서 확인된 내용인데... 다른 컴파일러에서는 어찌 반응할지 찾아봐야 겠다.


직접 시도 안해 봤으면 그냥 예전 지식 그대로 알고 있을 뻔 한 사건;;


역시 직접 부딪혀 보는게 최고~!!




Posted by 해비
2015. 3. 9. 11:59




파일 읽기 - 라인단위, 텍스트 파일

#include <iostream>
#include <fstream>

using namespace std;

int main(void)
{
    ifstream ifile;

    char line[200]; // 한 줄씩 읽어서 임시로 저장할 공간
       
    ifile.open("input.txt");  // 파일 열기

    if (ifile.is_open())
    {
        while (ifile.getline(line, sizeof(line))) // 한 줄씩 읽어 처리를 시작한다.
        {
            cout << line << endl; // 내용 출력
        }
    }

    ifile.close(); // 파일 닫기

    return 0;
}




파일 쓰기 - 라인단위, 텍스트 파일

#include <iostream>
#include <fstream>

using namespace std;

int main(void)
{
    ofstream ofile;
    ofile.open("output.txt");

    // 파일 쓰기
    for (int x = 0; x < 10; x++)
    {
        ofile << x << endl;
    }

    ofile.close(); // 파일닫기

    return 0;
}







Posted by 해비
2015. 2. 24. 15:52

텍스트 상자 라던가...


여튼 입력 항목이 많을 때에는 컨트롤 배열로 선언해서 다루면 여러모로 편리합니다. ^__^

가끔 써먹어야 하는데 기억이 가물가물 해서 정리를...

 


전역변수 선언

        TextBox[] tBox; // 컨트롤 배열로 사용할 TextBox 전역변수 선언

        const int ARR_TEXTBOX_COUNT = 20; // 생성할 텍스트 박스 갯수 설정 



컨트롤 배열 생성, 기본 옵션 설정, 이벤트 연결

        private void CreateTextBoxes()

        {

            tBox = new TextBox[ARR_TEXTBOX_COUNT];


            for (int i = 0; i < tBox.Length; i++)

            {

                // 새 인스턴스 생성

                tBox[i] = new TextBox();


                // 기본옵션 설정

                tBox[i].Name = "txt" + i.ToString(); // 텍스트상자에 이름을 부여한다. 예) txt1

                tBox[i].Text = "";

                tBox[i].Width = 32;

                tBox[i].Height = 21;

                tBox[i].Tag = i.ToString();


                // 그룹박스에 텍스트상자 추가, 각 그룹박스당 10개씩 생성한다.

                if(i < 10)

                {

                    groupBox1.Controls.Add(tBox[i]);

                }

                else

                {

                    groupBox2.Controls.Add(tBox[i]);

                }


                // 이벤트 등록

                tBox[i].KeyPress += Form1_KeyPress;

            }



이벤트 처리

        // 키눌림 이벤트 처리

        void Form1_KeyPress(object sender, KeyPressEventArgs e)

        {

            // 엔터 키 입력

            if(e.KeyChar == (char)13)

            {

                // 이벤트가 발생한 컨트롤의 핸들 얻기

                TextBox hTextBox = (TextBox)sender;


                // 컨트롤 번호 얻기

                int ctrlNum = Convert.ToInt32(hTextBox.Tag);


                // 마지막 박스에서는 다음으로 이동하지 않는다. (오류방지)

                if(ctrlNum < ARR_TEXTBOX_COUNT -1)

                {

                    tBox[ctrlNum + 1].Focus();

                }

            }

        } 



텍스트 상자 이름 한번에 가져오기

        // 모든 텍스트 상자의 이름 가져오기!!

        private void getControlNames(Control control)

        {

            // 대상 control 의 하위에 있는 모든 컨트롤을 대상으로 스캔한다.

            foreach (Control tmpCtrl in control.Controls)

            {

                // 재귀호출 : 컨트롤이 또 다른 컨트롤을 품고 있으면 하위의 끝까지 스캔한다.

                if (tmpCtrl.Controls.Count > 0)

                    getControlNames(tmpCtrl);


                // 현재 스캔중인 컨트롤이 TextBox 형식이면...

                if (tmpCtrl is TextBox)

                {

                    TextBox textBox = (TextBox)tmpCtrl; // TextBox 으로 형 변환 한다.


                    txtLog.AppendText(textBox.Name.ToString() + "\n"); // 현재 컨트롤 이름 Log 추가

                }

            }

        }




vs2013 에서 작성한 예제 소스 첨부


ControlArrayExample.rar




예제 실행 화면



Posted by 해비
2014. 12. 3. 15:11



소스코드

using System.Runtime.InteropServices;


...

Some Class ...


        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

        private int WM_IME_NOTIFY         = 0x0282;
        private int WM_DESTROY            = 0x0002;
        private int WM_NCDESTROY          = 0x0082;
        private int WM_CLOSE              = 0x0010;
        private int IMN_CLOSESTATUSWINDOW = 0x0001;
        private int WM_KILLFOCUS          = 0x0008;
        private int WM_COMMAND            = 0x0011;


...


        protected override void WndProc(ref Message m)
        {
            if(m.Msg == (int)WM_CLOSE)
            {
                MessageBox.Show("X 눌렸엉 ㅠㅠ");
            }

            base.WndProc (ref m);
        }




Form1_Closing() 역시 종료 할때 발생합니다만, 그 이전에 WndProc 가 먼저 처리 됩니다.


X 눌렸을 때 막고, 닫기 버튼을 눌렀을 경우에만 종료 시키려면 Form1_Closing() 메서드에 e.Cancel = false; 주되, 특정 변수값 세팅이면 true 로 주면 되겠네요.


닫기 버튼은

변수 값 세팅, form.Close() 주면 될 것이고


우상단 X 는

WndProc에서 변수값 세팅해 주면 될 것이고~ 



그냥 Form1_Closing() 에 e.Cancel = false; 만 주면 불사속성을 지닌 창을 마주하게 될 겁니다. 





Posted by 해비
2014. 11. 28. 17:05


코드 실행 중 새 창 띄우고, 새 창이 닫히면 다시 코드가 계속 실행되는 예제 입니다.


요약 : 모달 폼으로 띄우면 됩니다.

            textBox1.AppendText("1");
            textBox1.AppendText("2");
            textBox1.AppendText("3");
           
            Form2 form2 = new Form2();
            form2.ShowDialog(this);
         
            textBox1.AppendText("4");
            textBox1.AppendText("5");
            textBox1.AppendText("6"); 


저렇게 다이얼로그 형식으로 불러내면 새 창 닫기 전 까지 코드 수행이 중단 됩니다.

Posted by 해비
2014. 10. 24. 09:47

C# 윈폼에서 창 열고 닫는 소스코드 입니다.


단순히 .Show()만 하면 되지 않느냐구요??

(직접 겪어보시믄 이게 왜 필요한지 알게 됩니다잉~)


여기서 ntcDF는 Form객체 입니다.



창 열기

        private void showNotice()
        {

            if(!ntcDF.Created)
            {
                if(ntcDF.IsDisposed)
                {
                    ntcDF = new NoticeDF();
                }

                ntcDF.Show();
            }
            else
            {
                ntcDF.Activate();
            }
        }



창 닫기

        private void closeNotice()
        {
            if(!(ntcDF == null))
            {
                ntcDF.Close();
            }
        }  




Posted by 해비
2014. 10. 15. 17:19


컬럼(Column) 잠그기

this.fpSpread1_Sheet1.Columns.Get(0).Locked = true;
this.fpSpread1_Sheet1.Columns.Get(1).Locked = true;
this.fpSpread1_Sheet1.Columns.Get(2).Locked = true;




컬럼(Column) 정렬기능 추가

1

 this.fpSpread1.ActiveSheet.SetColumnAllowAutoSort(-1, true);

2

 this.fpSpread1.Sheets[0].Columns[1].AllowAutoSort = true;

- Column 헤더 클릭 시 해당 Column 기준으로 데이터를 정렬하여 표시 해 준다.

- [1] 또는 [2] 중 한가지를 선택 적용한다.



현재 선택한 Row 색칠하기(잠겨진 셀 포함)

//잠긴 셀에 지정된 색상 해제
fpSpread1.ActiveSheet.LockBackColor = Color.Empty;

//기존에 칠된것 모두 제거
for(int i=0; i<fpSpread1.ActiveSheet.Rows.Count; i++)
{
    fpSpread1.ActiveSheet.Rows[i].BackColor = Color.Empty;
}

//새로 색칠
fpSpread1.ActiveSheet.Rows[iCurRow].BackColor = Color.FromArgb(255, 255, 0);

- RGB색상은 마음에 드는 색상으로 설정한다.




싱글 선택만 되도록

this.fpSpread1_Sheet1.SelectionPolicy = FarPoint.Win.Spread.Model.SelectionPolicy.Single;

- 디자이너 화면에서 설정 가능하다.




셀 한번 클릭 시 이벤트

private void fpSpread1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{

}

- CellClick 이벤트는 이전 클릭된 것을 바라보는 문제가 있고, SelectionChange 이벤트는 1개뿐이 없을 때 다시 선택해도 이벤트 발생이 되지 않는 문제가 있다.(이건 당연하지만...)


- DoubleClick 이벤트는 말 그대로 두번 클릭질 해야 하는데 역시 불편하다. 두번클릭보단 한번클릭이 아무래도 더 편하니까~~


혹시 다른 또는 더 좋은 방법 있으면 한 수 가르침 부탁드립니다.




컬럼 헤더 셋팅 예제코드

fpSpread1.Sheets[0].ColumnHeader.Cells[0, 0].Text = "코드";
fpSpread1.Sheets[0].Columns[0].Width = 60;
fpSpread1.Sheets[0].ColumnHeader.Cells[0, 1].Text = "내용";
fpSpread1.Sheets[0].Columns[1].Width = 60;
fpSpread1.Sheets[0].ColumnHeader.Cells[0, 2].Text = "설명";
fpSpread1.Sheets[0].Columns[2].Width = 250;

- 컬럼 헤더 타이틀, 컬럼의 가로길이 설정 예.




멀티라인 텍스트 데이터 가져오기

private DataTable _rtnDT; // 전역변수 선언


// 스프레드에 데이터 바인딩

_rtnDT = _controller.getData();

fpSpread1.DataSource = _rtnDT;


// 스프레드 내용 참조 시

iRow = fpSpread1.ActiveSheet.ActiveRowIndex;


txtProhAbbr.Text = _rtnDT.Rows[iRow][0].ToString();
txtProhMemo.Text = _rtnDT.Rows[iRow][1].ToString();

- fpSpread의 [row,col].Text 로는 멀티라인 데이터를 텍스트 박스에 연결 시 한줄로 출력되는 문제가 있어 해당 Row번호만 알아낸 다음 DataTable에서 직접 가져온다.

혹시 다른 또는 더 좋은 방법 있으면 한 수 가르침 부탁드립니다.




셀 병합

fpSpread1.Sheets[0].AddSpanCell(0, 0, 2, 1);

- 인자 4개를 넘겨줘야하는데 앞 2개 인자는 병합 시작 셀의 row, col 값 이고, 뒤에 2개 인자는 병합할 row 수, 병합할 col 수 이다.

- 위 예제를 본다면 0,0 으로 부터 2개 Row, 1개 Col 병합 이므로 아래와 같이 될 것이다.


 

 


0,0 좌표로 부터 2개 Row 병합된 상태, Col 은 1 이었으므로 병합되지 않는다. 만약 Col 도 2 였다면, 2 x 2 셀 모두 합쳐 진다.




엑셀 내보내기

// 스프레드에 내보낼 정보 적재
fpSpread1.DataSource = rtnDS.Tables[0];

// 컬럼길이 조정
fpSpread1.Sheets[0].Columns[0].Width = 70;  // 사번
fpSpread1.Sheets[0].Columns[1].Width = 70;  // 성명
fpSpread1.Sheets[0].Columns[2].Width = 50;  // 삭제

// 파일저장 다이얼로그 확장자 필터 설정
savedlg.Filter = "Excel files (*.xls)|*.xls|All files (*.*)|*.*";
savedlg.FilterIndex = 1;

if (savedlg.ShowDialog() == DialogResult.OK)
{
    // 스프레드의 내용을 엑셀파일로 저장
    fpSpread1.SaveExcel(savedlg.FileName, FarPoint.Win.Spread.Model.IncludeHeaders.ColumnHeadersCustomOnly);

    MessageBox.Show("저장이 완료 되었습니다.", this.Text,
        MessageBoxButtons.OK, MessageBoxIcon.Information);
}

- SaveFileDialog 컨트롤 을 호출하여 저장할 위치, 파일명을 입력받고, 스프레드의 내용을 해당 위치에 내보낸다.

- 스프레드 속성 중 Visible = false 인 경우, 시트 없는 빈 엑셀 파일이 생성되므로 주의한다.






Posted by 해비