반응형

기존에 ClickOnce로 배포했던 앱을 여러 이유로 새로운 명칭과 설치 URL 변경 등으로 새로 만들어서 배포를 할 경우, 기존 ClickOnce 앱의 삭제를 사용자에게 맡길 것인가 아니면 자동으로 삭제하고 설치도 자동으로 하고 싶을 경우 어떻게 하면 될까?

여러가지 방법이 있을 수 있다. 

기존 ClickOnce 배포 앱을 삭제하기 위한 스크립트를 만들어서 스크립트 실행을 통해서 삭제 또는 설치할 수가 있다.

이럴경우 스크립트 파일(WScript)의 디버깅이나 제어가 여러가지로 불편할 수가 있고, 편집기로 쉽게 스크립트 내용을 볼 수 있는 단점이 있다.

스크립트 파일로 삭제/설치도 좋지만 요즘 보안이다 뭐다 해서 주요 정보들을 스크립트로 제공하는 것도 문제가 될 수 있다.

아래는 이러한 문제를 개선한 실행 앱(exe)을 통해서 기존의 ClickOnce 앱을 삭제하고 설치하는 소스이다.

public class ClickOnceRemoveInstall
    {        
        public static void Main(string[] args)
        {
            //CMD 창을 숨긴다.
            var handle = GetConsoleWindow();
            ShowWindow(handle, SW_HIDE); // 숨기기

            //활성화된 ClickOnce App. Kill            
            Process.Start("taskkill", "/F /IM ClickOnce앱명칭*");
            Process killProc = Process.GetProcessesByName("taskkill").FirstOrDefault();
            killProc.WaitForExit();
            killProc.Close();
            
            //기존 설치된 ClickOnce App. 제거
            string app = "dfshim.dll,ShArpMaintain ClickOnceAppName.application, Culture=neutral, PublicKeyToken=e5ccc1554e61cee3, processorArchitecture=x86";
            Process.Start("rundll32.exe", app);
            Process removeProc = Process.GetProcessesByName("dfsvc").FirstOrDefault();
            System.Threading.Thread.Sleep(3500); //제거 창이 뜰때까지 잠깐 대기
            if (removeProc != null)
            {
                IntPtr h = removeProc.MainWindowHandle;               
                SetForegroundWindow(h);                
                SendKeys.SendWait("O"); //제거 창에서 확인 버튼 클릭을 위해 Key 입력
                removeProc.Close();                
            }
            
            System.Threading.Thread.Sleep(500);
            
            //새로운 ClickOnce App. 설치
            string appUrl = "http://ClickOnce배포서버명 또는 접속URL/ClickOnceAppName.application";
            Process.Start("rundll32.exe", "dfshim.dll,ShOpenVerbApplication " + appUrl);
            Process installProc = Process.GetProcessesByName("dfsvc").FirstOrDefault();
            System.Threading.Thread.Sleep(2500); //설치 창이 뜰때까지 잠깐 대기
            if (installProc != null)
            {
                IntPtr h = installProc.MainWindowHandle;                
                SetForegroundWindow(h);                                
                SendKeys.SendWait("I"); //설치 창에서 설치 버튼 클릭을 위해 Key 입력               
                installProc.Close();
            }            
        }
        
        [DllImport("User32.dll")]
        static extern int SetForegroundWindow(IntPtr point);
        
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        const int SW_HIDE = 0; // 숨기기
        const int SW_SHOW = 1; // 보이기
    }

 

반응형
반응형

프로그램에서 언어나 문화권에 대한 설정 값을 받아와서 요일 정보를 반환하는 함수

DateTime 을 입력하면 그에 맞는 요일을 반환한다. 

반환하는 값은 월요일, 화요일 .... 토요일, 일요일 로 값을 반환한다.

public static string WeekDay(DateTime dateTime)
{
    //반환할 요일
    String weekDay = string.Empty;
    //입력받은 일자에 대한 요일정보
    var dt = dateTime.DayOfWeek;
    //프로그램에서 언어나 문화권에 대한 설정값을 받아온다.
    CultureInfo cultureInfo = CultureInfo.CurrentCulture;
    weekDay = cultureInfo.DateTimeFormat.GetDayName(dateTime.DayOfWeek);
    return weekDay;
}
반응형
반응형

Window를 상속해 클래스를 정의하는 것도 가능하다.

다음 예제는 세 개의 클래스가 있고, 세 개의 소스 코드 파일이 있다. 

[InheritAppAndWindow.cs]

Main에서 MyApplication 타입의 객체를 생성하고, 이 객체의 Run을 호출한다.

using System;
using System.Windows;
using System.Windows.Input;

namespace InheritAppAndWindow
{
    public class InheritAppAndWindow
    {
        [STAThread]
        public static void Main()
        {
            MyApplication app = new MyApplication();
            app.Run();
        }
    }
}

[MyApplication.cs]

OnStartup 메소드를 오버라이딩한 부분에서 MyWindow 타입의 객체를 생성하고 있다.

using System;
using System.Windows;
using System.Windows.Input;

namespace InheritAppAndWindow
{
    public class MyApplication : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MyWindow win = new MyWindow();
            win.Show();
        }
    }
}

[MyWindow.cs]

Window를 상속받는 클래스는 일반적으로 생성자에서 그 클래스를 초기화한다. 예제에서는 Title 프로퍼티만 초기화한다. 프로퍼티 이름 앞에 객체의 이름을 따로 쓰지 않았는데, MyWindow가 Window를 상속받기 때문이다.

using System;
using System.Windows;
using System.Windows.Input;

namespace InheritAppAndWindow
{
    public class MyWindow : Window
    {
        public MyWindow()
        {
           this.Title = "Inherit App & Window";
        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            string strMessage = string.Format("Window clicked with {0} button at point ({1})", e.ChangedButton, e.GetPosition(this));
            MessageBox.Show(strMessage, Title);
        }
    }
}

 

InheritAppAndWindow.zip
0.02MB

반응형
반응형

명령 행 인자는 문자열의 배열 형태로 Main에 전달된다. 이 문자열의 배열은 OnStartUp 메소드에서도 사용할 수 있다. StartupEventArgs 인자의 Args 프로퍼티를 참조하면 된다.

