본문 바로가기
카테고리 없음

[C#]WinForm print(인쇄)printpreviewdialog datagridview 표현 (꿀정보) e.HasMorePages,세로 복사

by IT황구 2021. 1. 6.
728x90
반응형

오늘 알아볼것은 WinForm에 datagridView의 내용을 넣어볼것이다. 그리고 페이지 여러개가 있을경우도 생각해볼것이다.

c#은 그나마 좀 발전된거라서 print클래스.showdialog 이런거해서 한번에 뚞딱 복사할줄 알았는데

진짜로 너무 귀찮았고 진짜로 지옥같았다 아무리 뒤져도 다 똑같은 복붙코드 ..

.

1. 찾으면 무슨 자기네들 클래스 추가해서 쓰라고함.(이해안됨)

2. report인가? 이걸로 만들라고함 (원하는게 아님)

3. 한 3일간 구글 네이버만 본 결과 결국 모든 코드들이 다 똑같음 서로 한 코드 퍼다나르기뿐

원하는 정보는 없었음.

오늘 내가 할것.

용지 여백에 맞춰서 datagridview 넣고 한 페이지에 뿌려주는 개수 정해서 페이지 넘겨주는 기능까지.

코드 많아보일수도 있는데 내가 쓴게 아마 가장 적은게 아닐까 싶다.

그리고 가만보면 다 비슷비슷하다. 이렇게해야 나중에 다른형식 만들기 가능

클래스 가져다쓰면 원하는대로 못해서 안좋았다.

결과창:

자료가 40개가 넘으면 페이지를 하나 더 만들고 2페이지로 넘어가서 나머지를 뿌려준다.

페이지번호가 2인것 보이시죠??

만들기)

1.if(printDialog1.ShowDialog()==DialogResult.OK){

printdocument1.Print();

} 로 인쇄 메뉴를 완료하고,

우리가 할것은 미리보기를 눌렀을때 나오게 하는것이다.

printPreviewDialog는 저기 위에 결과창처럼 미리보기 창이다. Dialog니깐 ShowDIalog로 보여주면 된다.

printDocument는 printPreviewDialog의 document속성안에 들어갈것이다.

즉 보여줄 내용들이 저기에 들어가게 될것이다.

printDialog는 흔히 우리가 인쇄를 눌렀을때 인쇄기 고르고 확인하는 그 창이다. 일단 안쓴다 나는 . 미리보기만 하므로.

gridView에 뿌리는것은 생략하겠다.

이제 printDocument에 printPage 이벤트를 연결한다 이것은 보여줄 페이지의 내용을 설정하는 이벤트이다.

순서대로 누르면 된다.

나는 인쇄 미리보기라는 메뉴를 만들어서 이것을 눌렀을때 저 미리보기 창이 나오게 했다.

코드를 보면 Dialog가 꺼졌을때 cnt와 pageNo를 다시 초기화 해주는데 이것은 나중에 알게될 것이다.

 

 private void 인쇄미리보기ToolStripMenuItem_Click(object sender, EventArgs e)
        {
        

            printPreviewDialog1.Document = printDocument1;
            if (printPreviewDialog1.ShowDialog() == DialogResult.Cancel)
            {
                cnt = 0;
                pageNo = 1;
            }
        }

일단 전역변수로 int cnt=0; int pageNo=1;을 설정해준다.

cnt는 행의 개수를 계속 세어주는 역할을 할것이고 pageNo는 결과창에 보이는 페이지번호 : 1 ,2 이것을 나타내주는

중요한 역할을 할것이다.

2)이제 printPage를 채워볼것이다. 설명은 아래에.

