728x90

이벤트 개념을 잡고 이벤트를 만들어서 코드 작성을 하면 훨씬 더 좋을거 같아서 C#이 자동으로 만들어주는 이벤트를 분석해보기로 했다.



button1 을 더블 클릭하면 자동으로 코드가 만들어진다.


private void button1_Click(object sender, EventArgs e)
{

}


모든 이벤트는 이벤트를 발생시킨 객체(object) 와 해당 이벤트의 정보를 가지는 이벤트 객체로 구성되는 두개의 인자를 가진다.


Designer.cs 파일에도 자동으로 만들어진 코드가 있다.

this.button1.Click += new System.EventHandler(this.button1_Click);


이걸 분석해보자.

마우스 우클릭을 눌러서 정의 피킹(Alt + F12)을 해보면 연결된 정의가 뭔지 나온다.

이걸 도식화해서 그려봤다.



결국 .NET 기본 클래스 라이브러리에 존재하는 미리 정의된 delegate 타입을 사용한 것이다.


this.btnTest.Click += new System.EventHandler(this.MyHandler);

명칭을 변경해주면

자동으로

private void MyHandler(object sender, EventArgs e)
{

}

로 변경된다.


이벤트를 직접 만들어서 사용하려면 어떻게 해야 할까?

지정자 event event-delegate event_name;

이라는 걸 알 수 있다.

이벤트(event)는 어떤 사건이 발생했을 때 호출되어야 하는 메서드의 목록이다.

이벤트는 내부적으로 delegate로 구현되므로 이벤트를 선언하기 전에 delegate를 먼저 선언해두어야 한다.

delegate는 이벤트가 어떤 인수를 전달하는지를 정의하며, 이벤트 핸들러의 형태를 규정한다.


http://link2me.tistory.com/853 게시물의 delegate 개념 이해하기 와 http://link2me.tistory.com/856 폼간 데이터 전송 게시물에 보면 delegate 를 활용한 이벤트가 나온다.


영문 대신에 이해하기 쉽게 우리말로 적어보면

의 과정으로 동작되는 걸 알 수 있다.

- 하나의 delegate 로 여러개의 event 를 생성할 수 있다.

- 실행메서드명은 delegate 에서 정의한 이벤트핸들러 명칭 대신 다른 명칭을 사용하고 시그너처(string data)는 동일하다.


타 블로그에 작성된 글을 보고 코드를 만들어서 실행은 해봤는데 명확하게 이해가 안되었었다.

좀 더 분석을 해보니 이런 흐름이라는 걸 어렴풋이 이해하게 되었고 다음부터는 이벤트 만들어서 활용하는 걸 어렵지 않게 할 수 있을 거 같다.


빠른 개념 이해는 역시 파워포인트를 활용해서 그림으로 도식화해보는 것이다.


개념 이해가 된 상태에서 http://www.csharpstudy.com/CSharp/CSharp-delegate3.aspx 게시물을 보면 좀 더 이해하는데 도움이 된다.


델리게이트와 이벤트 핸들러에 대한 이해를 완벽하게 하려면 위 그림 가지고는 부족하다.

같은 폼내에서 이벤트 핸들러 처리하는 것은 별로 어렵지 않다.

다른 폼으로 값을 전달하기 위해서는 어떻게 해야 할까?

다른 폼에서 값을 전달받기 위해서는 어떻게 해야 할까?


그 해답은 http://link2me.tistory.com/928

블로그 이미지

Link2Me

,
728x90

delegate(대리자)는 참조형으로 메서드를 대신 호출하는 역할을 한다.

지정자 delegate 리턴타입 이름(인수목록);

delegate int Mydelegate(int a, int b) ;  // delegate 선언

delegate 는 타입이므로 그 자체만으로 메서드를 가리킬 수는 없으며,

인스턴스를 생성한 후 메서드를 가리키도록 초기화해야 한다.

(** 메소드(Method)는 하나의 실행 가능한 코드의 블럭( { } )으로 정의할 수 있으며, 함수라고 부르기도 한다.)

이 delegate 의 입력 파라미터와 반환값의 타입은 서로 같아야 한다.

입력 파라미터로 정수형(int) a, b 를 받아서 그 결과를 정수형(int)로 반환해주는 형태로 delegate를 정의한다.

delegate 만 빼면 메소드와 같다.


// delegate 와 연결할 함수 선언

class Calculator
{
    public int Plus(int a, int b)
    {
        return a + b;
    }

    public static int Minus(int a, int b)
    {
        return a - b;
    }


    public int Multiply(int a, int b)
    {
        return a * b;
    }

}


Calculator cal = new Calculator(); // 클래스형인 cal 개체 초기화(메모리 할당)

Mydelegate callback;  // delegate 타입의 객체를 생성(인스턴스 참조변수 선언)

// delegate 에 실제 함수를 할당해준다.

callback = new Mydelegate(cal.Plus);  //  delegate에 Plus() 메소드 주소 바인딩
Console.WriteLine(callback(3, 4));  // delegate 수행

Console.WriteLine("원래 함수 이용한 값 더하기 " + cal.Plus(3, 4));


// delegate 인스턴스 참조변수 선언 및 delegate에 정적 메소드인 Minus() 메소드 바인딩

Mydelegate mysub = new Mydelegate(Calculator.Minus);  // delegate 수행
Console.WriteLine("두 값의 차이는 " + mysub(11, 5) + " 입니다");


Mydelegate multiply = cal.Multiply;  // new Mydelegate(cal.Multiply) 의 축약형

Console.WriteLine("두 값의 곱셈은 " + multiply(3, 4) + " 입니다");


델리게이트(delegate) 는

- 정적, 비정적 메소드 구분없이 사용

- 특정 틀래스에 소속되지 않음

- 타입만 일치하고 접근만 허용되면 어떤 메소드도 접근 가능


위 코드를 직접 실행해 볼 수 있는 소스코드이다.


delegate-01.zip


Ctrl + F5 키를 누르면 콘솔창에 값이 표시된다.

아니면 마지막 줄에 Console.ReadLine(); 을 추가해주고 F5 키를 눌러도 된다.


블로그 이미지

Link2Me

,
728x90

C# 델리게이트를 어떻게 사용해야 하는지 예제를 통해서 살펴보자.

그동안 델레게이트를 어떻게 처리해야 할지 개념이 잡힐 듯 잡힐 듯 하면서도 잘 잡히지 않았는데 구글링하다가 발견한 예제를 통해서 폼간에 값을 어떻게 전달해야 하는지 확실하게 감을 좀 잡은거 같다.


1. 메인 폼(MainForm)에서 자식폼으로 값을 전달해야 할 경우

2. 자식폼에서 메인폼으로 값을 전달해야 할 경우


세가지 경우만 알면 폼간에 값을 넘기는 것에 대한 이해는 다 했다고 볼 수 있다.

