반응형

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

반응형
반응형

Image img = new Image();
img.Source = new BitmapImage(new Uri(“pack://application:,,,/Assambly Name;component/Resources/aaa.png”, UriKind.RelativeOrAbsolute));

반응형

'C# 개발 > WPF' 카테고리의 다른 글

WPF에서 UserControl 마우스로 이동 처리 방법  (0) 2021.06.29
Observable Collection에서 Item 찾기  (0) 2019.01.10
C1 DataGrid 필터링 설정  (0) 2019.01.07
반응형

사용자 컨트롤을 마우스 좌측 버튼을 눌러서 이동시키고자 할 경우 유용하게 사용할 수 있는 코드이다.

MemoViewControl 클래스는 내가 만든 사용자 컨트롤이며, 이 컨트롤을 선택해서 마우스로 선택/이동 하는 기능을 수행한다.

MemoViewControl 부분을 각자가 만든 UserControl로 바꾸어서 사용하면 된다.

MemoViewControl에 대한 마우스 Down/Up/Move 이벤트를 아래와 같이 처리해서 사용하면 된다.

        private object movingObject;
        private double firstXPos, firstYPos;

        private void MemoView_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && sender == movingObject)
            {
                MemoViewControl memoCtnl = sender as MemoViewControl;
                Canvas canvas = memoCtnl.Parent as Canvas;
                // Horizontal
                double newLeft = e.GetPosition(canvas).X - firstXPos - canvas.Margin.Left;
                // newLeft inside canvas right-border?
                if (newLeft > canvas.Margin.Left + canvas.ActualWidth - memoCtnl.ActualWidth)
                    newLeft = canvas.Margin.Left + canvas.ActualWidth - memoCtnl.ActualWidth;
                // newLeft inside canvas left-border?
                else if (newLeft < canvas.Margin.Left)
                    newLeft = canvas.Margin.Left;

                memoCtnl.SetValue(Canvas.LeftProperty, newLeft);

                //Vertical
                double newTop = e.GetPosition(canvas).Y - firstYPos - canvas.Margin.Top;
                // newTop inside canvas bottom-border?
                // -- Bottom --
                if (newTop > canvas.Margin.Top + canvas.ActualHeight - memoCtnl.ActualHeight)
                    newTop = canvas.Margin.Top + canvas.ActualHeight - memoCtnl.ActualHeight;
                // newTop inside canvas top-border?
                // -- Top --
                else if (newTop < canvas.Margin.Top)
                    newTop = canvas.Margin.Top;

                memoCtnl.SetValue(Canvas.TopProperty, newTop);
            }
        }

        private void MemoView_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            selectMemo = (MemoViewControl)sender;
            Canvas canvas = selectMemo.Parent as Canvas;

            movingObject = null;
            
            Mouse.Capture(selectMemo);
        }              

        private void MemoView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {            
            selectMemo = (MemoViewControl) sender;
            Canvas canvas = selectMemo.Parent as Canvas;

            firstXPos = e.GetPosition(selectMemo).X;
            firstYPos = e.GetPosition(selectMemo).Y;

            movingObject = sender;            
            Mouse.Capture(null);
        }

 

 

반응형

'C# 개발 > WPF' 카테고리의 다른 글

WPF에서 리소스에 등록된 이미지 가져오기  (0) 2021.08.23
Observable Collection에서 Item 찾기  (0) 2019.01.10
C1 DataGrid 필터링 설정  (0) 2019.01.07
반응형

아래와 같이 ObservableCollection이 있다고 할때, 특정 Item만 찾아서 처리를 해야 할 경우

 

public struct PLU {
        
public int ID { get; set; }
        
public string name { get; set; }
        
public double price { get; set; }
        
public int quantity {get;set;}

}
public static ObservableCollection<PLU> PLUList = new ObservableCollection<PLU>();

 

PLU item = PLUList.Where(z => z.ID == 12).FirstOrDefault();

if (item != null)

{

//item이 있을 경우

}

else

{

//item이 존재하지 않을 경우

}

반응형
반응형

C1Component의 DataGrid에서 필터를 적용하고자 할 경우 아래와 같이 적용하면 된다.

 

//필터 삭제(초기화)
FilterExample.FilterBy(new DataGridColumnValue<DataGridFilterState>[0]);

 

//첫 번째 필터링

 

string exam1 = cboExam1.SelectedValue.ToString();

List<DataGridFilterInfo> filterInfoList1 = new List<DataGridFilterInfo>();
DataGridFilterState filterState1 = new DataGridFilterState();
filterInfoList1.Add(new DataGridFilterInfo() { FilterOperation = DataGridFilterOperation.Equal, FilterType = DataGridFilterType.Text, Value = exam1 });
filterState1.FilterInfo = filterInfoList1;

 

string exam2 = cboExam2.SelectedValue.ToString();

//두 번째 필터링 (AND)

List<DataGridFilterInfo> filterInfoList2 = new List<DataGridFilterInfo>();
DataGridFilterState filterState2 = new DataGridFilterState();
filterInfoList2.Add(new DataGridFilterInfo() { FilterOperation = DataGridFilterOperation.Equal, FilterCombination = DataGridFilterCombination.And,

                                                          FilterType = DataGridFilterType.Text, Value = exam2 });
filterState2.FilterInfo = filterInfoList2;

 

//2개의 필터를 KeyValuePair로 생성

KeyValuePair<C1.WPF.DataGrid.DataGridColumn, DataGridFilterState>[] filters =

                    new KeyValuePair<C1.WPF.DataGrid.DataGridColumn, DataGridFilterState>[2];

filters[0] = new KeyValuePair<C1.WPF.DataGrid.DataGridColumn, DataGridFilterState>(FilterExample.Columns["EXAM_01"], filterState1);
filters[1] = new KeyValuePair<C1.WPF.DataGrid.DataGridColumn, DataGridFilterState>(FilterExample.Columns["EXAM_02"], filterState2);

 

//필터 적용

FilterExample.FilterBy(filters);

반응형

+ Recent posts