= 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(); } } }