Application에 MainWindow란 이름의 프로퍼티가 있다는 것은 프로그램이 여러 개의 창을 가질 수 있음을 시사하고 있는데, 이는 사실이다. 대화 상자를 그 좋은 예로 들 수 있다. 대화 상자는 기본적으로 Window 객체이지만 표시 방식이 조금 다르다는 점과 사용자와 상호작용을 한다는 점에서 약간의 차이가 있다.

 

다음 프로그램은 몇 개의 창을 더 만드는  프로그램이다. 마치 여러 개의 창을 초대해 파티를 여는 것 같아서 이름을 ThrowWindowParty로 했다.

using System;
using System.Windows;
using System.Windows.Input;

namespace ThrowWindowParty
{
    public class ThrowWinowParty : Application
    {
        [STAThread]
        public static void Main()
        {
            ThrowWinowParty app = new ThrowWinowParty();
            //app.ShutdownMode = ShutdownMode.OnMainWindowClose;
            app.Run();

        }

        protected override void OnStartup(StartupEventArgs e)
        {
            //메인 창이 닫힐 때만 Run이 반환되고, 프로그램이 종료된다.
            ShutdownMode = ShutdownMode.OnMainWindowClose;

            Window winMain = new Window();
            winMain.Title = "Main Window";
            winMain.MouseDown += WindowOnMouseDown;
            winMain.Show();

            for (int i = 0; i < 2; i++)
            {
                Window win = new Window();
                win.Title = "Extra Window No. " + (i + 1);
                //세 개의 윈도우 모두 화면 하단부에 있는 윈도우의 작업 표시줄에 나타나지 않도록 설정.
                win.ShowInTaskbar = false;
                //MainWindow = win;
                win.Owner = winMain;
                win.Show();
            }
        }

        private void WindowOnMouseDown(object sender, MouseButtonEventArgs e)
        {
            Window win = new Window();
            win.Title = "Modal Dialog Box";
            win.ShowDialog();
        }
    }
}

ThrowWindowParty.zip
0.02MB

반응형
반응형
//사용자가 선택한 ComboBox의 Index값을 이용해서 Value 값을 가져온다.

var prop = cboTime.Items[cboTime.SelectedIndex].GetType().GetProperty(cboTime.ValueMember);
Time = prop.GetValue(cboTime.Items[cboTime.SelectedIndex], null).ToString();

C#을 이용해서 WinForm 개발을 하다 보면 ComboBox 컨트롤 사용을 자주 하게 된다.
매번 ComboBox를 사용하면서 특정 값 또는 텍스트로 ComboBox의 목록 중 1개를 선택하고자 할 경우가 많다.
다양한 방법이 있겠지만, 간단하게 구현해서 사용할 수 있는 방법을 소개하고자 한다.

ComboBox를 이용해서 선택할 수 있는 시간을 콤보박스에 보여주기 위해 Items에 추가하는 로직과 특정 시간대의 값에 따라 콤보박스가 선택되어져 있도록 하기 위한 코드를 남겨둔다.

 

아래는 ComboBox에 간단하게 DisplayMember와 ValueMember로 구분해서 Items에 추가하는 방법.

cboTime.DisplayMember = "Text";
cboTime.ValueMember = "Value";
cboTime.Items.Add(new { Text = "09:00", Value = "0900" });
cboTime.Items.Add(new { Text = "09:30", Value = "0930" });
cboTime.Items.Add(new { Text = "10:00", Value = "1000" });
cboTime.Items.Add(new { Text = "13:30", Value = "1330" });
cboTime.Items.Add(new { Text = "14:00", Value = "1400" });
cboTime.Items.Add(new { Text = "14:30", Value = "1430" });

 

아래는 사용자가 입력한 값에 따라 ComboBox가 선택되어져 있도록 하는 방법

string Time = "1000";
//Time 값이 비어 있을 경우에는 첫번째 항목이 선택되도록 처리
if (string.IsNullOrEmpty(Time) || string.IsNullOrWhiteSpace(Time)) cboTime.SelectedIndex = 0;
else
{
    //ComboBox 항목 수 만큼 돌면서 Time 변수와 같은 값을 가진 항목을 검색
    for(int i = 0; i < cboTime.Items.Count; i++)
    {
        var prop = cboTime.Items[i].GetType().GetProperty(cboTime.ValueMember);
        if (prop != null && prop.GetValue(cboTime.Items[i], null).ToString() == Time)
        {
            cboTime.SelectedIndex = i;
            break;
        }
    }
}

 

사용자가 ComboBox에서 특정 값을 선택했을 경우 가져오는 방법

//사용자가 선택한 ComboBox의 Index값을 이용해서 value을 가져온다.
var prop = cboTime.Items[cboTime.SelectedIndex].GetType().GetProperty(cboTime.ValueMember);
Time = prop.GetValue(cboTime.Items[cboTime.SelectedIndex], null).ToString();

 

반응형
반응형

프로그램에서 Application 클래스를 상속받아 프로그램 종료 시 프로그램 종료 여부를 확인하는 팝업 창을 보여주는 예제인 듯 하다.

제공된 예제를 실행했으나, 오버라이딩한 OnSessionEnding 메소드가 호출되지 못하고 있다.

내가 잘못 알고 있는 것일 수도 있는데..혹 OnSessionEnding 메소드가 호출되기 위한 조건이나 방법을 아시는 분이 있으시면 댓글로 알려 주셨으면 합니다.

제가 책을 읽으며 테스트 했던 소스는 아래와 같습니다.

using System;
using System.Windows;
using System.Windows.Input;

namespace InheritTheApp
{
    public class InheritTheApp : Application
    {
        [STAThread]
        public static void Main()
        {
            InheritTheApp app = new InheritTheApp();
            app.Run();
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            Window win = new Window();
            win.Title = "Inherit the App";
            win.Show();
        }

        protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
        {
            base.OnSessionEnding(e);

            MessageBoxResult result = MessageBox.Show("Do you want to save your data?", 
            	MainWindow.Title, MessageBoxButton.YesNoCancel, MessageBoxImage.Question, 
                MessageBoxResult.Yes);

            e.Cancel = (result == MessageBoxResult.Cancel);
        }

        protected override void OnExit(ExitEventArgs e)
        {
            MessageBox.Show("Application이 종료됩니다.");
            base.OnExit(e);
        }
    }
}

InheritTheApp.zip
0.01MB

반응형
반응형

