레이블이 WPF인 게시물을 표시합니다. 모든 게시물 표시
레이블이 WPF인 게시물을 표시합니다. 모든 게시물 표시

2011년 8월 3일 수요일

WPF에서 타이머개체 사용하기

1. WPF자체의 타이머가 이닌 Windows.Form의 타이머를 사용하는 방법
using System.Windows.Forms;


// Timer개체를 생성, 속성, 이벤트 정의
timer = new Timer();
timer.Interval = 200;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;


// 타이머의 Tick 이벤트
private void timer_Tick(object sender, EventArgs e)
{
            DrawClock();
}
/// 이코드는 Shape로 만들어진 시계의 시, 분, 초 침을 현재 시간에 맞추어
/// 각도를 조정하는 코드이다.
///   첨부한 샘플을 참고하라.


/// <summary>
/// 시침, 분침, 초침 그리기
/// </summary>
private void DrawClock()
{
            DateTime now = DateTime.Now;
            txtTime.Text = now.ToLongTimeString();
            int hour = now.Hour;
            int minute = now.Minute;
            int second = now.Second;
            double hourAngle = hour * 30 + minute * 0.5;
            double minuteAngle = minute * 6 + second * 0.1;
            double secondAngle = second * 6;
            rotateHour.Angle = hourAngle;
            rtHour.RenderTransform = rotateHour;
            rotateMinute.Angle = minuteAngle;
            rtMinute.RenderTransform = rotateMinute;
            rotateSecond.Angle = secondAngle;
            rtSecond.RenderTransform = rotateSecond;
}

2. WPF자체의 타이머를 사용하는 방법
이것은 윈폼용과 동일하게 동작합니다.
이벤트도 같구요. 다만, Enabled가 이니라 Start(), Stop()이네요.

public DispatcherTimer Timer = new DispatcherTimer();
void Window1_Loaded(object sender, RoutedEventArgs e)
{
    Timer.Interval = TimeSpan.FromSeconds(3);
    Timer.Tick += new EventHandler(Timer_Tick);
    Timer.Start();
}


3. 별도의 쓰레드를 만들어 동작하는 타이머를 사용하는 방법
Thread로 동작하는 것 같더군요. 따라서, 폼 개체에 접근할 수 없습니다.
this 라던가.. 폼 개체에 접근하려면 에러가 발생합니다.


사용방법은 using System.Timers; 를 추가하고 그냥 사용하시면 됩니다.
다만, Tick 이벤트가 아니라
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
Elapsed 이벤트를 사용합니다. 이벤트 핸들러도 다릅니다.
참고하세요.

System.Timers.Timer Class는 TestWindow와 다른 새로운 Thread를 생성해서 동작을 합니다.
System.Windows.Threading.DispatcherTimer는 TestWindow와 같은 Thread에서 동작을 합니다.


WPF용으로는 두가지의 타이머가 있습니다.
이 두가지 타이머간에는 다음과 같은 차이점이 존재합니다..


Dispatcher Class
 - 같은 Thread에서 동작하기 때문에 동기화 작업이 필요없고, TestWindow의 멤버도
   자유롭게 접근 가능합니다.

Timer Class
 - 서로 다른 Thread에서 동작하기 때문에 TestWindow의 멤버변수에 직접 접근을 할수 없습니다.
    (직접 접근을 하기 위해서는 Invoke나 BeginInvoke를 사용하여야 합니다)
 - 서로 다른 Thread에서 동작하기 때문에 List를 추가/삭제 하기 위해서는 추가적으로 Thread간
   동기화 작업을 해줘야 합니다.

현재 작업경로 가져오기

현재 작업 디렉토리의 졍규화된 경로를 가져옵니다.

System.Environment.CurrentDirectory

결과값에는 "\"는 포함되지 않습니다.
따라서, System.Environment.CurrentDirectory + "\\" + txtFileName.Text; 와 같이 사용해야 합니다.

아무데나 눌러서 창을 이동할때

타이틀이 없는 창을 이동하는 방법

Win32나 MFC등에서는 보통 이러한 경우 창 이동과 관련된 부분을 직접 구현 해야 하지만 WPF에서는 이러한 상황 에서 개발자의 부담을 덜어드리고자 간단한 메서드 호출 한번으로 마우스를 사용한 창 이동 기능을 구현 할 수 있습니다. 

Window Class에서 DragMove 메서드를 제공하는데요, 이 는 마우스 왼쪽 단추를 누른 상태로 창 클라이언트 영역의 노출된 영역에서 창을 끌 수 있도록 돕습니다. 사용방법이 매우 간단합니다. 

마우스를 사용해서 창을 끌 수 있도록 하고 싶은 객체의 MouseLeftButtonDown이벤트에 DragMove()메서드를 호출해주기만 하면 됩니다. 

void Window1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)     this.DragMove();}

외부의 프로세스(앱)을 호출(실행)하고 태스크를 관리할때

외부의 프로그램을 실행하는 코드입니다.
System.Diagnostics.Process.Start("Shutdown.exe", "-s -f -t 00");         // 시스템 종료
System.Diagnostics.Process.Start("Shutdown.exe", "-r -f -t 00");          // 시스템 다시 시작

