티스토리 뷰

반응형

WPF는 여러분이 만든 프로그램을 다양하게 표현할 방법들을 제공하고 있습니다.
이번 강좌는 이미지를 애니메이션처럼 보이게 하는 방법을 소개합니다.

(워어...원래 원문 블로그에서 작성자가 20명이랍니다. 그러다니보니 이미지를 애니메이션처럼 만드는 방법을 자꾸. 자꾸. 자꾸. 소개하네요 ㅋ)

먼저 파일을 다운 받으세요.


프로젝트를 열어보면 아래와 같은 파일이 있을 겁니다.
   App.xaml
   MapControlClass.cs
   TrafficMap.
csproj
   TrafficMapWindow.cs
   TrafficMapWindow.xaml.cs

교통 지도 데이타워싱턴은 10분마다 갱신된 교통지도 데이터를 제공합니다.
아래 그림은 2004년 1월의 이미지 입니다.

우측 메뉴에 보면 Select interval이라고 시간간격을 입력할 수 있게 해놨죠?
저기에 입력한 시간대로 이미지가 애니메이션처럼 움직이게 됩니다.

WSDOT Puget Sound Traffic Map

교통 지도 데이터 캐쉬에 저장하기맨처음은 캐쉬에 저장된 이미지가 출력될 겁니다.
그 순간 바로 다음 이미지가 캐쉬에 담기고 이게 화면에 출력될때는 그 다음 이미지가 캐쉬에 담기게 됩니다.

(더블 버퍼링 같은거네요 ^^);

Timer interval actions


Interval sequence

.NET Framework 타이머 사용하기
.NET Framework 2.0 타이머 객체를 몇개 제공 하고 있는데
그중에 System.Timers.Timer과         System.Threading.Timer과         System.Windows.Forms.Timer가 있겠습니다.근데 이 타이머는 WPF에서는 바로 사용 할 수가 없습니다. 흠..
WPF UI랑은 좀 안맞아서 그래요.

예를 들면,
System.Timers.Timer 객체로 WPF 응용프로그램을 만들었을 때
Timer.Elapsed e이벤트가 일어나면 다음과 같은 예외가 떨어집니다.

System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.

물론, 사용할 수 있게 하는 방법이 있지요. 코드를 좀 더 추가해서 말이죠.

이 페이지가 WPF에서 타이머를 사용하는 방법을 가르쳐 줍니다.
Disable Command Source Via System Timer Sample

DispatcherTimer 가 뭘까요
타이머...타이머..타이머..뭔가 많이 복잡했죠?
하하...미리얘기했쟎아요. 2.0 이라고.
우리는 3.0을 배우쟎아요? ㅎㅎ

그렇다면?
당근 3.0에서는 이런게 많이 좋아졌죠.
.NET Framework 3.0 에는 DispatcherTimer 객체가 WPF 타이머를 쓰기 편하게 해준답니다.

TrafficMapWindow.xaml.cs 파일을 보면
DispatcherTimer 객체를 사용해 2초마다 한번씩 타이머를 돌리는 곳이 보일 겁니다.

아래 소스를 보시죠.

private DispatcherTimer intervalTimer = new DispatcherTimer();

public void OnWindowLoaded(object sender, EventArgs e)
{
 
int newInterval = Intervals.SelectedIndex + 1;
  intervalTimer.Interval =
new TimeSpan(0, 0, newInterval);
  intervalTimer.Tick +=
new EventHandler
(UpdateMapEvent);
  InitDateAndTime();
}

버튼을 클릭해야 시작할 수 있게 해놨습니다.

public
void OnPlayClick(object sender, EventArgs e)
{
  ...
  intervalTimer.Start();
  ...
}

마찬가지로 멈춤 버튼도 있습니다.

public void OnPauseClick(object sender, EventArgs e)
{
  ...
  intervalTimer.Stop();
  ...
}