int cnt = 0; //아까 말한 전역변수 행의 개수를 셀것임.
        int pageNo = 1;//페이지 넘버를 담당하는 전역변수
        private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
          
            int dialogWidth=528;//페이지 전체넓이 printPreviewDialog.Width
         
           
           
            StringFormat sf = new StringFormat();//컬럼 안에있는 값들 가운데로 정렬하기위해서.
            sf.Alignment = StringAlignment.Center;
            int width,width1;//width 는 시작점 위치, width1은 datagrid 1개의 컬럼 가로길이
            int startWidth = 10;//시작 x좌표
            int startHeight = 140;//시작 y좌표
            int avgHeight = dataGridView1.Rows[0].Height;//gridview 컬럼 하나의 높이
            int i,j;//반복문용 변수
            int temp = 0;//row개수 세어줄것. cnt의 역할
           


            e.Graphics.DrawString("전체 사원대장(사번순)",new Font("Arial",20,FontStyle.Bold),Brushes.Black, dialogWidth / 2,40);
            e.Graphics.DrawString("인쇄일 : " + DateTime.Now.ToString("yyyy/MM/dd"), new Font("Arial", 13) , Brushes.Black, dialogWidth - 20 , 80);
            e.Graphics.DrawString("페이지번호 : " + pageNo, new Font("Arial", 13), Brushes.Black, dialogWidth - 20, 100);
            
            for (i = 0; i < dataGridView1.ColumnCount; i++)//columnCount는 일정
            {
                if (i == 0)
                {
                    width = 0;
                    width1 = this.dataGridView1.Columns[i].Width + 15;
                }
                else if (i >= 1 && i <= dataGridView1.ColumnCount - 2)
                {
                    width = this.dataGridView1.Columns[i - 1].Width + 15;
                    width1 = this.dataGridView1.Columns[i].Width + 15;
                }
                else
                {
                    width = this.dataGridView1.Columns[i - 1].Width + 15;
                    width1 = this.dataGridView1.Columns[i].Width + 40;
                }

                RectangleF drawRect = new RectangleF((float)(startWidth + width), (float)startHeight, (float)width1, avgHeight);
                
                e.Graphics.DrawRectangle(Pens.Black, (float)(startWidth + width), (float)startHeight, (float)width1,avgHeight);
                
                // e.Graphics.FillRectangle(Brushes.LightGray, (float)(startWidth + width), (float)startHeight, (float)width, avgHeight);
                e.Graphics.DrawString(dataGridView1.Columns[i].HeaderText, new Font("Arial",12,FontStyle.Bold), Brushes.Black,drawRect,sf);

                startWidth += width;
               
              
            }
            startHeight += avgHeight;
           
            for (i = cnt; i < dataGridView1.RowCount-1; i++)
            {
                startWidth = 10;//다시 초기화
                for (j = 0; j < dataGridView1.ColumnCount; j++)
                {
                    if (j == 0)
                    {
                        width = 0;
                        width1 = this.dataGridView1.Columns[j].Width + 15;
                    }
                    else if(j>=1 && j<=dataGridView1.ColumnCount-2)
                    {
                        width = this.dataGridView1.Columns[j - 1].Width + 15;
                        width1 = this.dataGridView1.Columns[j].Width + 15;
                    }
                    else
                    {
                        width = this.dataGridView1.Columns[j - 1].Width + 15;
                        width1 = this.dataGridView1.Columns[j].Width + 40;
                    }

                    RectangleF drawRect = new RectangleF((float)(startWidth + width), (float)startHeight, (float)width1, avgHeight);

                    e.Graphics.DrawRectangle(Pens.Black, (float)(startWidth + width), (float)startHeight, (float)width1, avgHeight);

                    // e.Graphics.FillRectangle(Brushes.LightGray, (float)(startWidth + width), (float)startHeight, (float)width, avgHeight);
                    e.Graphics.DrawString(dataGridView1.Rows[i].Cells[j].FormattedValue.ToString(), new Font("Arial", 9), Brushes.Black, drawRect, sf);
                    startWidth += width;

                   
                }
                startHeight += avgHeight;
                temp++;
                cnt++;
            
                if (temp%40==0)
                {
                   // MessageBox.Show("40으로 나눠졋다");
                    e.HasMorePages = true;
                    pageNo++;
                    return;

                }
         
            
               
            }
            
            


        }
       

설명)

dialogWidth는 preview에 보여주는 가로길이를 구한것이다.

이게 처음엔 작게나오다가 화면 확대후에 다시 인쇄 미리보기를 누르면

width도 변경되어서 결과가 이상하게 변해가지고

