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 즉, 가장 왼쪽, 상단에 있어야만 정상적으로 표시될 것입니다.

실버라이트에서 투명배경 적용

실버라이트를 웹 페이지에 올릴때 배경을 투명하게 할 때에는 아래와 같이 3가지 속성을 바꿔주세요.

<asp:Silverlight ID="Silverlight_SearchEngine" runat="server" Source="~/ClientBin/COS_SHOP.SILVERLIGHT.xap" MinimumVersion="2.0.31005.0" Width="100%" Height="100%" PluginBackground="Transparent" BackColor="Transparent" Windowless="true" />

대한민국 주민번호 검증 코드

주민번호를 파라미터와 함께 넘겨주면 주민번호를 검사하여 리턴해 주는 함수입니다.
이 함수도 인터넷 어딘가에서 구한 것인데...
필요할 때 유용하게 잘 사용하고 있습니다. ^^

bool IsAvailableRRN(string RRN)
{
    //공백 제거
    RRN = RRN.Replace(" ", "");
    //문자 '-' 제거
    RRN = RRN.Replace("-", "");
    //주민등록번호가 13자리인가?
    if (RRN.Length != 13)
    {
        return false;
    }

    int sum = 0;
    for (int i = 0; i < RRN.Length - 1; i++)
    {
        char c = RRN[i];
        //숫자로 이루어져 있는가?
        if (!char.IsNumber(c))
        {
            return false;
        }
        else
        {
            if (i < RRN.Length)
            {
                //지정된 숫자로 각 자리를 나눈 후 더한다.
                sum += int.Parse(c.ToString()) * ((i % 8) + 2);
            }
        }
    }
    // 검증코드와 결과 값이 같은가?
    if (!((((11 - (sum % 11)) % 10).ToString()) == ((RRN[RRN.Length - 1]).ToString())))
    {
        return false;
    }
    return true;
}

저의 자료창고를 만들었습니다. ^^

앞으로 이곳을 통해
내 개인적인 글부터...
공부한 것들...
잊어버리지 않기 위해 필요한 것들...
그리고...
연구하고 개발하면서 만들어낸 결과물들...

다양한 자료들을 기록할 것입니다.
또한, 여기에다 넓은 인터넷에서 구한 자료들도 첨부하여 기록할 것입니다.

이곳을 통해서 저 뿐 아니라 이곳에 방문한 모든 이에게 도움이 되기를 바랍니다.
사랑하고 축복합니다.