System.Diagnostics의 Process 개체로 노트패드 프로세스를 생성하여 실행합니다.
Process myProcess = new Process();
myProcess.StartInfo.FileName = "Notepad";
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
myProcess.Start();

나중에 강제로 중지시키거나 정보를 검색할 수 있습니다.
myProcess.CloseMainWindow();

응답의 유무를 검사하여 정상종료와 강제종료를 할 수 있습니다.
If myProcess.Responding Then
    myProcess.CloseMainWindow()
Else
    ' Forces the process to close if the Responding value is False.
    myProcess.Kill()
End If

실행중인 모든 프로세스를 검색할 수 있습니다.
Process[] myProcesses = Process.GetProcesses();
foreach (Process myProcess in myProcesses)
{
      Console.WriteLine(myProcess.ProcessName);
}

프로세스 이름으로 현재 실행중인 프로세스를 가져올 수 있습니다.
myProcesses = Process.GetProcessesByName("Notepad");

아이콘파일, 실행파일에서 아이콘 가져오기

파일관리자 같은 앱을 만들다보면 꼭 필요하지요...


/// System.Drawing.Icon을 이용하여 아이콘을 추출하는 함수
public System.Windows.Media.ImageSource getIcon(string filename)
{
            System.Windows.Media.ImageSource icon;
            using (System.Drawing.Icon sysicon = System.Drawing.Icon.ExtractAssociatedIcon(filename))
            {
                icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
                            sysicon.Handle,
                            System.Windows.Int32Rect.Empty,
                            System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
            }
            return icon;
}

// 이렇게 사용하세요. 아이콘파일의 기본 크기는 32이니깐...
private void button5_Click(object sender, RoutedEventArgs e)
{
            image1.Source = getIcon(textBox1.Text);
            image1.Width = 32;
            image1.Height = 32;
}

당연하겠지만, 이코드를 사용하려면 System.Drawing을 참조추가해야만 하겠죠?


getIcon(textBox1.Text) 에서 textBox1.Text는 추출하고자 하는 파일의 실제경로(FullPath)이어야 합니다.
경로가 틀리면 에러가 발생하겠죠?

using (System.Drawing.Icon sysicon = System.Drawing.Icon.ExtractAssociatedIcon(filename))
이 줄에서 에러가 발생합니다. 

실제 사용하려면 에러 핸들러를 달거나, 파일유무를 검사하면 되겠죠...

최소화할때 태스크바의 Notify 아이콘에 넣기

프로그램을 최소화할때 태스크바의 NotifyIcon으로 등록하는 방법 public partial class Window1 : System.Windows.Window { public Window1() { InitializeComponent(); System.Windows.Forms.NotifyIcon ni = new System.Windows.Forms.NotifyIcon(); ni.Icon = new System.Drawing.Icon("Main.ico"); ni.Visible = true; ni.DoubleClick += delegate(object sender, EventArgs args) { this.Show(); this.WindowState = WindowState.Normal; }; } protected override void OnStateChanged(EventArgs e) { if (WindowState == WindowState.Minimized) this.Hide(); base.OnStateChanged(e); } }
트레이 아이콘에서 제거 하고 싶으면 NotifyIcon 클래스의 Visible 프로퍼티를 조작한다.
ni.Visible = false;

원도우즈의 시스템 경로를 찾을 때...

// 선택한 경로가 없다면 시스템의 내 그림 폴더로 이동, 향후 마지막 사용한 폴더로 이동 기능 구현
nowPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);