사실상 프로그램이 하는 일은 이벤트(event)에 대해 반응하는 것이 전부라고 말할 수 있다. 이벤트란 통상적으로 사용자의 키보드, 마우스, 스타일러스 펜의 입력을 의미한다. UIElement 클래스에는 키보드, 마우스, 스타일러스와 관련된 몇 가지의 이벤트가 정의돼 있으며, Window 클래스는 이 모든 이벤트를 상속받는다. 이런 이벤트 중 하나는 MouseDown 이다. 사용자가 윈도우의 클라이언트 영역을 누를 때마다 윈도우에서는 MouseDown 이벤트가 발생한다.

 

사용자가 윈도우 클라이언트 영역을 누를 때마다 MouseDown 이벤트가 발생된다. 이벤트 핸들러의 첫 번째 인자는 이벤트를 발생시키는 객체인데, 여기서는 Window 객체가 된다. 이벤트 핸들러는 이 객체를 Window 타입의 객체로 안전하게 형 변환한다.

 

이 프로그램에서 이벤트 핸들러에 Window 객체가 필요한 이유는 두 가지다. 첫 번째 이유는 MouseButtonEventArgs 클래스에 정의된 GetPosition 메소드의 인자로 Window 객체를 넘겨야 하기 때문이다. 이 GetPosition 메소드는 Point 타입(System.Windows에 정의된 구조체)의 객체를 반환하는데, 이 값은 인자로 넘긴 객체의 좌측 상단을 기준으로 한 마우스의 위치 좌표다. 두 번째 이유는 이벤트 핸들러가 Window 객체의 Title 프로퍼티를 읽어서 메시지 박스의 제목으로 사용하기 때문이다.

 

[HandleAnEvent.cs 파일]

using System;
using System.Windows;
using System.Windows.Input;

namespace HandleAnEvent
{
    public class HandleAnEvent
    {
        [STAThread]
        public static void Main()
        {
            Application app = new Application();

            Window win = new Window();
            win.Title = "Handle An Event";
            win.MouseDown += WindowOnMouseDown;

            app.Run(win);
        }

        private static void WindowOnMouseDown(object sender, MouseButtonEventArgs args)
        {
            Window win = sender as Window;
            //Window win = Application.Current.MainWindow;
            string strMessage = string.Format("Window clicked with {0} button at point({1})", args.ChangedButton, args.GetPosition(win));

            MessageBox.Show(strMessage, win.Title);
            //MessageBox.Show(strMessage, Application.Current.MainWindow.Title);
        }
    }
}

 

프로젝트 다운로드

HandleAnEvent.zip
0.02MB

반응형
반응형

마이크로소프트의 윈도우 프리젠테이션 파운데이션(WPF)으로 만든 애플리케이션은 Application과 Window 타입의 객체를 생성하는 것으로 시작한다.

간단한 WPF 프로그램 예제.

 

WPF 프로그램에서는 [STAThread] 속성(attribute)이 반드시 Main의 앞에 있어야 한다. 그렇지 않으면 InvalidOperationException 예외 상황과 함께 프로그램이 종료된다.

이 속성은 최초의 애플리케이션 스레드의 스레드 모델을 단일 스레드 아파트먼트(single-threaded apartment)로 지정하는 것을 의미한다. 이는 컴포넌트 오브젝트 모델(COM)과 상호 운용하기 위해 필요하다. 단일 스레드 아파트먼트는 .NET 이전의 과거 COM 시대의 프로그래밍 용어이긴 하지만 애플리케이션이 다중 스레드를 사용하지 않는다는 것을 의미한다고 짐작할 수 있다.

 

SayHello 프로그램에서 Main은 Window 타입의 객체를 생성하는 것으로 시작한다. 이 클래스는 표준 애플리케이션 창을 생성할 때 사용한다. Title 프로퍼티는 생성된 창의 제목 표시줄에 나타나게 될 글자를 지정하며, Show 메소드는 화면에 창을 나타나게 한다.

 

마지막으로 중요한 단계는  Application 객체를 생성하고 Run 메소드를 호출하는 것이다. 윈도우 프로그래밍 용어로 말하면 이 메소드는 메시지 루프를 생성한다고 표현한다. 애플리케이션은 메시지 루프를 통해 키보드나 마우스로 사용자 입력을 받을 수 있게 된다. 태블릿 PC에서 프로그램을 실행한다면 스타일러스 펜의 입력도 받을 수 있다.

 

한 프로그램은 하나의 Application 객체만 생성할 수 있는데, 이 객체는 프로그램 내의 다른 부분에서도 항상 접근할 수 있다. Application 객체는 보이지 않는 반면에, Window 객체는 표준 창으로서 화면에 표시된다.

 

Title 프로퍼티로 지정한 텍스트를 표시하는 제목 표시줄이 있으며, 제목 표시줄의 왼쪽에는 시스템 메뉴 아이콘이 있고, 오른쪽에는 최소화/최대화/닫기 아이콘이 있다. 또한 창에는 창의 크기를 조절하는 경계가 있고, 창 내부를 차지하는 클라이언트 영역이 있다.

 

제한된 범위 내에서 SayHello 프로그램의 Main 메소드 안에 있는 명령문들의 순서를 뒤바꿀 수 있다. 그래도 여전히 제대로 동작할 것이다. 예를 들어 Show 메소드를 호출한 후에 Title 프로퍼티를 설정할 수도 있다. 이론상으로는 이렇게 수정할 경우에 제목 표시줄에 아무런 글자도 없는 상태로 윈도우가 시작되겠지만, 실제로는 너무 빨라서 눈치 챌 수 없을 것이다.

 

Window 객체를 생성하기 전에 Application 객체를 생성할 수도 있다. 그러나 Run 메소드는 반드시 가장 나중에 호출해야 한다. Run 메소드는 창이 닫히기 전까지는 반환되지 않는다. 따라서 창이 닫힐 때 Main 메소드가 종료되고, 창이 정리돼야 한다. Run을 호출하는 부분을 삭제해도 Window 객체는 여전히 생성되고 화면에 창도 표시된다. 그러나 Main이 종료되는 시점에 바로 객체가 소멸된다.

 

Window 객체의 Show 메소드를 호출하는 대신에 아래와 같이 Window 객체를 Run의 인자로 넘길 수도 있다.

app.Run(win);

 