ㅇ 델리게이트(delegate)는 Class 밖에 선언한다.

ㅇ 델리게이트 타입과 실행메서드 타입은 동일 해야 한다.

ㅇ 이벤트는 값을 전달하는 쪽에서 선언해야 한다.



namespace DelegatesDemo
{
    public delegate void DataPushEventHandler(string value);  // 메인폼 --> 자식폼 으로 값 전달 델리게이트
    public delegate void DataGetEventHandler(string item); // 자식폼 --> 메인폼으로 값 전달 델리게이트

    public partial class MainForm : Form
    {
        // 값을 보내는 곳의 Form 쪽에 이벤트 선언해야 한다
        public DataPushEventHandler DataSendEvent

        public MainForm()
        {
            InitializeComponent();
        }


1. 메인 폼(MainForm)에서 자식폼으로 값을 전달해야 할 경우



=== 메인폼 코드 ===

private void btnOpenFrm1_Click(object sender, EventArgs e)
{
    // 같은 폼이 2개 이상 뜨지 않도록 처리하는 코드
    foreach (Form openForm in Application.OpenForms)
    {
        if (openForm.Name == "ChildForm1")
        {
            if (openForm.WindowState == FormWindowState.Minimized)
            {
                openForm.WindowState = FormWindowState.Normal;
            }
            openForm.Activate();
            return;
        }
    }

    ChildForm1 frm1 = new ChildForm1();
    this.DataSendEvent += new DataPushEventHandler(frm1.SetActionValue1);
    frm1.StartPosition = FormStartPosition.Manual;
    frm1.Location = new Point(this.Location.X + this.Width + 5, this.Location.Y);
    frm1.Show();
}

private void txtParam_TextChanged(object sender, EventArgs e)
{
    // 텍스트박스에 값을 입력하면 실시간적으로 자식폼으로 값이 전달됨
    DataSendEvent(txtParam.Text);
}

private void btnOpenFrm2_Click(object sender, EventArgs e)
{
    // 같은 폼이 2개 이상 뜨지 않도록 처리하는 코드
    foreach (Form openForm in Application.OpenForms)
    {
        if (openForm.Name == "ChildForm2")
        {
            if (openForm.WindowState == FormWindowState.Minimized)
            {
                openForm.WindowState = FormWindowState.Normal;
            }
            openForm.Activate();
            return;
        }
    }

    ChildForm2 frm2 = new ChildForm2();
    this.DataSendEvent += new DataPushEventHandler(frm2.SetActionValue2);
    frm2.StartPosition = FormStartPosition.Manual;
    frm2.Location = new Point(this.Location.X + this.Width + 5, this.Location.Y + 100);
    frm2.Show();
}


=== 자식폼 코드 ===

public void SetActionValue1(string param)
{
    txtParam.Text = param;
}


2. 자식폼에서 메인폼으로 값을 전달해야 할 경우



=== 자식폼 코드 ===

namespace DelegatesDemo
{
    public partial class FormDialog : Form
    {
        // Declare delagete callback function
        // 값을 보내는 곳의 Form 쪽에 이벤트 선언해야 한다
        public DataGetEventHandler DataSendEvent;

