반응형

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

반응형
반응형

프로그램에서 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; }
        }
    }
}

 

 

반응형
반응형

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

UIElement에서 파생된 클래스 인스턴스를 인쇄할 때 중요한 단계가 필요하며, 엘리먼트를 배치하기 위해 객체의 Measure와 Arrange 메소드를 호출해야 한다.

Arrange 메소드 호출 후에 UIElement의 InvalidateArrange 메서드를 호출해야 인쇄 시 정상적으로 표시가 된다.

 

[PrintaBunchaButtons.cs 파일]

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

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

        public PrintaBunchaButtons()
        {
            Title = "Print a Bunch of Buttons";
            SizeToContent = SizeToContent.WidthAndHeight;
            ResizeMode = ResizeMode.CanMinimize;

            //Print 버튼 생성
            Button btn = new Button();
            btn.FontSize = 24;
            btn.Content = "Print ...";
            btn.Padding = new Thickness(12);
            btn.Margin = new Thickness(96);
            btn.Click += PrintOnClick;
            Content = btn;
        }

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

            if ((bool)dlg.ShowDialog().GetValueOrDefault())
            {
                //그리드 패널 생성
                Grid grid = new Grid();

                //자동으로 크기가 변하는 열과 행을 5개 정의
                for (int i = 0; i < 5; i++)
                {
                    ColumnDefinition colDef = new ColumnDefinition();
                    colDef.Width = GridLength.Auto;
                    grid.ColumnDefinitions.Add(colDef);

                    RowDefinition rowDef = new RowDefinition();
                    rowDef.Height = GridLength.Auto;
                    grid.RowDefinitions.Add(rowDef);
                }

                //그라디언트 브러시로 그리드의 배경색을 지정
                grid.Background = new LinearGradientBrush(Colors.Gray, Colors.White, 
                                                          new Point(0, 0), new Point(1, 1));

                //난수 생성
                Random rand = new Random();

                //25개 버튼으로 Grid를 채움
                for (int i = 0; i < 25; i++)
                {
                    Button btn = new Button();
                    btn.FontSize = 12 + rand.Next(8);
                    btn.Content = "Button No. " + (i + 1);
                    btn.HorizontalAlignment = HorizontalAlignment.Center;
                    btn.VerticalAlignment = VerticalAlignment.Center;
                    btn.Margin = new Thickness(6);
                    grid.Children.Add(btn);
                    Grid.SetRow(btn, i % 5);
                    Grid.SetColumn(btn, i / 5);
                }

                //그리드 크기 결정
                grid.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

                Size sizeGrid = grid.DesiredSize;

                //페이지상의 그리드의 중앙점을 결정
                Point ptGrid = new Point((dlg.PrintableAreaWidth - sizeGrid.Width) / 2,
                                         (dlg.PrintableAreaHeight - sizeGrid.Height) / 2);

                //레이아웃은 설정하지 않고 통과
                grid.Arrange(new Rect(ptGrid, sizeGrid));
                grid.InvalidateArrange();
                //인쇄
                dlg.PrintVisual(grid, Title);
            }
        }
    }
}
반응형
반응형

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

2개의 클래스 파일을 작성했으며, PrintWithMargins.cs 와 PageMarginsDialog.cs 파일로 구성된다.

 