프로그램은 Run 메소드가 호출되기 전까지는 실제로 시작되지 않으며, 호출된 후에야 Window 객체가 사용자의 입력에 반응할 수 있게 된다. 사용자가 창을 닫고 Run 메소드가 반환될 때 프로그램은 종료를 준비한다. 즉, 프로그램은 Run을 호출하는 데 대부분의 시간을 사용한다. 그런데 프로그램이 Run에 모든 시간을 쓴다면 다른 일을 어떻게 할 수 있을까?

 

[SayHello.cs 파일]

using System;
using System.Windows;

namespace SayHello
{
    public class SayHello
    {
        [STAThread]
        public static void Main()
        {
            Window win = new Window();
            win.Title = "Say Hello";
            win.Show();

            Application app = new Application();
            app.Run();
            //app.Run(win);
        }
    }
}

 

프로젝트 다운로드

SayHello.zip
0.02MB

반응형
반응형

찰스 페졸드의 WPF 를 읽으면서 정리한 폰트 선택 다이얼로그 박스(ChooseFont) 소스.

WPF에서 기본으로 제공되지 않는 폰트 선택 다이얼로그 박스를 구현한 소스.

이 소스를 기반으로 좀 더 미려한 폰트 선택 다이얼로그 박스를 만들 수 있을 것이다.

 

[ChooseFont.cs 파일]

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ChooseFont
{
    public class ChooseFont : Window
    {
        [STAThread]
        public static void Main()
        {
            Application app = new Application();
            app.Run(new ChooseFont());
        }

        public ChooseFont()
        {
            Title = "Choose Font";

            Button btn = new Button();
            btn.Content = Title;
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            btn.VerticalAlignment = VerticalAlignment.Center;
            btn.Click += ButtonOnClick;
            Content = btn;
        }

        void ButtonOnClick(object sender, RoutedEventArgs args)
        {
            FontDialog dlg = new FontDialog();
            dlg.Owner = this;

            //윈도우의 폰트 대화상자 프로퍼티를 설정
            dlg.Typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
            dlg.FontSize = FontSize;

            if (dlg.ShowDialog().GetValueOrDefault())
            {
                //폰트 대화상자에서 윈도우 프로퍼티를 설정
                FontFamily = dlg.Typeface.FontFamily;
                FontStyle = dlg.Typeface.Style;
                FontWeight = dlg.Typeface.Weight;
                FontStretch = dlg.Typeface.Stretch;
                FontSize = dlg.FaceSize;
            }
        }
    }
}

 