타이머가 돈다면 당연히 Tick이벤트도 생기겠죠
interval이 사용되어지고 나면
DispatcherTimer.Tick
이벤트가 발생합니다.
이놈은 다음과 같은 일을 합니다.

  1) 캐쉬에 있던 이미지를 화면에 뿌려주기
  2) 화면에 있는 시간을 업데이트 하기
  3) 다음 이미지를 캐쉬에 담기 위해 받아오기

void UpdateMapEvent(object sender, EventArgs e)
{
 
// Update the Image control with the cached BitmapImage.
 
if (isDownLoaded == true)
  {
    TrafficMap.Source = mapImage;

 
  // Update clock to reflect timestamp of traffic image.
   
string tmpString = mapImage.UriSource.ToString();
    int hours = Convert.ToInt32(tmpString.Substring
      (tmpString.Length - 8, 2));
    int minutes = Convert.ToInt32(tmpString.Substring
      (tmpString.Length - 6, 2));
    hourHandTransform.Angle =
      (hours * 30) + (minutes / 2) - 180;
    minuteHandTransform.Angle = (minutes * 6) - 180;
  }

 
// Get the next or previous map image.
  GetMapImage();
}

알림 : 이미지가 없으면 업데이트 되지 않습니다. (당연한 말씀 감사합니다.)

Graphics 를 이용하며 정보 전달
맵 이미지 상단에 보면 시간이 있는데요
시간이 가는걸 보면서 우리는 지금 제대로 업데이트 되고 있구나 하고 제일 빨리 생각할 수 있죠.
Clock synchronized to traffic map timestamp
 

URI 참조의 유효성 검사?
이미지 파일명이나, URI등의 유효성이 검사가 참 큰 과제인데....
그게 WSDOT 는 가끔씩 교통 지도 이미지 전송을 누락 시킬때가 있거든요.

자, 그래서 아래와 같이 문제를 해결해 보았습니다.

public void GetMapImage()
{
  isDownLoaded =
false;

 
string imageFileString = mapControl.GenerateMapFileName();

  mapImage =
new BitmapImage();

  mapImage.DownloadCompleted +=
   
new EventHandler(mapImage_DownloadCompleted);

  mapImage.Dispatcher.UnhandledException +=
   
new DispatcherUnhandledExceptionEventHandler
    (Dispatcher_UnhandledException);

  mapImage.BeginInit();
  mapImage.UriSource =
new Uri(imageFileString);
  mapImage.EndInit();

 
if (mapControl.UpdateIntervalTime() == false
)
  {
    intervalTimer.Stop();
  }
}

BitmapImage.DownloadCompleted 이벤트에서 이미지가 성공적으로 다운로드됐을 때 처리를 해주는 것입니다.

public void mapImage_DownloadCompleted(object sender, EventArgs e)
{
  isDownLoaded =
true
;
}

마찬가지로 다운로드가 안됐다면 WebException 으로 떨어집니다.
그런데 try/catch를 잘 걸어놨는데 그걸 못 잡아내고 WPF가 정상작동을 하지 않는다면
어떻게 처리를 하겠습니까?

이렇게 해결을 해야지요
Dispatcher.UnhandledExeception 이벤트가 WebException 을 정의합니다.뭔 난리가 나도 저 예외로 가라구요 ^^

public void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
  isDownLoaded =
false;
  e.Handled =
true
;
}

DispatcherUnhandledExceptionEventArgs.Handled 속성은 default값이 false이며
한번만 true로 바꾸면 컴퓨터를 끄기 전까지는 계속 적용 됩니다.

Dispatcher 객체에 대해 더 궁금하시면
Windows Presentation Foundation Threading Model,
Dispatcher
여기 두군데 가보세요 ^^



프로그램 실행

자, 여러분 컴퓨터가 성능속도 인터넷 속도가 빠르다고 믿고!!
기본설정인 1초에 한번씩 갱신하는걸로 테스트 해봅시다.

좀 해보다보면, 여러분이 살고 있는곳도 잘되나 테스트 해보고 싶어질텐데 ^^
아직까지 그렇게 데이터가 잘 오는 곳이 없더라구요.
Los Angeles City Traffic Conditions, 여기는 그나마 괜찮아요 ^^

- Lorin 씀

반응형
댓글