        public FormDialog()
        {
            InitializeComponent();
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            if (textBoxItem.Text.Length == 0)
            {
                MessageBox.Show("값이 입력되지 않았습니다");
                return;
            }
            else
            {
                DataSendEvent(textBoxItem.Text);
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}


=== 메인폼 코드 ===

private void btnScenario2_Click(object sender, EventArgs e)
{
    // 같은 폼이 2개 이상 뜨지 않도록 처리하는 코드
    foreach (Form openForm in Application.OpenForms)
    {
        if (openForm.Name == "FormDialog")
        {
            if (openForm.WindowState == FormWindowState.Minimized)
            {
                openForm.WindowState = FormWindowState.Normal;
            }
            openForm.Activate();
            return;
        }
    }

    FormDialog frm3 = new FormDialog();
    frm3.DataSendEvent = new DataGetEventHandler(this.Form3DataAction);
    frm3.StartPosition = FormStartPosition.Manual;
    frm3.Location = new Point(this.Location.X + this.Width + 5, this.Location.Y + 250);
    frm3.Show();
}

private void Form3DataAction(string item)
{
    listBox.Items.Add(item);
}


예제에 사용된 소스코드가 포함된 자료


DelegatesDemo.zip


본 예제는 http://www.c-sharpcorner.com/uploadfile/mosessaur/winformsdelegates09042006094826am/winformsdelegates.aspx 에서 받은 파일을 더 이해하기 쉽게 파일을 수정했다.

원본 예제는 파일 창 중복체크도 없고, 창을 띄울 때 Location 설정하는 것이 반영되지 않았다.

명칭은 변경해서 이해하기 쉽게 정리를 했다.



블로그 이미지

Link2Me

,
728x90

C# 에서 User Documnets 폴더 밑에다가 특정 폴더를 만들고 싶은 경우가 있다.

이때 간단하게 폴더를 생성하는 코드다.


private void MyFolderCreate(string myfolder)
{
    string UserPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); // User Documents 폴더
    string MyPath = UserPath + "\\" + myfolder;
    if (!Directory.Exists(MyPath))
    {
        System.IO.Directory.CreateDirectory(MyPath);
    }
}


아래는 폴더 경로 정보와 폴더 생성를 분리하여 처리한 코드이다.

public static void MyFolderCreate(string myfolder)
{
    string MyPath = MyFolder(myfolder);
    if (!Directory.Exists(MyPath))
    {
        Directory.CreateDirectory(MyPath);
    }
}

public static string MyFolder(string myfolder)
{
    string UserPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); // User Documents 폴더
    return UserPath + "\\" + myfolder;
}


=== 폴더 삭제 코드 ===


public static void MyFolderDelete(string path)
{
    DeleteDirectory(path, false);
}

public static void DeleteDirectory(string path, bool recursive)
{
    if (recursive)
    {
        var subfolders = Directory.GetDirectories(path);
        foreach (var subfolder in subfolders)
        {
            DeleteDirectory(subfolder, recursive);
        }
    }

    // Get all files of the folder
    var files = Directory.GetFiles(path);
    foreach (var file in files)
    {
        var attribute = File.GetAttributes(file);
        if ((attribute & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
        {
            File.SetAttributes(file, attribute ^ FileAttributes.ReadOnly);
        }
        File.Delete(file);
    }
    Directory.Delete(path);
}


폴더 삭제코드 출처 : http://caioproiete.net/en/csharp-delete-folder-including-its-sub-folders-and-files-even-if-marked-as-read-only/


'C# > 문법 및 기능' 카테고리의 다른 글

C# NameValueCollection  (0) 2016.03.20
C# Dictionary 와 comboBox 연결  (0) 2016.02.08
C# 시간 메소드  (0) 2016.01.15
C# 파일 입출력 개념 정리  (0) 2016.01.05
C# int.TryParse  (0) 2015.12.25
블로그 이미지

Link2Me

,
728x90

C# 독학하는 초보자인데 질문하신 분이 있어서 올립니다.

Encoding 자동 감지하는 소스를 올리지 않습니다.

그 부분은 Encoding enc = Encoding.Default;

와 같이 코드를 수동으로 처리해서 테스트하시기 바랍니다.


using System.IO;
private void btnHangul_Click(object sender, EventArgs e)
{
    // 한글이 포함되어 있는지 검사
    using (OpenFileDialog dlg = new OpenFileDialog())
    {
        dlg.Filter = "csv (*.csv) | *.csv";
        if (dlg.ShowDialog() == DialogResult.OK)
        {
            Read_CSV_firstColumn(dlg.FileName);
        }
    }
}

public void Read_CSV_firstColumn(string fileName)
{
    string delimiter = ";";  // 구분자
    Encoding enc = GetFileEncoding(fileName);
    if (enc == null)
    {
        MessageBox.Show("Encoding Detection failed.");
        return;
    }

    using (var sr = new StreamReader(fileName, enc, true))
    {
        string line = null;
        while ((line = sr.ReadLine()) != null)
        {
            string[] fields = line.Split(new string[] { delimiter }, StringSplitOptions.None);
            if (isContainHangul(fields[0]))  // 첫번째 칼럼에 한글이 포함되어 있으면
            {
                MessageBox.Show(fields[0] + " : 한글 포함되어 있네요");
            }
            else
            {
                MessageBox.Show(fields[0] + " : 한글 포함 안되어 있어요");
            }
        }
        sr.Close();
    }
}

/// <summary>
/// 문자열에 한글이 포함되어 있는지 검사하여 포함되어 있으면 true 반환
/// </summary>
public bool isContainHangul(string s)
{
    char[] charArr = s.ToCharArray();
    foreach (char c in charArr)
    {
        if (char.GetUnicodeCategory(c) == System.Globalization.UnicodeCategory.OtherLetter)
        {
            return true;
        }
    }
    return false;
}


블로그 이미지

Link2Me

,
728x90

C# 에서 시간을 다루는 메소드가 몇가지 있다.

활용하는 목적이 좀 다르다.


System.Windows.Forms 을 이용하여 타이머를 구동시키는 경우에는 함수가 실행될 때 타이머가 동작하고 함수가 종료되면 타이머를 멈추는 방식이다.

using System.Windows.Forms;
private void MainForm_Load(object sender, EventArgs e)
{
    timer1.Interval = 60000;  // 지정단위는 millisecond
    timer1.Start();   // 다른 방법으로는 timer1.Enabled = true; 라고 하면 된다
}

private void timer1_Tick(object sender, EventArgs e)
{
    //현재 시각을 가져와 time 변수에 할당
    DateTime time = DateTime.Now// 현재 날짜와 시간을 얻는다
    if (time.Hour == 09 && time.Minute == 10)
    {  //time 변수의 시가 09시이고, 분이 10이면 코드 실행
        // 실행할 코드
        timer1.Stop();
    }
}


C#에서 시간에 대한 데이타형은 2종류다.
DateTime과 TimeSpan
DateTime 구조체는 날짜와 시간을 표현한다. 2016년 1월 10일 이렇게 시각을 정할때 사용하고
TimeSpan은 두 시점간의 간격을 경과일과 시분초로 표현하며, 정밀도는 천만분의 1초이다.

시간으로 10일을 더하고 싶으면 TimeSpan.FromDays(10)이렇게 하면 10일을 tick으로 환산하여 계산한다.
// DateTime(년,월,일,시,분,초)
DateTime birthday = new DateTime(1997,04,21,19,05,30);
//시각(DateTime) - 시각 = 시간(TimeSpan)
TimeSpan result = DateTime.Now - birthday;


// 현재 날짜와 시간을 출력하고자 할 경우 출력하는 형태를 지정해서 할 수 있다

DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");


소요(동작)시간 체크하기

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();  // Stopwatch 객체 생성
sw.Start();
OpenSubtitle(fileName, listView1); // 체크하고 싶은 메소드
sw.Stop();

// 동작시간 출력

MessageBox.Show(sw.Elapsed.ToString());


// 동작시간을 밀리초 단위로 출력

MessageBox.Show(sw.ElapsedMilliseconds.ToString() + " msec");


또다른 소요시간 체크하는 방법

var start = DateTime.Now;
OpenSubtitle(fileName, listView1);
var stop = DateTime.Now - start;
MessageBox.Show("소요시간:" + stop.ToString());

사용자 지정 날짜 및 시간 형식 문자열 MSDN : https://msdn.microsoft.com/ko-kr/library/8kb3ddd4%28v=vs.110%29.aspx




'C# > 문법 및 기능' 카테고리의 다른 글

C# Dictionary 와 comboBox 연결  (0) 2016.02.08
C# Directory (폴더) 생성 및 삭제  (0) 2016.01.16
C# 파일 입출력 개념 정리  (0) 2016.01.05
C# int.TryParse  (0) 2015.12.25
C# 오버라이딩(Overriding) 개념 이해  (1) 2015.12.21
블로그 이미지

Link2Me

,
728x90


C# SQLite 에서 다중으로 행을 선택한 경우 해당 행을 모두 삭제하는 코드이다.

C# SQLite에서 다량의 데이터를 삭제하는 테스트를 해봤더니 새벽에 테스트한 로직은 시간이 너무 많이 걸린다.

그래서 로직은 새로 구현하여 변경했고, 테스트 결과는 매우 만족스럽게 빠르게 처리된다.

foreach (DataGridViewRow item in this.dataGridView1.SelectedRows) 이 구문은 다중으로 행을 선택하면 로직이 가장 나중에 선택된 행부터 선택하여 삭제를 한다는 걸 확인했다.

연결형 방식으로 처리하며 코드는 아래 포멧을 준수하여 필요한 부분만 수정하면, 구글링이나 네이버 사이트 검색하지 않아도 될 것이다.

if (!this.dataGridView1.Rows[this.rowIndex].IsNewRow) 는 dataGridView 에서 신규행 추가를 위한 행이 아니면을 의미하므로 dataGridView 가 아닌 경우에는 코드를 제외하면 된다.

Update 처리도 동일한 방식으로 코드를 구현하면 된다.


private void btnDelete_Click(object sender, EventArgs e)
{
    int delete_cnt = 0; // 삭제건수 카운트
    if (!this.dataGridView1.Rows[this.rowIndex].IsNewRow)
    {
        using(var Conn = new SQLiteConnection(ConnectionString))
        {
            Conn.Open();
            using (var cmd = new SQLiteCommand(Conn))
            {
                using (var transaction = Conn.BeginTransaction())
                {
                    try
                    {
                        foreach (DataGridViewRow item in this.dataGridView1.SelectedRows)
                        {
                            cmd.CommandText = string.Format("DELETE FROM items Where uid={0}", item.Cells[2].Value);
                            cmd.ExecuteNonQuery();
                            delete_cnt++;
                        }
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        MessageBox.Show(ex.Message);
                        throw;
                    }
                }
            }
            Conn.Close();
            dgv = GetSqliteItems();
            MessageBox.Show(delete_cnt.ToString() + "건 삭제되었습니다...");
        }
    }
}

블로그 이미지

Link2Me

,
728x90

C# 에서 SQLite DB에 있는 테이블을 가져오는 쿼리문이며 dataGridView1 에 뿌려주는 코드이다.

dataGridView1_Display(ds.Tables[0]); 함수는 없어도 된다. dataGridView1 의 특정 칼럼을 readonly 로 한다든지, hidden 으로 처리할 칼럼을 지정한다든지 이런 화면 구성상의 옵션처리를 위해서 만든 함수이다.


string ConnectionString = @"Data Source=test.db;Version=3;";
SQLiteConnection sqliteConn;
SQLiteDataAdapter adapter;
DataTable dt;
DataTable GetSqliteItems()
{
    try
    {
        sqliteConn = new SQLiteConnection(ConnectionString);
        if (sqliteConn.State == ConnectionState.Closed)
        {
            sqliteConn.Open();
        }

        string strqry = "select * from items ";
        if (searchBox.Text.Length > 0)
        {
            strqry += "where Quantity='" + searchBox.Text + "' ";
        }
        strqry += "order by uid";
        adapter = new SQLiteDataAdapter(strqry, sqliteConn);  //
SQLiteDataAdapter 객체 생성

        DataSet ds = new DataSet();  // DataSet 객체 생성
        adapter.Fill(ds); // 생성된 DataSet ds에 데이터 채우기 : select * from items 의 결과물을 채우겠다.

        sqliteConn.Close();  // SQLite 연결해제
        dataGridView1.DataSource = ds.Tables[0];  //바인딩부분
        dataGridView1_Display(ds.Tables[0]); 
// 화면 구성/제어에 필요한 사용자 함수
         
        return ds.Tables[0];
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        return null;
    }
    finally
    {
        //always close the connection
        if (sqliteConn.State == ConnectionState.Open)
        {
            sqliteConn.Close();
        }
    }
}


또다른 방법으로는


string ConnectionString = @"Data Source=test.db;Version=3;";

public void Read()
{
    try
    {
        using (var Conn = new SQLiteConnection(ConnectionString))
        {
            Conn.Open();
            string strqry = "select * from items ";
            if (searchBox.Text.Length > 0)
            {
                strqry += "where ItemName LIKE '%" + searchBox.Text + "%' or Price LIKE '%" + searchBox.Text + "%' or Quantity='" + searchBox.Text + "' ";
            }
            strqry += "order by uid";

            using (var cmd = new SQLiteCommand(strqry,Conn))
            {
                using(SQLiteDataReader reader = cmd.ExecuteReader())
                {
                    DataTable dataTable = new DataTable();
                    dataTable.Load(reader);
                    dataGridView1.DataSource = dataTable;
                    dataGridView1_Display(dataTable);  // 화면 구성/제어에 필요한 사용자 함수
                }
            }
            Conn.Close();
        }
           
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        throw;
    }
}



블로그 이미지

Link2Me

,
728x90

C# SQLite 에서 다중으로 행을 선택한 경우 해당 행을 모두 삭제하는 코드이다.

this.dataGridView1.Rows[this.rowIndex].IsNewRow 은 dataGridVeiw1 에서 새로운 행 추가를 의미하므로 새로운 행 추가가 아닌 경우에만 행을 삭제할 수 있도록 if 문으로 처리했다.

SQLiteDataAdapter adapter;
DataTable ds;
변수 선언은 select 문에서 처리하면서 선언해서 delete 에서는 관련 내용이 나오지 않는다.


if (dataGridView1.SelectionMode != DataGridViewSelectionMode.FullRowSelect)
{
    dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
}

코드도 필요하면 추가해준다.


dataGridView1 자체의 행만 삭제하는 경우에는 아래 코드만 있으면 된다.

foreach (DataGridViewRow item in this.dataGridView1.SelectedRows)
{
    dataGridView1.Rows.RemoveAt(item.Index);
}
하지만 SQLite DB의 테이블 데이터까지 삭제하려면 아래 코드처럼 처리해줘야 한다.


private void btnDelete_Click(object sender, EventArgs e)
{
    if (!this.dataGridView1.Rows[this.rowIndex].IsNewRow)
    {
        sqliteConn = new SQLiteConnection(ConnectionString);
        sqliteConn.Open();

        foreach (DataGridViewRow item in this.dataGridView1.SelectedRows)
        {
            dataGridView1.Rows.RemoveAt(item.Index);
            // Set the DELETE command and parameter.
            adapter.DeleteCommand = new SQLiteCommand(
                "DELETE FROM items "
                + "WHERE uid=@uid;", sqliteConn);
            adapter.DeleteCommand.Parameters.Add("@uid", DbType.Int16, 4, "uid");
            adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;
        }

        adapter.Update(ds);
        ds = GetSqliteItems();
        MessageBox.Show("선택한 행이 성공적으로 삭제되었습니다...");
    }
}

블로그 이미지

Link2Me

,
728x90

C# 에서 SQLite DB를 연동하려고 하니 Data Type 을 잘 몰라서 생소하다.

그래서 SQLite 로 DB를 변환해주는 툴(SQLiteConverter)을 이용하여 MySQL 테이블 구조와 데이터를 변환해봤다.

아래 테이블 구조는 구글링하다가 발견했던 자료(http://www.codeproject.com/Articles/71346/Connecting-to-MySQL-Database-using-C-and-NET)를 기준으로 기능 파악을 위해 이것저것 연습해보는 자료다.

이 자료가 100% 만족스런 결과를 가져다 주지는 못한다. 이 자료를 기반으로 응용해서 만들 때 Update, Insert 등을 해보면서 스스로 파악하면 된다.


MySQL 테이블 구조

CREATE TABLE IF NOT EXISTS items (
  uid int(11) NOT NULL AUTO_INCREMENT,
  ItemName varchar(100) NOT NULL,
  Price double NOT NULL,
  Quantity int(11) NOT NULL,
  d_regis datetime NOT NULL,
  PRIMARY KEY (uid)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;


SQLite 테이블 구조

CREATE TABLE [items] (
'uid' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
'ItemName' TEXT NOT NULL,
'Price' REAL NOT NULL,
'Quantity' INTEGER NOT NULL,
'd_regis' DATETIME NOT NULL);

로 SQLite Expert 프로그램에서 확인할 수 있다.


보는 바와 같이 데이터타입(Data Type) 구조가 다르다.

http://www.sqlite.org/datatype3.html 에 가면 Data Type 구조에 대한 설명이 나온다.


NULL. The value is a NULL value.
INTEGER. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.
REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number.
TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).
BLOB. The value is a blob of data, stored exactly as it was input.


SQLite 는 데이터베이스의 모든 정보가 하나의 파일에 저장된다.

테이블 스키마, 레코드 데이터, 인덱스와 같은 모든 정보가 한 파일에 저장된다.

파일 하나로 동작하기 때문에 다중 프로세스나 다중 스레드로 동작하는 경우 파일 잠금 이슈가 발생할 수 있다.

SQLite3 는 테이블 이름과 칼럼 이름에 대해 대소문자를 구별하지 않는다.

SQLite 는 동적 타이핑(dynamic typing)을 사용한다. 데이터 타입 제약을 강제하지 않는다.
어떤 데이터도 아무 컬럼에나 들어갈 수 있다.


대략적인 구조파악이 되었다면 이제 C# 과 SQLite 간에 연동을 위한 구조를 파악해야 한다.

SQLite Expert 에서 확인해보면 테이블 size 에 길이 제한이 없다.


아래 코드는 dataGridView1 에서 데이터를 Update, Insert 하는 코드인데, MySQL 기준으로 동작하던 걸 약간 수정해서 사용해보니 에러가 발생해서

adapter.UpdateCommand.Parameters.Add("@ItemName", DbType.String, 100, "ItemName");

와 같은 데이터 타입을 맞춰야 SQLite에 맞게 수정해야 한다.

http://www.blogbus.com/hyangl-logs/2219450.html 에 나온 SQLite ADO.NET wrapper 를 참조하여 적정하게 매핑처리하면 된다. 테이블의 사이즈가 없으니까 DbType.Int16 을 사용하던, DbType.Int32 를 사용하던 상관이 없을거 같다.

datetime 을 MySQL 에서는 now() 로 하면 되었지만, SQLite 에서는 에러가 발생한다.

datetime('now','localtime') 로 localtime 까지 해줘야만 한국시간으로 정확하게 표시가 된다.


private void btnSave_Click(object sender, EventArgs e)
{
    try
    {
        sqliteConn = new SQLiteConnection(ConnectionString);
        sqliteConn.Open();

        // Set the UPDATE command and parameters.
        adapter.UpdateCommand = new SQLiteCommand(
            "UPDATE items SET ItemName=@ItemName, Price=@Price, Quantity=@Quantity, d_regis=datetime('now','localtime') WHERE uid=@uid;", sqliteConn);
        adapter.UpdateCommand.Parameters.Add("@uid", DbType.Int16, 4, "uid");
        adapter.UpdateCommand.Parameters.Add("@ItemName", DbType.String, 100, "ItemName");
        adapter.UpdateCommand.Parameters.Add("@Price", DbType.Decimal, 10, "Price");
        adapter.UpdateCommand.Parameters.Add("@Quantity", DbType.Int16, 11, "Quantity");
        adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;

        // Set the INSERT command and parameter.
        adapter.InsertCommand = new SQLiteCommand(
            "INSERT INTO items VALUES (@uid,@ItemName,@Price,@Quantity,datetime('now','localtime'));",
            sqliteConn);
        adapter.InsertCommand.Parameters.Add("@uid", DbType.Int16, 4, "uid");
        adapter.InsertCommand.Parameters.Add("@ItemName", DbType.String, 100, "ItemName");
        adapter.InsertCommand.Parameters.Add("@Price", DbType.Decimal, 10, "Price");
        adapter.InsertCommand.Parameters.Add("@Quantity", DbType.Int16, 11, "Quantity");
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;

        adapter.Update(ds);
        ds = GetSqliteAllItems();
        MessageBox.Show("성공적으로 저장되었습니다...");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}


MySQL 연동과 다르게 에러가 발생하는 부분이 좀 있어서 기능을 하나 하나 테스트하면서 수정해보고 있다.


SQLite 특징 및 분석 추천 사이트

http://crystalcube.co.kr/89

http://blog.naver.com/kai4th/20179602367

http://sqlite.org/faq.html#q1


블로그 이미지

Link2Me

,
728x90

C# 에서 관리자 권한으로 실행중인지 체크하는 코드다.

관리자 권한으로 실행중인지 알아야 할 때 이 코드를 활용하여 구현하면 도움이 될 거 같다.


using System;
using System.Security.Principal;


간단하게 작성하면

bool IsUserAdministrator(){
    WindowsIdentity user = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(user);
    return principal.IsInRole(WindowsBuiltInRole.Administrator);
}


좀 더 자세히 작성하면

public bool IsUserAdministrator()
{
    bool isAdmin;
    WindowsIdentity user = null;
    try
    {
        user = WindowsIdentity.GetCurrent(); // 현재 로그인된 user의 정보
        if (user == null)
            throw new InvalidOperationException("Couldn't get the current user identity");
        WindowsPrincipal principal = new WindowsPrincipal(user);
        isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
    }
    catch (UnauthorizedAccessException ex)
    {
        isAdmin = false;
    }
    catch (Exception ex)
    {
        isAdmin = false;
    }
    finally
    {
        if (user != null)
            user.Dispose();
    }
    return isAdmin;
}

private void IsAdmin_Click(object sender, EventArgs e)
{
    if (IsUserAdministrator())
    {
        MessageBox.Show("관리자 권한으로 실행중입니다");
    }
    else
    {
        MessageBox.Show("관리자 권한으로 실행중이 아닙니다");
    }
}

블로그 이미지

Link2Me

,
728x90

SQLite DB를 생성하는 걸 연습해봤다.

SQLite 는 Data Type 이 MySQL 과는 다른 것 같아서 DB Type 부터 익혀야 할 것 같다.

SQLite DB는 PC에 설치하는 파일 DB 라서 파일명을 정해줘야 한다.

파일의 확장자는 sqlite 라고 하기도 하는데 편의상 dat 라고 정했다.

구글링으로 찾은 소스를 내가 사용하는 포멧 형태로 변경하고 파일 검사 루틴을 추가했다.

DB를 만들었는데 또 생성을 할 경우를 대비해서 try catch 문도 추가했으며,

테이블 생성시 테이블이 기존에 생성되어 있으면 생성하지 않도록 하는 CREATE TABLE IF NOT EXISTS 를 추가했다.

테이블을 이런식으로 만들지 않아서 그런지 참 생소하다.

빌드를 할 때 주의할 사항이 있다.

대상 플랫폼을 AnyCPU 로 설정했더니 에러가 발생했다.

 "여기에 종속되어 있는 파일이나 어셈블리 중 하나를 로드할 수 없습니다. 프로그램을 잘못된 형식으로 로드하려고 했습니다" 라는 에러 메시지가 팝업된다.

원인은 System.Data.SQLite 을 32비트 용으로 받았기 때문에 명확하게 32비트 환경에서 동작하도록 지정을 해줘야 한다.


==================== DB 생성 소스코드 =====================

using System.Data.SQLite;

private void sqliteDBCreate_Click(object sender, EventArgs e)
{
    string DbFile = "MyDatabase.dat";
    string ConnectionString = string.Format("Data Source={0};Version=3;", DbFile);
    try
    {
        if (!System.IO.File.Exists(DbFile))
        {
            SQLiteConnection.CreateFile(DbFile);  // SQLite DB 생성
        }
        else  // 기능 동작 여부 확인을 위해서 추가했지만 불필요
        {
            MessageBox.Show("DB 생성되어 있습니다");
            return;
        }


        // 테이블 생성 코드
        SQLiteConnection sqliteConn = new SQLiteConnection(ConnectionString);
        sqliteConn.Open();

        string strsql = "CREATE TABLE IF NOT EXISTS scores (name varchar(20), score int)";

        SQLiteCommand cmd = new SQLiteCommand(strsql, sqliteConn);
        cmd.ExecuteNonQuery();
        sqliteConn.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        return;
    }
}


DB 의테이블을 조회(select), 추가(insert), 수정(update), 삭제(delete) 만 하고 테이블 생성은 SQL Expert Professional 을 가지고 해볼 생각이다.

일단 DB 구조부터 파악해야 하므로 처음부터 배운다는건 너무 어려울 거 같아서 찾아보니 http://www.sqlabs.com/sqliteconverter.php 가 있다. MySQL, Oracle 의 DB를 자동으로 변환해주는 툴이다.


직접 테스트를 해보니까 리눅스 서버에 있는 데이터베이스 접속해서 테이블 구조변환을 순식간에 한다.

이제 SQL Expert 를 이용하여 DB 을 열어서 내부 구조를 파악해 봐야겠다.

블로그 이미지

Link2Me

,
728x90

비주얼 스튜디오에서 참조를 찾아보니까 SQLite 는 기본으로 설정할 수가 없다.

따라서 SQLite 관련 DLL 을 구해야 한다.

http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki

에서 원하는 버전으로 다운로드 한다.


Setup 파일을 받아서 설치하면 Program Files(x86) 폴더에 설치된다.

설치된 곳을 찾아보면 DLL 파일 있다. 내가 설치한 DLL 파일이다.

.NET 4.0 32비트용 dll 파일 첨부 System.Data.SQLite.dll




코드 작성시 using System.Data.SQLite; 를 추가한다.


SQLite DB를 생성하는 것은 별도의 툴을 이용하는 것이 편하다.

http://www.sqliteexpert.com/download.html

에서 운영체제에 맞는 것을 받아서 설치하면 된다.


freeware 버전도 있는데 30일 trial 프로버전을 받아서  설치했다.

http://www.sqliteexpert.com/features.html 에 Personal 버전과 Professional 버전의 기능 차이가 나와 있고 가격도 표시되어 있다.


블로그 이미지

Link2Me

,
728x90

C# 에서 MySQL DB에 있는 자료를 DataSet 메모리 DB에 올린 다음 ListView 에 뿌리는 소스코드다.

listView 에 뿌려주는 순서를 정하려면

MySQL DB에서 Select 하는 구문에서 정해주면 된다.
select * from items 로 된 부분을 SELECT uid, item, price, Quantity, date FROM items 로 뽑아오고 싶은 칼럼만 지정하고 순서를 정하면 된다.


private void listView1Table()
{
    // listView1 의 검색 로직
    myConn = new MySqlConnection(ConnectionString);
    try
    {
        myConn.Open();

        string strqry = "select * from items ";
        if (listView1searchBox.Text.Trim().Length > 0)
        {
            strqry += "where Quantity='" + listView1searchBox.Text.Trim() + "' ";
        }
        strqry += "order by uid";

        adapter = new MySqlDataAdapter(strqry, myConn);
        DataSet ds = new DataSet();
        adapter.Fill(ds); // DS에 select * from items 의 결과물을 채우겠다.

        listView1.Items.Clear();
        DataTableTolistView(ds.Tables[0], listView1);

        lvt1totalcnt.Text = "총 " + i.ToString() + " Row";
        SetHeight(listView1, 20);  // 행높이 지정
        listView1.HeaderStyle = ColumnHeaderStyle.Nonclickable;
        myConn.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

private void DataTableTolistView(DataTable dtable, ListView LV)
{
    if (dtable.Rows.Count > 0)
    {
        int i = 0;
        foreach (DataRow drow in dtable.Rows)
        {
            i = i + 1;
            ListViewItem lvt = new ListViewItem();
            lvt.Text = i.ToString();  // 첫번째 칼럼에 저장, 첫번째 칼럼은 번호 증가 목적
            for (int n = 0; n < dtable.Columns.Count; n++)
            {
                lvt.SubItems.Add(drow[n].ToString());
            }
            LV.Items.Add(lvt);
        }
    }
}

블로그 이미지

Link2Me

,
728x90

트랙바를 이용하여 재생시간이 어느 정도 되고, 트랙바를 마음대로 움직여서 재생을 조절할 수 있게 하는 걸 하고 싶어서 폭풍검색을 해서 해결했다.

검색을 해보니 MP3 플레이어를 만드는 방법이 [DllImport("winmm.dll")] 를 이용하는 방법,  Interop.QuartzTypeLib.dll 을 이용하는 방법, NAudio.dll 를 이용하는 방법이 주로 나온다.

C# 5.0 프로그래밍 실전프로젝트(조호묵 저) 에 구현한 소스코드는 Interop.QuartzTypeLib.dll 을 이용하여 만들었다. (출판사 자료실 : http://www.kame.co.kr/)
winmm.dll 을 이용하여 tackBar 를 구현하려고 했으나 내 능력이 부족해서 구현할 수가 없었다.

검색하다보니 NAudio 를 활용하여 구현하면 된다는 내용이 블로그에 많았다.

그래서 이걸로 구현해보겠다고 마음먹고 구현을 해봤다.


처음에는 구글링한답시고 열라  삽질을 많이 했다.

구글링 검색해서 나오는 예제들은 모두 옛날 버전이더라.

유투브 동영상에 자료 찾아서 해봤는데 역시 예전버전이다. 하지만 모르는 코드를 알게되어 일부 건진 건 있다.



재생시간이 나오면서 트랙바가 움직이게 하는 걸 어떻게 할 수 있을까?

구글링해서 참조해서 만들어본 코드가

trackBar.Value = Convert.ToInt32((int)reader.CurrentTime.TotalSeconds * trackBar.Maximum / (int)reader.TotalTime.TotalSeconds);

이다.

그런데 NAudio 사이트에서 받은 코드에 나온 데모를 보니까 아주 간단하게 되어 있다.

trackBar.Value = Math.Min((int)((trackBar.Maximum * reader.Position) / reader.Length), trackBar.Maximum);


NAudio 트랙바 코드만 정리해보면

using NAudio.Wave;

using NAudio.FileFormats.Mp3;

private AudioFileReader reader;

private void timer1_Tick(object sender, EventArgs e)
{
    if (reader != null)
    {
        labelNowTime.Text = FormatTimeSpan(reader.CurrentTime); // 재생시간
        labelTotalTime.Text = FormatTimeSpan(reader.TotalTime);  // 총 시간
        trackBar.Value = Math.Min((int)((trackBar.Maximum * reader.Position) / reader.Length), trackBar.Maximum);
    }
}

private void trackBar_Scroll(object sender, EventArgs e)
{
    if (this.reader != null)
    {
        reader.Position = (trackBar.Value * reader.Length) / trackBar.Maximum;
    }
}


타이머가 동작되면 시간이 계산되어 움직이고, 타이머가 멈추면 같이 멈춘다.

트랙바에서 마우스로 찍으면 해당 지점에서 노래가 재생되도록 하고 싶어서 추가로 구현했다.

private void trackBar_MouseDown(object sender, MouseEventArgs e)
{
    if (this.reader != null)
    {
        double clickValue = ((double)e.X / (double)trackBar.Width) * (trackBar.Maximum - trackBar.Minimum);
        trackBar.Value = Convert.ToInt32(clickValue);
        reader.Position = (trackBar.Value * reader.Length) / trackBar.Maximum;
    }
}


NAudio 사이트 : http://naudio.codeplex.com/

에서 다운로드 하여 dll 파일을 참조로 포함시키고, 예제 소스코드를 참조할 수 있다.


// 재생시간 구현해보겠다고 삽질하면서 찾아낸 건데 나중에 도움이 될지도 몰라서 그냥 적어둔다.

public string Play_Duration(int iPostion)
{
    string strTimeLen = string.Empty;
    int iHour = iPostion / 3600;
    int iMinute = (int)((iPostion - (iHour * 3600)) / 60);
    int iSecond = (int)(iPostion - (iHour * 3600 + iMinute * 60));
    strTimeLen = String.Format("{0:D2}:{1:D2}:{2:D2}", iHour, iMinute, iSecond);

    return strTimeLen;
}

블로그 이미지

Link2Me

,
728x90

listView 에서 선택된 행과 이전행/다음행의 값을 서로 맞바꾸는 코드이다.

선택된 행도 같이 변경되게 처리했다.


private void lineSwapPreviousToolStripMenuItem_Click(object sender, EventArgs e)
{
    listView_previousSwap(listView1);
}

private void lineSwapNextToolStripMenuItem_Click(object sender, EventArgs e)
{
    listView_nextSwap(listView1);
}

private void listView_previousSwap(ListView LV)
{
    if (LV.SelectedIndices.Count > 0 && LV.SelectedItems[0].Index > 0)
    {
        int index = LV.SelectedItems[0].Index;  // 선택된 행의 인덱스
        int previousindex = index - 1; // 이전행의 인덱스

        string temp2 = LV.Items[index].SubItems[2].Text;  // 선택된 행의 값을 임시변수에 저장
        LV.Items[index].SubItems[2].Text = LV.Items[previousindex].SubItems[2].Text;
        LV.Items[previousindex].SubItems[2].Text = temp2;

        string temp3 = LV.Items[index].SubItems[3].Text;
        LV.Items[index].SubItems[3].Text = LV.Items[previousindex].SubItems[3].Text;
        LV.Items[previousindex].SubItems[3].Text = temp3;

        string temp4 = LV.Items[index].SubItems[4].Text;
        LV.Items[index].SubItems[4].Text = LV.Items[previousindex].SubItems[4].Text;
        LV.Items[previousindex].SubItems[4].Text = temp4;


        listView_FocusedLine(LV, previousindex);

    }
}

private void listView_nextSwap(ListView LV)
{
    if (LV.SelectedIndices.Count > 0 && LV.SelectedItems[0].Index < LV.Items.Count -1)
    {
        int index = LV.SelectedItems[0].Index;  // 선택된 행의 인덱스
        int nextindex = index + 1; // 다음행의 인덱스

        string temp2 = LV.Items[index].SubItems[2].Text;  // 선택된 행의 값을 임시변수에 저장
        LV.Items[index].SubItems[2].Text = LV.Items[nextindex].SubItems[2].Text;
        LV.Items[nextindex].SubItems[2].Text = temp2;

        string temp3 = LV.Items[index].SubItems[3].Text;
        LV.Items[index].SubItems[3].Text = LV.Items[nextindex].SubItems[3].Text;
        LV.Items[nextindex].SubItems[3].Text = temp3;

        string temp4 = LV.Items[index].SubItems[4].Text;
        LV.Items[index].SubItems[4].Text = LV.Items[nextindex].SubItems[4].Text;
        LV.Items[nextindex].SubItems[4].Text = temp4;


        listView_FocusedLine(LV, nextindex);

    }
}


private void listView_FocusedLine(ListView LV, int index)
{
    if (index < 0 || index >= LV.Items.Count || LV.Items.Count == 0) return;
    LV.SelectedItems.Clear();
    LV.Items[index].Selected = true;
    LV.Items[index].EnsureVisible();
    LV.Items[index].Focused = true;
    LV.Focus();
}


블로그 이미지

Link2Me

,
728x90

난 아직 디버깅 사용법이 약하다. 그냥 무식하게 디버깅을 한다.

이번 기회에 디버깅을 제대로 좀 배워볼까 하고 적어둔다.


F9 : BreakPoint (멈춤지정) : 조사해보고 싶은 곳에 지정한다.

F5 : Debug 모드 실행 : 코드 전체에 대해 실행하고 문제가 있는 곳을 반환한다.

F5 : 다음 BreakPoint 까지 작동


F10 : Step Over (다음줄로)

F11 : Step Into (함수 안으로 들어감)

Alt + 7 : Call Stack 윈도우

Alt + 3 : Watch 윈도우

Shift + F11 : 현재 루틴에서 빠져나온다.

Shift + F5 : 강제로 디버깅 모드 종료


블로그 이미지

Link2Me

,
728x90

데이터를 읽고 쓰기 위해 C# 에서는 stream(스트림) 객체를 사용한다.

닷넷의 스트림 관련 클래스들은 System.IO 네임스페이스에 정의되어 있다.


HDD(하드디스크), SSD 에 있는 파일을 읽고 쓰는데 사용되는 기본 클래스는 FileStream 이다.


var fs = new FileStream(srcFile, FileMode.Open, FileAccess.Read, FileShare.Read);

- srcFile : 파일의 경로 + 파일명

- FileMode : https://msdn.microsoft.com/ko-kr/library/system.io.filemode%28v=vs.110%29.aspx

- FileAccess : https://msdn.microsoft.com/ko-kr/library/4z36sx0f%28v=vs.110%29.aspx

- FileShare : https://msdn.microsoft.com/ko-kr/library/system.io.fileshare%28v=vs.110%29.aspx


byte[] buffer = new byte[4]; // 스트림으로부터 읽은 데이터를 저장할 크기가 4인 바이트 배열 버퍼
var fs = new FileStream(srcFile, FileMode.Open, FileAccess.Read, FileShare.Read);
fs.Read(buffer, 0, 4);
fs.Close();


byte[] buffer;
using (var fs = File.Open(srcFile, FileMode.Open))
{
    buffer = new byte[fs.Length];
    fs.Read(buffer, 0, (int)fs.Length);   
}


buffersize : 스트림을 입출력 효율 향상을 위해 버퍼를 사용한다.

HDD 에서 블럭 단위로 몽땅 읽어와 버퍼에 쌓아두고 다음에 읽을 때는 버퍼에서 읽어 입출력 속도가 향상된다.


FileStream 은 입출력 대상이 byte 배열로 고정되어 있어 실제 사용하기에는 불편하다.

응용 프로그램은 문자열, 실수, 정수와 같은 고수준 타입을 다루는 경우가 더 많다.

텍스트파일은 아주 단순한 구조로 되어 있지만 인코딩 방식에 따라 ANSI, UTF8, Unicode, UTF7 등 여러가지 포멧이 있다. 파일을 읽어올 때 인코딩 방식을 잘못 읽어오면 파일이 깨질 수 있다.

닷넷은 입출력 과정에서 자동으로 문자열 변환을 수행하는 StreamReader, StreamWriter 클래스를 제공한다.

using 문은 자동으로 해당 코드를 수행한 후 스트림의 Dispose() 메소드를 호출한다. using 문을 사용하면 스트림이 닫히지 않는 상태에서 해당 파일을 사용하려고 할 때 발생하는 버그를 걱정하지 않아도 된다.


//인코딩
C# 에서의 자료형을 갖는 데이터 -> 스트림(byte[])
EUC-KR(ANSI, MS949)
  - 영어(숫자,특수) 1칸
  - 한글 2칸
UTF-8(C#의 기본 인코딩 방식)
  - 영어(숫자,특수) 1칸
  - 한글 3칸
UTF-16
  - 영어(숫자,특수) 2칸
  - 한글 2칸


using (StreamReader sr = new StreamReader(fileName, System.Text.Encoding.UTF8))
{
    readText = sr.ReadToEnd();
    sr.Dispose();
}


HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader readerPost = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8, true);
resResult = readerPost.ReadToEnd();

readerPost.Close();

string txt = string.Empty;
while ((txt = result.ReadLine()) != null)  // 한줄씩 읽어서 구분자로 나눠서 배열로 담아서 처리
{
    //1. 파싱
    string[] item = txt.Split(';');
    //2. Member 배열 객체 생성, m 은 참조변수
    Member m = new Member(item[0], item[1], item[2], item[3]);
    //3. Member 배열(ArrayList) 삽입
    list.Add(m);
} //while
result.Close();


using(Stream stream = File.Create(fileName))
{
    using (var textExport = new StreamWriter(stream, Encoding.UTF8))
    {
        foreach (ListViewItem row in LV.Items)
        {
            textExport.WriteLine(row.SubItems[0].Text);
            textExport.WriteLine(row.SubItems[1].Text);
            textExport.Write(Environment.NewLine);
        }

        textExport.Flush(); // flush from the buffers
        textExport.Close();
    }
}


여러 객체에 대해서는 여러개의 using 문을 사용하면 된다.

위의 코드에 using 을 두번 사용하고 중괄호{} 두번 사용했는데 첫번째 것은 중괄호를 생략해도 된다.

using(Stream stream = File.Create(fileName))
using (var textExport = new StreamWriter(stream, Encoding.UTF8))
{
    foreach (ListViewItem row in LV.Items)
    {
        textExport.WriteLine(row.SubItems[0].Text);
        textExport.WriteLine(row.SubItems[1].Text);
        textExport.Write(Environment.NewLine);
    }

    textExport.Flush(); // flush from the buffers
    textExport.Close();
}

클래스나 구조체같은 복잡한 이진 데이터를 저장할 때는 BinaryReader, BinaryWriter 클래스를 사용한다.

아직 이걸로 작성해본 것이 없어서 다음에 작성하게 되면 자료를 추가 보완할 예정


MemoryStream 참조하면 도움되는 사이트 http://net-informations.com/q/faq/memory.html


'C# > 문법 및 기능' 카테고리의 다른 글

C# Directory (폴더) 생성 및 삭제  (0) 2016.01.16
C# 시간 메소드  (0) 2016.01.15
C# int.TryParse  (0) 2015.12.25
C# 오버라이딩(Overriding) 개념 이해  (1) 2015.12.21
C# 메소드 오버로딩 개념잡기  (0) 2015.12.21
블로그 이미지

Link2Me

,
728x90

이번에 자동 업데이터를 만들어서 실행파일에 같이 실행되게 하고 싶었다.

그래서 관련 내용을 찾아서 테스트해보고 적어둔다.


1. 별도로 만든 자동업데이터의 파일을 추가한다.




SubtitleManager 솔루션에 관련된 프로젝트 파일을 추가했다.


빌드가 같은 폴더에 되도록 경로 변경을 하는 방법이다.




빌드 종속성을 눌러서 순서를 정해줘야만 제대로 동작이 된다.





블로그 이미지

Link2Me

,
728x90

C# 자동 업데이터 구현 흐름도이다.

개념부족, 코드 활용 부족으로 몇일동안 만들어본 자동 업데이터의 코드 구현 흐름도이다.


Setup 파일은 Inno Setup 을 이용해서 만들고

자동업데이터는 설치된 폴더의 실행파일만 덮어쓰기 하는 형태다.

Program Files 폴더는 관리자권한이 있어야 파일 덮어쓰기가 가능하다.


서버에 있는 setup.txt 파일과 PC에 설치된 setup.txt 파일의 내용을 비교하여 버전이 서로 다르면 Updater 파일을 실행하고, 현재 실행된 파일은 종료시킨다.


Updater 파일에서도 버전 비교를 하는 이유는 업데이터 파일만 단독으로 실행했을 경우를 고려해서다.

보통은 업데이트할 내용을 팝업창으로 제공하지 않고 바로 서버에서 파일을 다운로드하여 덮어쓰기를 한다.

하지만 난 업데이트할 내용을 팝업창으로 제공되도록 보여줬다.




블로그 이미지

Link2Me

,