[ FontDialog.cs 파일]

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ChooseFont
{
    public class FontDialog : Window
    {
        TextBoxWithLister boxFamily, boxStyle, boxWeight, boxStretch, boxSize;
        Label lblDisplay;
        bool isUpdateSuppressed = true;

        //Public 프로퍼티
        public Typeface Typeface
        {
            set
            {
                if (boxFamily.Contains(value.FontFamily))
                    boxFamily.SelectedItem = value.FontFamily;
                else
                    boxFamily.SelectedIndex = 0;

                if (boxStyle.Contains(value.Style))
                    boxStyle.SelectedItem = value.Style;
                else
                    boxStyle.SelectedIndex = 0;

                if (boxWeight.Contains(value.Weight))
                    boxWeight.SelectedItem = value.Weight;
                else
                    boxWeight.SelectedIndex = 0;

                if (boxStretch.Contains(value.Stretch))
                    boxStretch.SelectedItem = value.Stretch;
                else
                    boxStretch.SelectedIndex = 0;
            }
            get
            {
                return new Typeface((FontFamily)boxFamily.SelectedItem, (FontStyle)boxStyle.SelectedItem,
                                    (FontWeight)boxWeight.SelectedItem, (FontStretch)boxStretch.SelectedItem);
            }
        }
        public double FaceSize
        {
            set
            {
                double size = 0.75 * value;
                boxSize.Text = size.ToString();

                if (!boxSize.Contains(size)) boxSize.Insert(0, size);

                boxSize.SelectedItem = size;
            }
            get
            {
                double size;

                if (!Double.TryParse(boxSize.Text, out size)) size = 8.25;

                return size / 0.75;
            }
        }

        //생성자
        public FontDialog()
        {
            Title = "Font";
            ShowInTaskbar = false;
            WindowStyle = WindowStyle.ToolWindow;
            WindowStartupLocation = WindowStartupLocation.CenterOwner;
            SizeToContent = SizeToContent.WidthAndHeight;
            ResizeMode = ResizeMode.NoResize;

            //윈도우 Content를 위해 3개 행을 가진 Grid를 생성
            Grid gridMain = new Grid();
            Content = gridMain;

            //TextBoxWithLister 컨트롤을 위한 행
            RowDefinition rowDef = new RowDefinition();
            rowDef.Height = new GridLength(200, GridUnitType.Pixel);
            gridMain.RowDefinitions.Add(rowDef);

            //샘플 텍스트를 위한 행
            rowDef = new RowDefinition();
            rowDef.Height = new GridLength(150, GridUnitType.Pixel);
            gridMain.RowDefinitions.Add(rowDef);

            //버튼을 위한 행
            rowDef = new RowDefinition();
            rowDef.Height = GridLength.Auto;
            gridMain.RowDefinitions.Add(rowDef);

            //메인 Grid를 위한 행
            ColumnDefinition colDef = new ColumnDefinition();
            colDef.Width = new GridLength(650, GridUnitType.Pixel);
            gridMain.ColumnDefinitions.Add(colDef);

            //TextBoxWithLister 컨트롤을 위해 2개 행과 5개 열을 가진 Grid를 생성
            Grid gridBoxes = new Grid();
            gridMain.Children.Add(gridBoxes);

            //라벨을 위한 행
            rowDef = new RowDefinition();
            rowDef.Height = GridLength.Auto;
            gridBoxes.RowDefinitions.Add(rowDef);

            //EditBoxWithLister 컨트롤을 위한 행
            rowDef = new RowDefinition();
            rowDef.Height = new GridLength(100, GridUnitType.Star);
            gridBoxes.RowDefinitions.Add(rowDef);

            //폰트 패밀리를 위한 첫 번째 열
            colDef = new ColumnDefinition();
            colDef.Width = new GridLength(175, GridUnitType.Star);
            gridBoxes.ColumnDefinitions.Add(colDef);

            //폰트 스타일을 위한 두 번째 열
            colDef = new ColumnDefinition();
            colDef.Width = new GridLength(100, GridUnitType.Star);
            gridBoxes.ColumnDefinitions.Add(colDef);

            //폰트 웨이트를 위한 세 번째 열
            colDef = new ColumnDefinition();
            colDef.Width = new GridLength(175, GridUnitType.Star);
            gridBoxes.ColumnDefinitions.Add(colDef);

            //폰트 스트레치를 위한 네 번째 열
            colDef = new ColumnDefinition();
            colDef.Width = new GridLength(100, GridUnitType.Star);
            gridBoxes.ColumnDefinitions.Add(colDef);

            //크기를 위한 다섯 번째 열
            colDef = new ColumnDefinition();
            colDef.Width = new GridLength(75, GridUnitType.Star);
            gridBoxes.ColumnDefinitions.Add(colDef);

            //TextBoxWithLister 컨트롤과 폰트 패밀리 레이블 생성
            Label lbl = new Label();
            lbl.Content = "Font Family";
            lbl.Margin = new Thickness(12, 12, 12, 0);
            gridBoxes.Children.Add(lbl);
            Grid.SetRow(lbl, 0);
            Grid.SetColumn(lbl, 0);

            boxFamily = new TextBoxWithLister();
            boxFamily.IsReadOnly = true;
            boxFamily.Margin = new Thickness(12, 0, 12, 12);
            gridBoxes.Children.Add(boxFamily);
            Grid.SetRow(boxFamily, 1);
            Grid.SetColumn(boxFamily, 0);

            //TextBoxWithLister 컨트롤과 폰트 스타일 레이블 생성
            lbl = new Label();
            lbl.Content = "Style";
            lbl.Margin = new Thickness(12, 12, 12, 0);
            gridBoxes.Children.Add(lbl);
            Grid.SetRow(lbl, 0);
            Grid.SetColumn(lbl, 1);

            boxStyle = new TextBoxWithLister();
            boxStyle.IsReadOnly = true;
            boxStyle.Margin = new Thickness(12, 0, 12, 12);
            gridBoxes.Children.Add(boxStyle);
            Grid.SetRow(boxStyle, 1);
            Grid.SetColumn(boxStyle, 1);

            //TextBoxWithLister 컨트롤과 폰트 웨이트 레이블 생성
            lbl = new Label();
            lbl.Content = "Weight";
            lbl.Margin = new Thickness(12, 12, 12, 0);
            gridBoxes.Children.Add(lbl);
            Grid.SetRow(lbl, 0);
            Grid.SetColumn(lbl, 2);

            boxWeight = new TextBoxWithLister();
            boxWeight.IsReadOnly = true;
            boxWeight.Margin = new Thickness(12, 0, 12, 12);
            gridBoxes.Children.Add(boxWeight);
            Grid.SetRow(boxWeight, 1);
            Grid.SetColumn(boxWeight, 2);

            //TextBoxWithLister 컨트롤과 폰트 스트레치 레이블 생성
            lbl = new Label();
            lbl.Content = "Stretch";
            lbl.Margin = new Thickness(12, 12, 12, 0);
            gridBoxes.Children.Add(lbl);
            Grid.SetRow(lbl, 0);
            Grid.SetColumn(lbl, 3);

            boxStretch = new TextBoxWithLister();
            boxStretch.IsReadOnly = true;
            boxStretch.Margin = new Thickness(12, 0, 12, 12);
            gridBoxes.Children.Add(boxStretch);
            Grid.SetRow(boxStretch, 1);
            Grid.SetColumn(boxStretch, 3);

            //TextBoxWithLister 컨트롤과 크기 레이블 생성
            lbl = new Label();
            lbl.Content = "Size";
            lbl.Margin = new Thickness(12, 12, 12, 0);
            gridBoxes.Children.Add(lbl);
            Grid.SetRow(lbl, 0);
            Grid.SetColumn(lbl, 4);

            boxSize = new TextBoxWithLister();
            boxSize.Margin = new Thickness(12, 0, 12, 12);
            gridBoxes.Children.Add(boxSize);
            Grid.SetRow(boxSize, 1);
            Grid.SetColumn(boxSize, 4);

            //샘플 텍스트를 보여주기 위한 레이블 생성
            lblDisplay = new Label();
            lblDisplay.Content = "AaBbCc XxYyZz 012345";
            lblDisplay.HorizontalContentAlignment = HorizontalAlignment.Center;
            lblDisplay.VerticalContentAlignment = VerticalAlignment.Center;
            gridMain.Children.Add(lblDisplay);
            Grid.SetRow(lblDisplay, 1);

            //버튼을 위해 5개의 열을 가진 Grid를 생성
            Grid gridButtons = new Grid();
            gridMain.Children.Add(gridButtons);
            Grid.SetRow(gridButtons, 2);

            for (int i = 0; i < 5; i++)
            {
                gridButtons.ColumnDefinitions.Add(new ColumnDefinition());
            }

            //OK 버튼
            Button btn = new Button();
            btn.Content = "OK";
            btn.IsDefault = true;
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            btn.MinWidth = 60;
            btn.Margin = new Thickness(12);
            btn.Click += OkOnClick;
            gridButtons.Children.Add(btn);
            Grid.SetColumn(btn, 1);

            //Cancel 버튼
            btn = new Button();
            btn.Content = "Cancel";
            btn.IsCancel = true;
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            btn.MinWidth = 60;
            btn.Margin = new Thickness(12);
            gridButtons.Children.Add(btn);
            Grid.SetColumn(btn, 3);

            //시스템 폰트 패밀리로 폰트 패밀리 박스를 초기화
            foreach (FontFamily fam in Fonts.SystemFontFamilies)
            {
                boxFamily.Add(fam);
            }

            //폰트 크기 박스를 초기화
            double[] ptSizes = new double[] { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 };
            foreach (double ptSize in ptSizes)
            {
                boxSize.Add(ptSize);
            }

            //이벤트 핸들러를 설정
            boxFamily.SelectionChanged += FamilyOnSelectionChanged;
            boxStyle.SelectionChanged += StyleOnSelectionChanged;
            boxWeight.SelectionChanged += StyleOnSelectionChanged;
            boxStretch.SelectionChanged += StyleOnSelectionChanged;
            boxSize.TextChanged += SizeOnTextChanged;

            //윈도우 프로퍼티를 기반으로 선택 값을 설정
            //(이 부분은 프로퍼티가 설정되면 오버라이딩됨)
            Typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);

            FaceSize = FontSize;

            //키보드 포커스를 설정
            boxFamily.Focus();

            //샘플 텍스트를 수정할 수 있게 함
            isUpdateSuppressed = false;
            UpdateSample();
        }

        //폰트 패밀리 박스에 대한 SelectionChanged 이벤트 핸들러
        void FamilyOnSelectionChanged(object sender, EventArgs args)
        {
            //선택한 패밀리를 구함
            FontFamily fontFamily = (FontFamily)boxFamily.SelectedItem;

            //이전 스타일, 웨이트, 스트레치를 저장
            //이 값은 이 메소드가 처음 불릴 때는 null
            FontStyle? fntStyPrevious = (FontStyle?) boxStyle.SelectedItem;
            FontWeight? fntWtPrevious = (FontWeight?) boxWeight.SelectedItem;
            FontStretch? fntStrPrevious = (FontStretch?) boxStretch.SelectedItem;

            //샘플이 보이지 않게 함
            isUpdateSuppressed = true;

            //스타일, 웨이트, 스트레치 박스를 지움
            boxStyle.Clear();
            boxWeight.Clear();
            boxStretch.Clear();

            //선택된 폰트 패밀리의 typefaces에 대해서 루프를 수행
            foreach (FamilyTypeface ftf in fontFamily.FamilyTypefaces)
            {
                //boxStyle에 스타일을 추가(Normal이 가장 상위에 위치)
                if (!boxStyle.Contains(ftf.Style))
                {
                    if (ftf.Style == FontStyles.Normal)
                        boxStyle.Insert(0, ftf.Style);
                    else
                        boxStyle.Add(ftf.Style);
                }
                //boxWeight에 웨이트를 추가(Normal이 가장 상위에 위치)
                if (!boxWeight.Contains(ftf.Weight))
                {
                    if (ftf.Weight == FontWeights.Normal)
                        boxWeight.Insert(0, ftf.Weight);
                    else
                        boxWeight.Add(ftf.Weight);
                }
                //boxStretch에 스트레치를 추가(Normal이 가장 상위에 위치)
                if (!boxStretch.Contains(ftf.Stretch))
                {
                    if (ftf.Stretch == FontStretches.Normal)
                        boxStretch.Insert(0, ftf.Stretch);
                    else
                        boxStretch.Add(ftf.Stretch);
                }
            }

            //boxStyle에 선택 항목을 설정
            if (boxStyle.Contains(fntStyPrevious))
                boxStyle.SelectedItem = fntStyPrevious;
            else
                boxStyle.SelectedIndex = 0;

            //boxWeight에 선택 항목을 설정
            if (boxWeight.Contains(fntWtPrevious))
                boxWeight.SelectedItem = fntWtPrevious;
            else
                boxWeight.SelectedIndex = 0;

            //boxStretch에 선택 항목을 설정
            if (boxStretch.Contains(fntStrPrevious))
                boxStretch.SelectedItem = fntStrPrevious;
            else
                boxStretch.SelectedIndex = 0;

            //샘플 수정이 가능하게 하고 샘플을 갱신
            isUpdateSuppressed = false;
            UpdateSample();
        }

        //스타일, 웨이트, 스트레치 박스에 대한 SelectionChanged 이벤트 핸들러
        void StyleOnSelectionChanged(object sender, EventArgs args)
        {
            UpdateSample();
        }

        //크기 박스에 대한 TextChanged 이벤트 핸들러
        void SizeOnTextChanged(object sender, TextChangedEventArgs args)
        {
            UpdateSample();
        }

        //샘플 텍스트 갱신
        void UpdateSample()
        {
            if (isUpdateSuppressed) return;

            lblDisplay.FontFamily = (FontFamily)boxFamily.SelectedItem;
            lblDisplay.FontStyle = (FontStyle)boxStyle.SelectedItem;
            lblDisplay.FontWeight = (FontWeight)boxWeight.SelectedItem;
            lblDisplay.FontStretch = (FontStretch)boxStretch.SelectedItem;

            double size;

            if (!Double.TryParse(boxSize.Text, out size))
                size = 8.25;

            lblDisplay.FontSize = size / 0.75;
        }

        //OK 버튼을 누르면 대화상자를 종료
        void OkOnClick(object sender, RoutedEventArgs args)
        {
            DialogResult = true;
        }
    }
}

 