MessageBox.Show(convert.ToString(dialogWidth);로 값을 확인한 후에 고정값으로 넣어주었다.

여기서 알아야할것은 우리는 이제 다 직접 좌표를 찍어가면서 그려야한다.

내가 만든것은 보통은 맞게될것 같은데 페이지 안넘어가고 (컬럼 9개 기준)

일단 DrawString을 보면 (쓸 내용,폰트,브러쉬,시작x,시작y좌표)

이렇게 생겼다.

내가 처음에 dialogWidth/2 , 40을 한것은

미리보기 페이지의 절반에서 시작해서 글을 쓰라는것이다. y좌표는 아래로 40만큼 떨어졌다는 뜻이다.

결과창에서 보면 중간에 사원대장이라는 말이 있을것이다. 그부분이다.

그 아래도 마찬가지고 내가 쓸 위치에 따라서 값만 조금씩 바꿔놓은것을 볼 수 있을것이다

첫번째 for문에서는

headerText에 해당하는 부분이다. gray색으로 칠해진부분인데

i=0 i<컬럼수 컬럼이 9개이므로 9가 나온다.

drawRectagle 메소드는 (펜색,시작x좌표,시작y좌표,x너비,y너비)이렇게 생겼다.

i=0일때는 처음에 컬럼을 찍을때

width에 0을 주어서 (0,y좌표)에서 시작을하고

width1은 x너비이다. 이것은 컬럼마다 크기가 각각 다르기때문에 (ex 위에만봐도 이메일과 번호는 필요한 사이즈부터 다르다)

그 컬럼에 맞춰서 Width를 구해준것이다. +15는 내가 좀더 넓게쓰고 싶어서 준것이다.

이제 한개를 그렸다 사각형을.

그러면 좌표를 옮겨주어야하는데 y좌표는 옮겨줄필요가 없다. 아직 한줄에 다 컬럼명을 못썼으므로.

그럼 x좌표를 옮겨주는데 내가 아까 그린 dataGridview의 x좌표만큼 옮겨주면 사각형을 그리고 바로 그 옆에 또 그리게 될것이다.

따라서 else if에 width=~~.Column[i-1].Width 이게 있을것이다.

이것은 무슨뜻이냐?

i-1이 아닌 i로 한다면

내가 두번째 사각형을 그릴때 2번째 컬럼의 width에 맞춰서 칸이 띄어지므로 사이즈가 맞지 않게된다.

width는 시작점이다. 그러므로 이전의 사각형의 크기만큼 옮겨줘야 하므로 i-1이 들어간다.

width1은 너비다 . 너비는 지금 그리는 컬럼의 사이즈에 맞춰야하므로 i로 되어있는것이다.

else는 왜 붙어있냐 ?

내가 email을 쓰려고 하다보니 사이즈가 좁아서 칸이 밀렸다.

따라서 임의로 그냥 더 늘려주고싶어서 만든것이다. email이 맨 뒷부분이라서 이렇게 된것이다.

width1(너비)부분을 보면 + 40으로 한 이유가 email 때문이다. 그냥 해도된다.

이제 저렇게 기본 설정을 해주고

DrawRectagle로 사각형을 먼저 그려준다. 그리고

FillRectangle로 사각형을 그레이색으로 채워주고

DrawString으로 글자를 써주면 된다.

DrawString 메소드는 ("글자",폰트,브러쉬,시작사각형위치,정렬);

정렬부분에 아까 내가 써준 sf를 쓴것이고

시작사각형 위치는

RectagleF 구조체를 따라야한다

이것은 (시작x,시작y,x너비,y너비)

사실 drawRectagle 메소드랑 똑같다..

이렇게 하고 써준후에

startWidth+=width; 이것은 무슨뜻인가 ?

시작 위치가 계속 dataGridview 컬럼 너비만큼 늘어나므로 시작위치를 옮겨주란 뜻이다.

시작위치를 안바꿔주면

100에서 시작

i =1 120

i =2 115

i =3 120 이런식일것이다 하지만 추가해주면

i=1 120

i=2 135

i=3 160 이렇게 늘어날것이다.

for문이 끝나고

startHeiht+=avgHeight;가 있는데

이것은 이제 컬럼을 표시했으니 그 안에 데이터를 하나씩 그려서 찍어줄것인데

이제 한칸 내려가서 찍어줘야하니 컬럼의 높이만큼 시작 y축도 내려주라는 뜻이다.

2중 for문에서 dataGridView.Rowcount-1인 이유는

RowCount를 하면 HeaderText(컬럼명)의 개수까지 행으로 친다.

데이터가 5개면 6개로 나올것이다.

그러므로 -1을해야 5로 정상적인 개수로 되기 때문이다.

이제 배열에 값 넣듯이 쭉쭉 넣어주면 되는데 여기서 다른점은

i에서 시작점이 0이아닌 cnt다.

이것의 역할은

표현해야할 데이터가 많을때 페이지를 넘겨주어야한다.

한페이지에 200개를 다 보여줄 순 없기 때문이다.

나는 임의로 40개씩만 보여주고 나머진 뒤로 넘길것이다.

cnt를 전역변수로 설정한 이유는 뒤에 알아보자.

cnt++;

를 동시에 올려주면서 둘다 행을 세는 개수인데

if (temp%40==0)

{

// MessageBox.Show("40으로 나눠졋다");

e.HasMorePages = true;

pageNo++;

return;//이것이 없으면 for문이 계속 돌아간다.

}

모든게 이 코드때문이다.

행이 40개가 넘어서 40으로 나누어떨어진다 (즉 40개다)

그러면 e.hasMorePage라는것을 이용해 true로 바꿔서 페이지를 하나 더 만든다.

c#은 직접 페이지를 만들어줘야한다.

보통은 false인데 이렇게 하나를 더 만들면

print_page 이벤트를 한번 더 호출하게 된다.

즉 이 메소드 부분을 한번 더 실행한다.

한번 더 실행하면

지역변수로 설정했던 값들은 리셋이 되기때문에

만약에 40개를 찍었다면

41번레코드부터 찍어야한다. 그러므로 전역변수가 필요하다.

i가 cnt인 이유는 41번부터 찍으려면 전역변수로부터 숫자를 불러와서 이어서 찍기 위함이었다.

이렇게 하면 40개마다 새로운 페이지가 생기고 계속 찍힐것이다.

e.HasMorePages의 기본 설정은 false라서 보통은 페이지가 한개만 만들어진다.

이제

printPreviewDialog1.Document = printDocument1;

if (printPreviewDialog1.ShowDialog() == DialogResult.Cancel)

{

cnt = 0;

pageNo = 1;

}

여기 이렇게 한 이유는

내가 만약에 저 미리보기를 껐을때

프로그램이 종료되지 않아서 숫자가 계속 남아있다.

이때 다시 누르면 cnt가 이어서 증가하게 되므로

다시 리셋해주어야 또 눌렀을때 처음과 같은 화면이 나온다.

여기까지 하면 된다..그럼 이만..(유령)

===========================

추가(2018.06.19)

1.

이전 버전에는 글자가 가운데 정렬은 되지만 상하 가운데 정렬이 안되어서 위로 달라붙었는데 해결 방법은

StringFormat sf = new StringFormat(); 에서

sf.Alignment=StringAlignment.Center;만 해주면 사각형 위에만 달라붙는다

따라서 아래 LineAlignMent를 추가해주면 정확히 정 가운데에 써지게 된다.

sf.LineAlignment = StringAlignment.Center;

2.

세로로 출력하고 싶을경우

인쇄미리보기 이벤트에다가

printDocument1.DefaultPageSettings.Landscape = true;//세로로 출력

이것을 넣어주면 된다

printDocument1은 우리가 복사하는 a4용지라고 생각하면 되는데 그 기본 세팅을 세로로 바꿔주는 옵션이다.

이 이후에 복사하면 세로로 나오게 된다 .

private void 인쇄미리보기ToolStripMenuItem_Click(object sender, EventArgs e)

{

printPreviewDialog1.Document = printDocument1;

* printDocument1.DefaultPageSettings.Landscape = true;//세로로 출력

if (printPreviewDialog1.ShowDialog() == DialogResult.Cancel)

{

cnt = 0;

pageNo = 1;

}

}

 

다른 내용은

blog.naver.com/rbals0445 에도 있다. 화이팅!!

 

728x90
반응형