= Windows Presentation Foundation =
{{tag>WPF C# Desktop}}
= Orientation =
* 실제 Layout & Event 수정을 위한 파일은 아래 두 가지이다.
* Layout : MainWindow.xaml
* Logic : Mainwindow.xaml.cs
=== 예시 ===
* 클릭하면 TextBox가 "Hello World!" 및 0부터 1씩 증가.
* 추가적으로 배경색을 공유하고, 체크박스에 버튼 내용(Text)를 가져온다.
This is Textbox
using System;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private static int count = 0;
public MainWindow()
{
InitializeComponent();
}
void MyButtonClicked(object sender, RoutedEventArgs e)
{
Button btn = (Button) sender;
if (btn.Name.ToString().Equals("btn1"))
{
count++;
text1.Text = "Hello World! " + count;
}
else
{
text1.Text = "This is Textbox";
count = 0;
}
}
}
}
{{ :wpf-basic.jpg? |}}
= Layout =
* WPF에서 Layout은 **Panel**들에 의해 관리된다.
== StackPanel ==
* 가로 또는 세로 방향으로 지정할 수 있는 한 줄에 자식 요소를 정렬
* Android의 LinearLayout과 같은 개념. (선형 배치)
TextBlock
CheckBox1
CheckBox2
* Orientation : Pannel 안 자식 요소의 **배치 방향** 변경
* Vertical (Default), Horizontal
* VerticalAlignment : 세로 크기를 내용물만큼 조정하고, 상대적인 위치를 속성값에 따라 배치시킨다.
* stretch (Default), center, top, bottom
* HorizontalAlignment : 가로 크기를 내용물만큼 조정하고, M상대적인 위치를 속성값에 따라 배치시킨다.
* stretch (Default), center, left, right
{{ ::stackpanel.jpg? |}}
== WrapPanel ==
* 자식 요소들을 지정 방향에 따라 순서대로 배치하고, 너비를 넘을 때는 적절하게 다음 줄로 이동시킨다.
{{ :wrappanel.jpg |}}
== DockPanel ==
* 레이아웃 컨테이너의 가장자리를 기준으로 자식 콘텐츠의 위치를 지정하는 데 사용.
{{ ::dockpanel.jpg |}}
== Grid ==
* 열과 행으로 자식 요소 배치
* 컬럼 형태를 정의하고, 내용을 채운다. Index를 넘어가는(Overflow) 경우, 마지막 Index에 맞춤.
A
B
C
D
EEE
FFF
{{ ::grid.jpg |}}
=== 여러 행/열 걸치기 및 병합 (RowSpan, ColumnSpan)===
{{ :gridspan1.jpg |}}
A
B
C
D
E
F
{{ ::gridspan.jpg |}}
=== 동적 Grid 생성 ===
/* xaml에서 grid에 "myGrid"라는 Name 부여. 그리고 loaded 연결. */
using System.Windows;
using System.Windows.Controls;
namespace DynamicallyGrid
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
myGrid.ShowGridLines = true;
for(int row=0 ; row<3 ; row++)
myGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(50) });
for (int column = 0; column < 4; column++)
myGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(50) });
SetLocation(new TextBlock() { Text = "Hello" }, 0, 0);
SetLocation(new TextBlock() { Name = "name2", Text = "olleh" }, 1, 1);
SetLocation(new TextBlock() { Text = "WPF" }, 2, 2);
SetLocation(new TextBlock() { Text = "QWERTY" }, 1, 3);
}
private void SetLocation(TextBlock tb, int row, int column)
{
//이름 추적이 가능한 지 시험.
if(tb.Name == "name2")
tb.Text = "Change!";
Grid.SetRow(tb, row);
Grid.SetColumn(tb, column);
myGrid.Children.Add(tb);
}
}
}
{{ :grid_dynamically.png? }}
== UniformGrid ==
* 모든 칸이 똑같은 크기로 Grid안에 정렬된다.
* 균형있게 순차 배치.
{{ ::uniformgrid.jpg |}}
== Canvas ==
* 절대좌표로 자식 요소 위치 지정
{{ ::canvas.jpg |}}
== Viewbox ==
{{ ::viewbox.jpg |}}
== ScrollViewer ==
안쪽에 다른 Layout/Control을 삽입하여 사용
{{ ::scrollviewer.jpg |}}
== Custom Layout ==
Panel 클래스를 상속받아 Method Overriding 한다.
----
= Control =
== Buttons ==
Group1-1
Group1-2
Group1-3
Group2-1
Group2-2
Group2-3
{{ ::buttons.jpg |}}
== CheckBox ==
선택하세요.
CheckBox1
CheckBox2
CheckBox2
{{ :checkbox.jpg |}}
== Slider & Scrollbar ==
Slider vs. ScrollBar => **구간 설정 여부**
{{ :slider.jpg |}}
== ProgressBar ==
* 예제 생략. \\ => 보통 Progressbar는 동적으로 활용하기 때문에 정적인 예제가 필요 없음.
== Text Controls ==
{{ ::textcontrols.jpg |}}
=== TextBox 커서 글자 맨 끝으로 스크롤 ===
public static void tbox_cursor_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tbox = sender as TextBox;
if (tbox == null)
return;
var rect = tbox.GetRectFromCharacterIndex(tbox.Text.Length);
tbox.ScrollToHorizontalOffset(rect.Right);
}
=== 소수만 입력 받는 Textbox ===
public class NumberTextbox : TextBox
{
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
if (((e.Source as TextBox).Text.Contains(".") && e.Text.Equals(".")) || (!e.Text.Equals(".") && !Char.IsDigit(e.Text.ToCharArray()[0])))
e.Handled = true;
else
e.Handled = !AreAllValidNumericChars(e.Text);
base.OnPreviewTextInput(e);
}
bool AreAllValidNumericChars(string str)
{
bool ret = true;
if (str == System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator |
str == System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyGroupSeparator |
str == System.Globalization.NumberFormatInfo.CurrentInfo.CurrencySymbol |
str == System.Globalization.NumberFormatInfo.CurrentInfo.NegativeSign |
str == System.Globalization.NumberFormatInfo.CurrentInfo.NegativeInfinitySymbol |
str == System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator |
str == System.Globalization.NumberFormatInfo.CurrentInfo.NumberGroupSeparator |
str == System.Globalization.NumberFormatInfo.CurrentInfo.PercentDecimalSeparator |
str == System.Globalization.NumberFormatInfo.CurrentInfo.PercentGroupSeparator |
str == System.Globalization.NumberFormatInfo.CurrentInfo.PercentSymbol |
str == System.Globalization.NumberFormatInfo.CurrentInfo.PerMilleSymbol |
str == System.Globalization.NumberFormatInfo.CurrentInfo.PositiveInfinitySymbol |
str == System.Globalization.NumberFormatInfo.CurrentInfo.PositiveSign)
return ret;
for (int i = 0; i < str.Length; i++)
{
char ch = str[i];
ret &= Char.IsDigit(ch);
}
return ret;
}
}
...
...
== ToolTip ==
Plain text is so
last century
{{ :tooltip.jpg |}}
== Label ==
* 단순한 Text 출력 표시용
{{ ::label.jpg |}}
== GroupBox and Expander ==
{{ ::groupboxandexpander.jpg |}}
== List Controls ==
=== ComboBox ===
클릭하면 스크롤하여 목록 표시. (= Android의 Spinner)
A
B
Elipse :
{{ ::combobox.jpg |}}
==== ComboBoxItem의 "Content"로 선택 아이템 초기화 ====
using System;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
cbox.SelectedValue = 4 + " 번";
}
}
}
=== ListBox ===
A
B
TextBlock
{{ ::listbox.jpg |}}
=== TabControl ===
_Text
Hello, World!
TextBlock
{{ ::tabcontrol.jpg |}}
=== ListView ===
{{ ::listview1.jpg |}}
{{ ::listview2.jpg |}}
=== TreeView ===
{{ ::treeview.jpg |}}
== Menus ==
=== Menu ===
동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세
{{ ::menu.jpg |}}
=== ContextMenu ===
{{ ::contextmenu.jpg |}}
== Toolbar ==
{{ ::toolbar.jpg |}}
== GridSplitter ==
Grid의 행/열 재조정.
{{ ::gridsplitter.jpg |}}
== Calendar & DatePicker ==
http://www.dotnetperls.com/calendar
http://www.dotnetperls.com/datepicker
----
= Input =
== Routed Events ==
* Event가 어떻게 제어되는 지 살펴보자.
using System;
using System.Windows;
using System.Windows.Input;
using System.Diagnostics;
namespace Control1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("1-1 Button_PreviewMouseDown");
}
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("1-2 Button_MouseDown");
}
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("2-1 Grid_PreviewMouseDown");
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("2-2 Grid_MouseDown");
}
private void Canvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("3-1 Canvas_PreviewMouseDown");
}
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("3-2 Canvas_MouseDown");
}
private void Ellipse_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("4-1 Ellipse_PreviewMouseDown");
}
private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("4-2 Ellipse_MouseDown");
}
}
}
|<100%>|
| {{ ::routedevents1-1.jpg |}} | {{ ::routedevents1-2.jpg |}} |
* 이렇게 단계적으로 호출되지 않고, **Button 자체에만** Event를 주려면
//중략
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("1-1 Button_PreviewMouseDown");
e.Handled = true;
}
//중략
* 중복 호출을 방지하려면 "+=" 이용.
== Mouse Input ==
* xaml은 그대로 이용.
using System;
using System.Windows;
using System.Windows.Input;
using System.Diagnostics;
using System.Windows.Shapes;
namespace Control1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
myEllipse.MouseDown += myEllipse_MouseDown;
myEllipse.MouseMove += myEllipse_MouseMove;
myEllipse.MouseUp += myEllipse_MouseUp;
}
private void myEllipse_MouseDown(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(myEllipse);
}
private void myEllipse_MouseUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
}
private void myEllipse_MouseMove(object sender, MouseEventArgs e)
{
Debug.WriteLine(Mouse.GetPosition(myEllipse));
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("1-1 Button_PreviewMouseDown");
//e.Handled = true;
}
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("1-2 Button_MouseDown");
}
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("2-1 Grid_PreviewMouseDown");
}
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("2-2 Grid_MouseDown");
}
private void Canvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("3-1 Canvas_PreviewMouseDown");
}
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("3-2 Canvas_MouseDown");
}
private void Ellipse_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("4-1 Ellipse_PreviewMouseDown");
}
private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
{
//Debug.WriteLine("4-2 Ellipse_MouseDown");
}
}
}
* 결과적으로 Ellipse 위에 올릴 때, 움직일 때, 클릭할 때 좌표값을 추적한다.
== Keyboard Input ==
글자 입력 후 Enter!
using System;
using System.Windows;
using System.Windows.Input;
namespace Input1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
textBlock1.Text = "입력한 글자 : " + textBox1.Text;
}
}
}
}
{{ ::keyboardinput.jpg |}}
== Ink Input ==
* 터치 스크린에서의 입력.
== Commands ==
* Ctrl+X 같은 특수 명령.
----
= Simple Data Binding =
== Without Data Binding ==
* getter/setter 클래스를 따로 구현해야 한다.
Name:
Age:
//새로 생성한 getter/setter Class
using System;
namespace SimpleDataBinding
{
class Person
{
string name;
int age;
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
public string Name
{
get {return this.name;}
set {this.name = value;}
}
public int Age
{
get {return this.age;}
set {this.age = value;}
}
}
}
using System.Windows;
namespace SimpleDataBinding
{
public partial class MainWindow : Window
{
Person person = new Person("Tom", 11);
public MainWindow()
{
InitializeComponent();
this.tb_name.Text = person.Name;
this.tb_age.Text = person.Age.ToString();
this.btn_birthday.Click += btn_birthday_Click;
}
private void btn_birthday_Click(object sender, RoutedEventArgs e)
{
++person.Age;
this.tb_age.Text = person.Age.ToString();
MessageBox.Show(string.Format("Happy Birthday, {0}, age {1}!", person.Name, person.Age), "Birthday");
}
}
}
{{ ::withoutdatabinding.jpg |}}
== Data Binding ==
== Debuging Data Binding ==
----
= Binding to List Data =
== (임시) ComboBox Data Binding ==
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
List myList1 = new List();
List myList2 = new List();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
/* 단순 String Binding */
myList1.Add("String1");
myList1.Add("String2");
myList1.Add("String3");
cbox1.ItemsSource = myList1;
cbox1.SelectedIndex = 0;
/* 복합 Data Binding 1 */
myList2.Add(new Person(1, "a", "OK"));
myList2.Add(new Person(2, "b", "Hello"));
myList2.Add(new Person(3, "c", "Hi"));
myList2.Add(new Person(4, "d", "hehehe"));
myList2.Add(new Person(5, "e", "hohoho"));
cbox2.ItemsSource = myList2;
myList2.Add(new Person(77, "XX", "Wooooh")); //추가 삽입
cbox2.ItemsSource = null;
cbox2.ItemsSource = myList2;
cbox2.SelectedIndex = 0; //Default Index 0 지정.
cbox2.SelectionChanged += cbox2_SelectionChanged; //Index 변경 후 Event 걸기
/* 복합 Data Binding 2 */
cbox3.ItemsSource = typeof(Colors).GetProperties();
cbox3.SelectedIndex = 0;
}
private void cbox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsLoaded)
return;
if (cbox2.SelectedIndex < 0)
return;
MessageBox.Show(myList2[cbox2.SelectedIndex].Message);
}
}
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public string Message { get; set; }
public Person(int age, string name, string message)
{
this.Age = age;
this.Name = name;
this.Message = message;
}
}
}
== Binding to List Data ==
== Data Source Providers ==
== Master-Detail Binding ==
== Hierarchical Binding ==
----
= Styles =
== Without Styles ==
== Inline Styles ==
== Named Styles ==
== Element-Typed Styles ==
== Data Templates and Styles ==
== Triggers ==
----
= Control Templates =
== Beyound Styles ==
== Logical and Visual Trees ==
== Data-Driven UI ==
----
= Windows and Dialogs =
== Window ==
== Dialogs ==
----
= Application =
== Windows ==
* 최상위 화면 요소. (WinAPI의 HWND, MFC의 CWnd, WinForms의 Form과 같은 개념)
=== Life Cycles (생명/수명 주기) ===
* 생명주기 중 가장 많이 사용되는 Event
# Loaded : Window가 나타나기 직전
# Closing : Window가 닫히기 직전
# Closed : Window가 닫혔을 때
using System;
using System.Windows;
namespace Windows1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Title = "Hello World! " + DateTime.Now;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
MessageBoxResult result = MessageBox.Show("닫겠습니까?", "TITLE", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No)
e.Cancel = true;
}
private void Window_Closed(object sender, EventArgs e)
{
MessageBox.Show("닫혔습니다.");
}
}
}
=== Windows 표시 ===
* Modaless : 다른 Window와의 상호 작용 가능.
* Show() Method
* Visibility 속성(Property)을 "visible"
* Modal : 다른 Window와의 상호 작용 **불가**.
* ShowDialog() Method
using System.Windows;
namespace Windows2
using System.Windows;
namespace Windows2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ShowMethod(object sender, RoutedEventArgs e)
{
Window w = new Window();
w.Owner = this;
w.Show();
}
private void UseVisibilityProperty(object sender, RoutedEventArgs e)
{
Window w = new Window();
w.Visibility = Visibility.Visible;
w.ShowInTaskbar = false;
w.Show();
}
private void ShowDialogMethod(object sender, RoutedEventArgs e)
{
Window w = new Window();
w.Owner = this; // 소유권 지정. 가장 맨 앞 출력 + 최소화시 같이 최소화
w.SizeToContent = SizeToContent.WidthAndHeight; // 내용물에 맞게 창 크기 조정
w.WindowStartupLocation = WindowStartupLocation.CenterOwner; //창 초기 위치 지정. 소유자 중앙.
w.ShowInTaskbar = false; //작업 표시줄 표시X
w.ShowDialog();
}
}
}