[TextBoxWithLister.cs 파일]

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ChooseFont
{
    public class TextBoxWithLister : ContentControl
    {
        TextBox txtBox;
        Lister lister;
        bool isReadOnly;

        //Public 이벤트
        public event EventHandler SelectionChanged;
        public event TextChangedEventHandler TextChanged;

        //생성자
        public TextBoxWithLister()
        {
            //윈도우 Content를 위해 DockPanel 생성
            DockPanel dock = new DockPanel();
            Content = dock;

            //상단에 위치할 텍스트 박스
            txtBox = new TextBox();
            txtBox.TextChanged += TextBoxOnTextChanged;
            dock.Children.Add(txtBox);
            DockPanel.SetDock(txtBox, Dock.Top);

            //DockPanel의 나머지에 Lister를 추가
            lister = new Lister();
            lister.SelectionChanged += ListerOnSelectionChanged;
            dock.Children.Add(lister);
        }

        //텍스트 박스 항목과 관련된 Public 프로퍼티
        public string Text
        {
            get { return txtBox.Text; }
            set { txtBox.Text = value; }
        }
        public bool IsReadOnly
        {
            get { return isReadOnly; }
            set { isReadOnly = value; }
        }

        //Lister 요소의 다른 public 프로퍼티 인터페이스
        public object SelectedItem
        {
            set
            {
                lister.SelectedItem = value;

                if (lister.SelectedItem != null)
                    txtBox.Text = lister.SelectedItem.ToString();
                else
                    txtBox.Text = "";
            }
            get
            {
                return lister.SelectedItem;
            }
        }
        public int SelectedIndex
        {
            set
            {
                lister.SelectedIndex = value;

                if (lister.SelectedIndex == -1)
                    txtBox.Text = "";
                else
                    txtBox.Text = lister.SelectedItem.ToString();
            }
            get
            {
                return lister.SelectedIndex;
            }
        }
        public void Add(object obj)
        {
            lister.Add(obj);
        }
        public void Insert(int index, object obj)
        {
            lister.Insert(index, obj);
        }
        public void Clear()
        {
            lister.Clear();
        }
        public bool Contains(object obj)
        {
            return lister.Contains(obj);
        }

        //마우스를 클릭하면 키보드 포커스를 설정
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            Focus();
        }

        //키보드가 포커스를 갖게 되면 텍스트 박스에 포커스를 설정
        protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            base.OnGotKeyboardFocus(e);

            if (e.NewFocus == this)
            {
                txtBox.Focus();
                if (SelectedIndex == -1 && lister.Count > 0)
                    SelectedIndex = 0;
            }
        }

        //첫 문자를 입력하면 이 값을 GoToLetter 메소드로 넘김
        protected override void OnPreviewTextInput(TextCompositionEventArgs e)
        {
            base.OnPreviewTextInput(e);

            if (IsReadOnly)
            {
                lister.GoToLetter(e.Text[0]);
                e.Handled = true;
            }
        }

        //선택 항목을 변경하기 위해 커서 이동키를 처리
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);

            if (SelectedIndex == -1) return;

            switch(e.Key)
            {
                case Key.Home:
                    if (lister.Count > 0) SelectedIndex = 0;
                    break;
                case Key.End:
                    if (lister.Count > 0) SelectedIndex = lister.Count - 1;
                    break;
                case Key.Up:
                    if (SelectedIndex > 0) SelectedIndex--;
                    break;
                case Key.Down:
                    if (SelectedIndex < lister.Count - 1) SelectedIndex++;
                    break;
                case Key.PageUp:
                    lister.PageUp();
                    break;
                case Key.PageDown:
                    lister.PageDown();
                    break;
                default:
                    return;
            }
            e.Handled = true;
        }

        //이벤트 핸들러와 트리거
        void ListerOnSelectionChanged(object sender, EventArgs args)
        {
            if (SelectedIndex == -1)
                txtBox.Text = "";
            else
                txtBox.Text = lister.SelectedItem.ToString();

            OnSelectionChanged(args);
        }

        void TextBoxOnTextChanged(object sender, TextChangedEventArgs args)
        {
            if (TextChanged != null) TextChanged(this, args);
        }
        protected virtual void OnSelectionChanged(EventArgs args)
        {
            if (SelectionChanged != null) SelectionChanged(this, args);
        }
    }
}

 