[PrintWithMargins.cs 파일]

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace PrintWithMargins
{
    public class PageMarginsDialog : Window
    {
        //종이 테두리를 참조하는 내부 열거형
        enum Side
        {
            Left, Right, Top, Bottom
        }

        //숫자 입력을 위한 텍스트 박스 4개
        TextBox[] txtBox = new TextBox[4];
        Button btnOk;

        //페이지 여백을 위한 타입 Thickness의 Public 프로퍼티
        public Thickness PageMargins
        {
            set
            {
                txtBox[(int)Side.Left].Text = (value.Left / 96).ToString("F3");
                txtBox[(int)Side.Right].Text = (value.Right / 96).ToString("F3");
                txtBox[(int)Side.Top].Text = (value.Top / 96).ToString("F3");
                txtBox[(int)Side.Bottom].Text = (value.Bottom / 96).ToString("F3");
            }
            get
            {
                return new Thickness(Double.Parse(txtBox[(int) Side.Left].Text) * 96,
                                    Double.Parse(txtBox[(int)Side.Right].Text) * 96,
                                    Double.Parse(txtBox[(int)Side.Top].Text) * 96,
                                    Double.Parse(txtBox[(int)Side.Bottom].Text) * 96);
            }
        }

        //생성자
        public PageMarginsDialog()
        {
            //대화상자를 위한 표준 설정
            Title = "Page Setup";
            ShowInTaskbar = false;
            WindowStyle = WindowStyle.ToolWindow;
            WindowStartupLocation = WindowStartupLocation.CenterOwner;
            SizeToContent = SizeToContent.WidthAndHeight;
            ResizeMode = ResizeMode.NoResize;

            //윈도우 Content를 위한 스택 패널을 만든다.
            StackPanel stack = new StackPanel();
            Content = stack;

            //스택 패널의 자식으로 그룹 박스를 생성
            GroupBox grpBox = new GroupBox();
            grpBox.Header = "Margins (inches)";
            grpBox.Margin = new Thickness(12);
            stack.Children.Add(grpBox);

            //그룹 박스의 내용으로 그리드를 생성
            Grid grid = new Grid();
            grid.Margin = new Thickness(6);
            grpBox.Content = grid;

            //2개의 행과 4개의 열
            for (int i = 0; i < 2; i++)
            {
                RowDefinition rowDef = new RowDefinition();
                rowDef.Height = GridLength.Auto;
                grid.RowDefinitions.Add(rowDef);
            }

            for (int i = 0; i < 4; i++)
            {
                ColumnDefinition colDef = new ColumnDefinition();
                colDef.Width = GridLength.Auto;
                grid.ColumnDefinitions.Add(colDef);
            }

            //그리드에 레이블과 텍스트 박스 컨트롤을 추가
            for (int i = 0; i < 4; i++)
            {
                Label lbl = new Label();
                lbl.Content = "_" + Enum.GetName(typeof(Side), i) + ":";
                lbl.Margin = new Thickness(6);
                lbl.VerticalAlignment = VerticalAlignment.Center;
                grid.Children.Add(lbl);
                Grid.SetRow(lbl, i / 2);
                Grid.SetColumn(lbl, 2 * (i % 2));

                txtBox[i] = new TextBox();
                txtBox[i].TextChanged += TextBoxOnTextChanged;
                txtBox[i].MinWidth = 48;
                txtBox[i].Margin = new Thickness(6);
                grid.Children.Add(txtBox[i]);
                Grid.SetRow(txtBox[i], i / 2);
                Grid.SetColumn(txtBox[i], 2 * (i % 2) + 1);
            }

            //OK와 Cancel 버튼을 위해 UniformGrid를 이용
            UniformGrid uniGrid = new UniformGrid();
            uniGrid.Rows = 1;
            uniGrid.Columns = 2;
            stack.Children.Add(uniGrid);

            btnOk = new Button();
            btnOk.Content = "OK";
            btnOk.IsDefault = true;
            btnOk.IsEnabled = false;
            btnOk.MinWidth = 60;
            btnOk.Margin = new Thickness(12);
            btnOk.HorizontalAlignment = HorizontalAlignment.Center;
            btnOk.Click += OkButtonOnClick;
            uniGrid.Children.Add(btnOk);

            Button btnCancel = new Button();
            btnCancel.Content = "Cancel";
            btnCancel.IsCancel = true;
            btnCancel.MinWidth = 60;
            btnCancel.Margin = new Thickness(12);
            btnCancel.HorizontalAlignment = HorizontalAlignment.Center;
            uniGrid.Children.Add(btnCancel);
        }
        /// <summary>
        /// OK를 클릭하면 대화상자를 종료함
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OkButtonOnClick(object sender, RoutedEventArgs e)
        {
            DialogResult = true;
        }
        /// <summary>
        /// 텍스트 박스의 값이 숫자이면 OK 버튼을 활성화
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBoxOnTextChanged(object sender, TextChangedEventArgs e)
        {
            double result;

            btnOk.IsEnabled = Double.TryParse(txtBox[(int)Side.Left].Text, out result) &&
                            Double.TryParse(txtBox[(int)Side.Right].Text, out result) &&
                            Double.TryParse(txtBox[(int)Side.Top].Text, out result) &&
                            Double.TryParse(txtBox[(int)Side.Bottom].Text, out result);
        }
    }
}

 

 

[PageMarginsDialog.cs 파일]

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

namespace PrintWithMargins
{
    public class PrintWithMargins : Window
    {
        //PringDialog의 정보를 저장하기 위한 Private 필드
        PrintQueue prnQueue;
        PrintTicket prnTkt;
        Thickness marginPage = new Thickness(96);

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

