안녕하세요 C# 윈도우 폼 공부 중에 이해가 안가는 부분이 있어서 질문 드립니다.
버튼의 클릭을 이벤트핸들러로 하여 픽쳐박스에 이미지를 넣은 후에 이미지를 Dispose할려는데
매개변수가 없다고 오류가 납니다.
아래와 같이 코드를 짰습니다.
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Image = openImage;
openImage.Dispose();
}
그래서 이벤트핸들러 내의 이벤트가 모두 종료되어야 폼에 적용되서 이미지가 없어서 오류가 난다고 생각하고있는데
아래같이 중간에 쇼로 다른 행동을 넣으면 Dispose가 됩니다.
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Image = openImage;
MessageBox.Show(“aaaaa”);
openImage.Dispose();
}
그래서 다른 이벤트를 넣어도 되는지 궁금해 텍스트박스를 넣으면 또 동작이 안됩니다.
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Image = openImage;
textBox1.Text = “4444”;
openImage.Dispose();
}
이벤트핸들러는 안에 내용이 모두 동작 후에 폼에 반영되어서 image가 이미 dispose되어서 픽쳐박스에 적용할 이미지가 없어서 오류가 나고
show는 새로운 폼을 생성하기에 폼을 생성하기 전까지 이벤트들을 모두 동작시키고
폼을 새로 생성하여 메인 폼에 픽쳐박스 이미지가 적용 된 후 Dispose를 하는 것 같다고 생각하는데
알려주신 코드로는 픽쳐박스의 새로운 이미지를 넣은 후에 Dispose로 이전에 사용한 이미지의 리소스 제거하는 것으로 이해 했습니다.
다른 의문점으로
이미지 하나만을 사용할 때 Dispose를 하면 안되는 건가요?
위 2번째 코드에서 Dispose를 해도 폼에서는 이미지가 그대로 남아있기에 이미지 하나일 때도 Dispose를 하는 게 좋을까 해서 짰썼습니다.
이 이미지를 계속 사용하면 Dispose하면 안되겠지만 한번 화면에 띄우기만 하는 것이라면 Dispose 가능할까요?
이와 별개로 이벤트 핸들러 내에 각각의 코드들이 언제 폼에 적용되는지 알고 싶습니다.
코드를 하나씩 진행시켰을 때
1번은 이벤트 핸들러 버튼클릭이 끝나고나서 폼에 이미지가(또는 다른 코드들) 적용되었는데
2번째 코드에서 show로 다른 폼을 생성하였을 때 그 위에있던 코드들이 폼에 적용되었는데
실제는 코드 한줄씩 폼에 반영되는데 디버그 과정에선 보여주지않는건지
아니면 폼에 적용되는 시기가 따로있는지 알고 싶습니다.
일단 위의 코드는 "이벤트 핸들러"의 관점에서 보는게 아니라 "메인 스레드와 UI 스레드"의 관점에서 봐야합니다.
메인 스레드에서는 this.pictureBox1.Image = openImage;와 openImage.Dispose();는 순식간에 끝납니다.
하지만 this.pictureBox1.Image = openImage;가 호출되면 별도의 스레드(UI 스레드)에서 이미지 데이터를 가져와서 pictureBox1에 표시해주는 작업을 하게됩니다. 이렇게 폼 화면에 이미지를 표시하는데 "시간"이 소요되는데, 메인 스레드에서 openImage.Dispose();를 통해 이미지를 삭제해버리니까 UI 스레드에서 작업이 실패하는겁니다.
그럼 나중에 Dispose를 하면 폼의 화면에 이미지가 사라져야 하는 것 아닌가요?
→ this.pictureBox1.Image와 openImage는 바인딩(동기화)되어 있는게 아니라서 openImage를 삭제한다고 해서 이미 폼에 뿌려진 이미지가 바로 사라지지는 않습니다. 대신, 리프레쉬 이벤트가 발생되면 openImage를 다시 참조하려고하는데 그때 openImage가 없기 때문에 에러가 발생됩니다.
사소한 의문이지만
3번째 답변에서 메시지 루프를 타야 폼에 적용된다는 것은 제 2번째 코드에 show를 넣었을 때 show가 메시지 루프를 동작시키고 다시 이벤트 핸들러로 돌아온다는 건가요?
이게 맞다면 메시지 루프를 동작시키는 메소드도 따로 있나요? 또 show같은 자기 동작을 하면서 메시지 루프를 타는 메소드들이 무엇이 있나요?
윗분의 응답에서는 폼에 적용되는게 이벤트핸들러가 종료 후 메시지 루프에 진입하여 폼에 적용된다고 하였는데 단지 시간이 지나면 메시지 루프와 상관없이 폼에 적용된다는 뜻인가요? 아니면 시간이 지나면 자동으로 메시지 루프에 진입한다는 건가요?
messageBox.show 대신에 쓰레드 슬립을 넣어서 긴시간 동작시켜도 오류가 났습니다.
다른 질문으로 pictureBox.Image는 참조라 하셨는데 그럼 나중에 Dispose를 하면 폼의 화면에 이미지가 사라져야 하는 것 아닌가요? 그러나 Dispose후에 값은 사라지는 걸 확인했는데 폼 화면에는 이미지가 표시되기에 왜 남아있는지 궁금합니다.
스레드 관점으로 보아서
this.pictureBox1.Image = openImage;가 호출되면 별도의 스레드(UI 스레드)에서 이미지 데이터를 가져와서 pictureBox1에 표시해주는 작업을 하게됩니다. 라고 설명 주셨는데 이 코드 밑에 thread.sleep()을 적용하여 오랜시간 지나도 적용 되지않았습니다. 이에 UI 스레드도 슬립할 수 도 있다 생각하여
while(true){ textbox1.text = “1”;} 로 위 image와 전혀 상관없는 무한 루프를 생성하여 적용되기 까지 시간을 기다렸는데도 적용 되지않았습니다.
Application.DoEvents(); 를 써서 dispose 하면 잘 동작 되는 것을 했습니다.
이 과정이 제가 이해한 바로는
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Image = openImage; // Application 쓰레드의 메시지 큐에 메시지를 쌓는다.
Application.DoEvents(); // 메시지 큐에있는 메시지를 처리한다.
openImage.Dispose();
}
이러한 과정으로 진행되어서 동작하는게 맞나요?
결국 그럼 아래와 같이 코드를 작성하면
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Image = openImage; //Application 쓰레드의 메시지 큐에 메시지를 쌓는다
} // 이벤트 핸들러를 빠져나가면서 Application의 메시지 큐를 동작(Application.DoEvents(); 동작이 포함됨)
이벤트 핸들러를 빠져나가면서 Application.DoEvents();이 적용된다고 생각해도 되는 건가요?
그렇다면 본 글의 2번째 코드에서 동작이 되었던 이유가
private void button1_Click(object sender, EventArgs e)
{
this.pictureBox1.Image = openImage;
MessageBox.Show(“aaaaa”);
openImage.Dispose();
}
MessageBox.show(); 에 Application.DoEvents();이 포함되어있어서 인가요?