[Lister.cs 파일]

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace ChooseFont
{
    public class Lister : ContentControl
    {
        ScrollViewer scroll;
        StackPanel stack;
        ArrayList list = new ArrayList();
        int indexSelected = -1;

        //Public 이벤트
        public event EventHandler SelectionChanged;

        //생성자
        public Lister()
        {
            Focusable = false;

            //윈도우의 Content를 ContentControl의 Border로 설정
            Border border = new Border();
            border.BorderThickness = new Thickness(1);
            border.BorderBrush = SystemColors.ActiveBorderBrush;
            border.Background = SystemColors.WindowBrush;
            Content = border;

            //border의 자식으로 ScrollViewer 생성
            scroll = new ScrollViewer();
            scroll.Focusable = false;
            scroll.Padding = new Thickness(2, 0, 0, 0);
            border.Child = scroll;

            //ScrollViewer의 Content로 스택 패널을 생성
            stack = new StackPanel();
            scroll.Content = stack;

            //마우스 왼쪽 버튼에 대한 핸들러를 연결
            AddHandler(TextBlock.MouseLeftButtonDownEvent, new MouseButtonEventHandler(TextBlockOnMouseLeftButtonDown));

            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            //Lister가 처음 보여질 때 뷰에 선택된 항목을 스크롤
            ScrollIntoView();
        }

        //Lister에 항목을 추가하고, 삽입하는 Public 메소드
        public void Add(object obj)
        {
            list.Add(obj);
            TextBlock txtBlk = new TextBlock();
            txtBlk.Text = obj.ToString();
            stack.Children.Add(txtBlk);
        }

        public void Insert(int index, object obj)
        {
            list.Insert(index, obj);
            TextBlock txtBlk = new TextBlock();
            txtBlk.Text = obj.ToString();
            stack.Children.Insert(index, txtBlk);
        }

        public void Clear()
        {
            SelectedIndex = -1;
            stack.Children.Clear();
            list.Clear();
        }

        public bool Contains(object obj)
        {
            return list.Contains(obj);
        }

        public int Count
        {
            get { return list.Count; }
        }

        //입력한 문자에 따라 항목이 선택되게 하기 위해 호출되는 메소드
        public void GoToLetter(char ch)
        {
            int offset = SelectedIndex + 1;

            for (int i = 0; i < Count; i++)
            {
                int index = (i + offset) % Count;

                if (Char.ToUpper(ch) == Char.ToUpper(list[index].ToString()[0]))
                {
                    SelectedIndex = index;
                    break;
                }
            }
        }

        //선택바를 출력하기 위한 SelectedIndex 프로퍼티
        public int SelectedIndex
        {
            set
            {
                if (value < -1 || value >= Count)
                    throw new ArgumentOutOfRangeException("SelectedIndex");

                if (value == indexSelected) return;

                if (indexSelected != -1)
                {
                    TextBlock txtBlk = stack.Children[indexSelected] as TextBlock;
                    txtBlk.Background = SystemColors.WindowBrush;
                    txtBlk.Foreground = SystemColors.WindowTextBrush;
                }

                indexSelected = value;

                if (indexSelected > -1)
                {
                    TextBlock txtBlk = stack.Children[indexSelected] as TextBlock;
                    txtBlk.Background = SystemColors.HighlightBrush;
                    txtBlk.Foreground = SystemColors.HighlightTextBrush;
                }
                ScrollIntoView();

                //SelectionChanged 이벤트 트리거
                OnSelectionChanged(EventArgs.Empty);
            }
            get
            {
                return indexSelected;
            }
        }

        //SelectionItem 프로퍼티는 SelectedIndex를 이용
        public object SelectedItem
        {
            set
            {
                SelectedIndex = list.IndexOf(value);
            }
            get
            {
                if (SelectedIndex > -1) return list[SelectedIndex];
                return null;
            }
        }

        //리스트에서 페이지 업, 페이지 다운하는 Public 메소드
        public void PageUp()
        {
            if (SelectedIndex == -1 || Count == 0) return;

            int index = SelectedIndex - (int)(Count * scroll.ViewportHeight / scroll.ExtentHeight);
            if (index < 0) index = 0;

            SelectedIndex = index;
        }

        public void PageDown()
        {
            if (SelectedIndex == -1 || Count == 0) return;

            int index = SelectedIndex + (int)(Count * scroll.ViewportHeight / scroll.ExtentHeight);
            if (index > Count - 1) index = Count - 1;

            SelectedIndex = index;
        }

        //뷰에서 선택 항목을 스크롤하는 Private 메소드
        void ScrollIntoView()
        {
            if (Count == 0 || SelectedIndex == -1 || scroll.ViewportHeight > scroll.ExtentHeight) return;

            double heightPerItem = scroll.ExtentHeight / Count;
            double offsetItemTop = SelectedIndex * heightPerItem;
            double offsetItemBot = (SelectedIndex + 1) * heightPerItem;

            if (offsetItemTop < scroll.VerticalOffset) scroll.ScrollToVerticalOffset(offsetItemTop);
            else if (offsetItemBot > scroll.VerticalOffset + scroll.ViewportHeight)
            {
                scroll.ScrollToVerticalOffset(scroll.VerticalOffset + offsetItemBot - scroll.VerticalOffset - scroll.ViewportHeight);
            }
        }

        //이벤트 핸들러와 트리거
        void TextBlockOnMouseLeftButtonDown(object sender, MouseButtonEventArgs args)
        {
            if (args.Source is TextBlock)
                SelectedIndex = stack.Children.IndexOf(args.Source as TextBlock);
        }

        protected virtual void OnSelectionChanged(EventArgs args)
        {
            if (SelectionChanged != null)
                SelectionChanged(this, args);
        }
    }
}

 