        public PrintWithMargins()
        {
            Title = "Print with Margins";
            FontSize = 24;

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

            //페이지 설정 버튼 생성
            Button btn = new Button();
            btn.Content = "Page Set_up...";
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            btn.Margin = new Thickness(24);
            btn.Click += SetupOnClick;
            stack.Children.Add(btn);

            //인쇄 버튼 생성
            btn = new Button();
            btn.Content = "_Print...";
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            btn.Margin = new Thickness(24);
            btn.Click += PrintOnClick;
            stack.Children.Add(btn);
        }
        /// <summary>
        /// 인쇄 버튼 : PringDialog 실행
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PrintOnClick(object sender, RoutedEventArgs e)
        {
            PrintDialog dlg = new PrintDialog();

            //PrintQueue와 PrintTicket 설정
            if (prnQueue != null) dlg.PrintQueue = prnQueue;

            if (prnTkt != null) dlg.PrintTicket = prnTkt;

            if (dlg.ShowDialog().GetValueOrDefault())
            {
                //PrintQueue와 PrintTicket을 대화상자의 값으로 설정
                prnQueue = dlg.PrintQueue;
                prnTkt = dlg.PrintTicket;

                //DrawingVisual을 생성하고 DrawingContext를 염
                DrawingVisual vis = new DrawingVisual();
                DrawingContext dc = vis.RenderOpen();
                Pen pen = new Pen(Brushes.Black, 1);

                //Rectangle은 여백을 뺀 페이지를 나타냄
                Rect rectPage = new Rect(marginPage.Left, marginPage.Top, 
                                         dlg.PrintableAreaWidth - (marginPage.Left + marginPage.Right), 
                                         dlg.PrintableAreaHeight - (marginPage.Top + marginPage.Bottom));

                //사용자 여백을 반영한 사각형 출력
                dc.DrawRectangle(null, pen, rectPage);

                //PrintableArea 프로퍼티를 보여주는 포맷팅된 텍스트 객체를 생성
                FormattedText formattedText = new FormattedText(String.Format("Hello, Printer! {0} x {1}",
                                                                dlg.PrintableAreaWidth / 96, dlg.PrintableAreaHeight / 96),
                                                                CultureInfo.CurrentCulture,
                                                                FlowDirection.LeftToRight,
                                                                new Typeface(new FontFamily("Times New Roman"), 
                                                                                FontStyles.Italic, FontWeights.Normal, FontStretches.Normal),
                                                                48, Brushes.Black);

                //포맷된 텍스트 스트링의 물리적 크기를 계산
                Size sizeText = new Size(formattedText.Width, formattedText.Height);

                //여백 내의 텍스트의 중앙점을 계산
                Point ptText = new Point(rectPage.Left + (rectPage.Width - formattedText.Width) / 2, 
                                         rectPage.Top + (rectPage.Height - formattedText.Height) / 2);

                //텍스트와 이를 둘러싸는 사각형 출력
                dc.DrawText(formattedText, ptText);
                dc.DrawRectangle(null, pen, new Rect(ptText, sizeText));

                //DrawingContext를 종료
                dc.Close();

                //끝으로 페이지를 인쇄
                dlg.PrintVisual(vis, Title);
            }
        }
        /// <summary>
        /// 페이지 설정 버튼 : PageMarginsDialog 실행
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SetupOnClick(object sender, RoutedEventArgs e)
        {
            //대화상자를 생성하고 PageMargins 프로퍼티를 초기화
            PageMarginsDialog dlg = new PageMarginsDialog();
            dlg.Owner = this;
            dlg.PageMargins = marginPage;

            if (dlg.ShowDialog().GetValueOrDefault())
            {
                //대화상자의 페이지 여백을 저장
                marginPage = dlg.PageMargins;
            }
        }
    }
}

 

 

 

반응형
반응형

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

책을 읽으면서 직접 코딩을 하면서 정리했던 소스들을 그대로 옮겨 보았다.

 

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

namespace PrintEllipse
{
    public class PrintEllipse : Window
    {
        private PrintTicket prntkt;

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

        public PrintEllipse()
        {
            Title = "Print Ellipse";
            FontSize = 24;

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

            //인쇄를 위한 버튼 생성
            Button btn = new Button();
            btn.Content = "_Print...";
            btn.HorizontalAlignment = HorizontalAlignment.Center;
            btn.Margin = new Thickness(24);
            btn.Click += PrintOnClick;
            stack.Children.Add(btn);
        }

        private void PrintOnClick(object sender, RoutedEventArgs e)
        {
            PrintDialog dlg = new PrintDialog();
            if (prntkt != null) dlg.PrintTicket = prntkt;

            //페이지지정 라디오버튼을 활성화하기 위해서는 UserPageRangeEnabled를 true로 해야 한다.
            dlg.UserPageRangeEnabled = true;

            //ShowDialog는 널 값이 가능한 bool을 반환하게 정의되어 있다. 대화상자 클래스의 ShowDialog 메소드는 true/false/null을 반환한다.
            //사용자가 인쇄 버튼을 클릭하면 true를, 취소 버튼을 클릭하면 false를, 제목 옆의 닫기 버튼을 클릭하면 null을 반환한다.
            //GetValueOrDefault 메소드는 if문을 위해 결과가 항상 bool 형태가 되게 null 값을 false로 바꾸어 준다.
            if ((bool)dlg.ShowDialog().GetValueOrDefault())
            {
                prntkt = dlg.PrintTicket;

                //DrawingVisual을 생성하고 DrawingContext를 준비
                DrawingVisual vis = new DrawingVisual();
                DrawingContext dc = vis.RenderOpen();

                //타원을 출력
                //PrintableAreaWidth와 PrintableAreaHeight는 페이지의 인쇄 가능 영역을 참조하는 것이 아니라 장치 독립적인 단위(1/96)로 페이지의 전체 물리적인 크기를 가리킨다.
                dc.DrawEllipse(Brushes.LightGray, new Pen(Brushes.Black, 3),
                                new Point(dlg.PrintableAreaWidth / 2, dlg.PrintableAreaHeight / 2),
                                dlg.PrintableAreaWidth / 2 - 10, dlg.PrintableAreaHeight / 2 - 10);

                //DrawingCOntext를 닫음
                dc.Close();

                //끝으로 페이지를 인쇄
                dlg.PrintVisual(vis, "My First Print Job");
            }
        }
    }
}
반응형

+ Recent posts