아래는 SpecialFolder의 타입을 찾기위한 Enum 목록입니다.

        //     시스템 특수 폴더에 대한 디렉터리 경로를 검색하는 데 사용되는 열거 상수를 지정합니다.
        public enum SpecialFolder
        {
            //     실제 파일 시스템 위치가 아니라 논리 데스크톱입니다.
            Desktop = 0,
            //     사용자 프로그램 그룹이 들어 있는 디렉터리입니다.
            Programs = 2,
            //     문서에 대한 공용 리포지토리로 사용되는 디렉터리입니다.
            Personal = 5,
            //     "내 문서" 폴더입니다.
            MyDocuments = 5,
            //     사용자가 즐겨찾는 항목에 대한 공용 리포지토리로 사용되는 디렉터리입니다.
            Favorites = 6,
            //     시작 프로그램 그룹에 해당하는 디렉터리입니다.
            Startup = 7,
            //     사용자가 가장 최근에 사용한 문서가 들어 있는 디렉터리입니다.
            Recent = 8,
            //     보내기 메뉴 항목이 들어 있는 디렉터리입니다.
            SendTo = 9,
            //     시작 메뉴 항목이 들어 있는 디렉터리입니다.
            StartMenu = 11,
            //     "내 음악" 폴더입니다.
            MyMusic = 13,
            //     데스크톱에서 실제로 파일 개체를 저장하는 데 사용되는 디렉터리입니다.
            DesktopDirectory = 16,
            //     "내 컴퓨터" 폴더입니다.
            MyComputer = 17,
            //     문서 템플릿에 대한 공용 리포지토리로 사용되는 디렉터리입니다.
            Templates = 21,
            //     현재 로밍 사용자의 응용 프로그램 관련 데이터에 대한 공용 리포지토리로 사용되는 디렉터리
            ApplicationData = 26,
            //     현재 로밍하지 않은 사용자가 사용하는 응용 프로그램 관련 데이터에 대한 공용 리포지토리
            LocalApplicationData = 28,
            //     임시 인터넷 파일에 대한 공용 리포지토리로 사용되는 디렉터리입니다.
            InternetCache = 32,
            //     인터넷 쿠키에 대한 공용 리포지토리로 사용되는 디렉터리입니다.
            Cookies = 33,
            //     인터넷 기록 항목에 대한 공용 리포지토리로 사용되는 디렉터리입니다.
            History = 34,
            //     모든 사용자가 사용하는 응용 프로그램 관련 데이터에 대한 공용 리포지토리로 사용되는 디렉터리
            CommonApplicationData = 35,
            //     시스템 디렉터리입니다.
            System = 37,
            //     프로그램 파일 디렉터리입니다.
            ProgramFiles = 38,
            //     "내 그림" 폴더입니다.
            MyPictures = 39,
            //     전체 응용 프로그램에서 공유되는 구성 요소에 대한 디렉터리입니다.
            CommonProgramFiles = 43,
        }

WPF에는 DoEvents가 없습니다. 이걸로 해보세요...

DoEvents가 필요로 하는 위치에 아래 코드를 그대로 넣으세요.

Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Threading.ThreadStart(delegate { }));

VB나 Window.Form에서는 DoEvent를 이용하면 되는데.. WPF는 그게 없습니다..
대신 다른 대안으로 사용할 수 있는 코드립니다.

어떻게 동작되는 건지 제게 물어봐도 전 모릅니다...
그냥, 사용할 뿐... ^^

WPF 애플릿에서 멀티모니터 지원

기존의 닷넷 프로그래밍에서는 System.Windows.Forms.SystemInformation.MonitorCount를 이용해서 현재 시스템에 장착된 모니터의 갯수를 알 수 있었다. 

또 해당되는 모니터의 상세한 정보를 얻고자 할 경우에는 System.Window.Forms.Screen.AllScreens[index].WorkingArea;와 같이 해당되는 모니터의 정보를 알아낼 수 있었다.
하지만 WPF에서는 기본적으로 모니터와 관련된 속성이나 기능을 추가로 제공되지 않는 것 같다.

실제로 몇몇 경우에는 듀얼 모니터를 활용하는 경우가 많고 또 개발시에도 듀얼 모니터를 이용하는 경우가 많기 때문에 WPF Application의 출력 모니터 지정은 꼭 필요한 기능중에 하나이다.

아래와 같은 코드를 이용하면 이와 같은 상황을 해결할 수 있다.

fullScreenWindow.WindowStartupLocation = WindowStartupLocation.Manual;
System.Drawing.Rectangle workingArea = System.Windows.Forms.Screen.AllScreens[1].WorkingArea;
fullScreenWindow.Left = workingArea.Left;
fullScreenWindow.Top = workingArea.Top;
fullScreenWindow.Width = workingArea.Width;
fullScreenWindow.Height = workingArea.Height;
fullScreenWindow.WindowState = WindowState.Maximized;
fullScreenWindow.WindowStyle = WindowStyle.None;
fullScreenWindow.Topmost = true;
fullScreenWindow.Show();

WindowStartupLocation을 메뉴얼로 지정해야 위치를 지정하기 용의하며 위의 소스의 핵심은
System.Drawing.Rectangle workingArea = System.Windows.Forms.Screen.AllScreens[1].WorkingArea; 이다.

AllScreens[1]이라고 했기 때문에 이는 Primary Monitor가 아닌 Second Monitor의 정보를 가져와서 임시라 Ractangle을 하나 생성하고 이 정보를 바탕으로 출력하려고 하는 WPF Window를 셋팅한다.

위 소스를 사용하려면,
System.Windows.Forms와 System.Drawing 두개의 NET Component를 참조 추가해야만 사용할 수 있습니다.

[참고]
단순히 멀티 모니터 전체를 덮는 창을 설정하려면, 이렇게 하세요.
this.WindowStartupLocation = WindowStartupLocation.Manual;
this.Left = 0;
this.Top = 0;
this.Width = SystemParameters.VirtualScreenWidth;
this.Height = SystemParameters.VirtualScreenHeight;
this.WindowStyle = WindowStyle.None;
this.Topmost = true; 
this.Show();

SystemParameters.VirtualScreenWidth;
SystemParameters.VirtualScreenHeight;
이 두 가지에는 멀티 모니터 전체의 크기가 계산된 값이 저장되어 있습니다.
이 경우, 1번 모니터의 Left, Top이 0 즉, 가장 왼쪽, 상단에 있어야만 정상적으로 표시될 것입니다.