반응형
반응형

찰스 페졸드의 WPF 를 읽으면서 정리한 Banner 인쇄(PrintBanner) 소스.

 

[PrintBanner.cs 파일]

using System;
using System.Printing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace PrintBanner
{
    public class PrintBanner : Window
    {
        TextBox txtBox;

        [STAThread]
        public static void Main()
        {
            Application app = new Application();
            app.Run(new PrintBanner());
        }

        public PrintBanner()
        {
            Title = "Print Banner";
            SizeToContent = SizeToContent.WidthAndHeight;

            //윈도우 Content를 위한 스택 패널 생성
            StackPanel stack = new StackPanel();
            Content = stack;

            //텍스트 박스 생성
            txtBox = new TextBox();
            txtBox.Width = 250;
            txtBox.Margin = new Thickness(12);
            stack.Children.Add(txtBox);

            //버튼 생성
            Button btn = new Button();
            btn.Content = "_Print...";
            btn.Margin = new Thickness(12);
            btn.Click += PrintOnClick;
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            stack.Children.Add(btn);

            txtBox.Focus();
        }

        private void PrintOnClick(object sender, RoutedEventArgs e)
        {
            PrintDialog dlg = new PrintDialog();

            if (dlg.ShowDialog().GetValueOrDefault())
            {
                //인쇄 방향이 수직인지 확인
                PrintTicket prnTkt = dlg.PrintTicket;
                prnTkt.PageOrientation = PageOrientation.Portrait;
                
                dlg.PrintTicket = prnTkt;

                //BannerDocumentPaginator 객체 생성
                BannerDocumentPaginator paginator = new BannerDocumentPaginator();

                //텍스트 박스로 Text 프로퍼티를 설정
                paginator.Text = txtBox.Text;

                //종이의 크기를 기반으로 PageSize 프로퍼티 설정
                paginator.PageSize = new Size(dlg.PrintableAreaWidth, dlg.PrintableAreaHeight);

                //Call PrintDocument to print the document
                dlg.PrintDocument(paginator, "Banner : " + txtBox.Text);
            }
        }
    }
}

 

[BannerDocumentPaginator.cs 파일]

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

namespace PrintBanner
{
    public class BannerDocumentPaginator : DocumentPaginator
    {
        string txt = "";
        Typeface face = new Typeface("");
        Size sizePage;
        Size sizeMax = new Size(0, 0);

        //이 DocumentPaginator에 특화된 Public 프로퍼티
        public string Text
        {
            get { return txt; }
            set { txt = value; }
        }

        public Typeface Typeface
        {
            get { return face; }
            set { face = value; }
        }
        
        //FormattedText 객체를 생성하는 Private 함수
        FormattedText GetFormattedText(char ch, Typeface face, double em)
        {
            return new FormattedText(ch.ToString(), CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                                    face, em, Brushes.Black);
        }

        //오버라이딩이 필요
        public override bool IsPageCountValid
        {
            get
            {
                //100em 사이즈를 기반으로 문자의 최대 크기를 결정
                foreach (char ch in txt)
                {
                    FormattedText formTxt = GetFormattedText(ch, face, 100);
                    sizeMax.Width = Math.Max(sizeMax.Width, formTxt.Width);
                    sizeMax.Height = Math.Max(sizeMax.Height, formTxt.Height);
                }
                return true;
            }
        }

        public override int PageCount
        {
            get { return txt == null ? 0 : txt.Length; }
        }

        public override Size PageSize
        {
            get { return sizePage; }
            set { sizePage = value; }
        }

        public override DocumentPage GetPage(int numPage)
        {
            DrawingVisual vis = new DrawingVisual();
            DrawingContext dc = vis.RenderOpen();

            //em 사이즈의 factor를 계산할 때 1/2인치 여백을 가정
            double factor = Math.Min((PageSize.Width - 96) / sizeMax.Width,
                                    (PageSize.Height - 96) / sizeMax.Height);

            FormattedText formTxt = GetFormattedText(txt[numPage], face, factor * 100);

            //페이지 중앙에 위치할 수 있게 좌표 계산
            Point ptText = new Point((PageSize.Width - formTxt.Width) / 2,
                                    (PageSize.Height - formTxt.Height) / 2);

            dc.DrawText(formTxt, ptText);
            dc.Close();

            return new DocumentPage(vis);
        }

        public override IDocumentPaginatorSource Source
        {
            get { return null; }
        }
    }
}

 

 

반응형

+ Recent posts