WPF(Windows Presentation Foundation)
μλμ° κΈ°λ° μμ©νλ‘κ·Έλ¨μμ μ¬μ©μ μΈν°νμ΄μ€λ₯Ό νμνκΈ° μν λͺ©μ μΌλ‘ λ§μ΄ν¬λ‘μννΈμμ λ§λ κ·Έλν½ μλΈμμ€ν
WPF μν€ν μ²
WPFμμμ λͺ¨λ λμ€νλ μ΄λ DirectXμμ§μ ν΅ν΄ μνλλ―λ‘ ν¨μ¨μ μΈ νλμ¨μ΄ λ° μννΈμ¨μ΄ λ λλ§μ νμ©
System.Threading.DispatcherObject
λμ€ν¨μ²μ μν΄ κ΅¬νλ λ©μμ§ μμ€ν
μ κΈ°λ°
MVVM
model - view - viewmodel
XAML
XAMLμ μ ν리μΌμ΄μ
μ λͺ¨μμ ꡬννλ XML κΈ°λ° νκ·Έ μΈμ΄
- μ°½,λν.μμ, νμ΄μ§ λ° μ¬μ©μ μ μ 컨νΈλ‘€μ μ μ
- 컨νΈλ‘€, λν λ° κ·Έλν½μΌλ‘ μ±μ°λλ° μ¬μ©
μ½λ μ¨κΉ
μ ν리μΌμ΄μ
μ μ£Όμ λμμ μ¬μ©μ μνΈ μμ©μ μλ΅νλ κΈ°λ₯
μ ꡬννκ·Έμ μ°κ²°λ μ½λμμ ꡬννλ μ’
λ₯μ μ½λλ₯Ό μ½λ μ¨κΉμ΄λΌκ³ ν¨
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SDKSample.AWindow" Title="Window with button" Width="250" Height="100"> <!-- Add button to window --> <Button Name="button" Click="button_Click">Click Me!</Button> </Window> /////////////////////////// using System.Windows; namespace SDKSample { public partial class AWindow : Window { public AWindow() { // InitializeComponent call is required to merge the UI // that is defined in markup with this class, including // setting properties and registering event handlers InitializeComponent(); } void button_Click(object sender, RoutedEventArgs e) { // Show message box when button is clicked. MessageBox.Show("Hello, Windows Presentation Foundation!"); } } }
xmlns:x
: λ€μμ€νμ΄μ€λ₯Ό μ μνκ³ μ½λ μ¨κΉ νμμ λν μ§μμ μΆκ°νλ μ€ν€λ§μ 맀ν
x:Class
: μ½λ μ¨κΉ ν΄λμ€λ₯Ό νΉμ XAML νκ·Έμ μ°κ²°νλλ° μ¬μ©
<window>
: μ½λ μ¨κΉν΄λμ€λ <window>ν΄λμ€μμ μμ
InitializeComponent
: μ½λ μ¨κΉ ν΄λμ€λ₯Ό μ¬μ©νμ¬ νκ·Έμμ μ μλ UIλ₯Ό λ³ν©νκΈ° μν΄ μ½λ μ¨κΉ ν΄λμ€μ μμ±μμμ νΈμΆ
μ λ ₯ λ° λͺ λ Ή
컨νΈλ‘€μ λμ²΄λ‘ μ¬μ©μ μ
λ ₯μ κ°μ§νκ³ μλ΅ν©λλ€.
WPF μ
λ ₯ μμ€ν
μ μ§μ λ° λΌμ°νΈλ μ΄λ²€νΈλ₯Ό μ¬μ©νμ¬ ν
μ€νΈμ
λ ₯, ν¬μ»€μ€ κ΄λ¦¬ λ° λ§μ°μ€ μμΉ μ§μ μ μ§μ
λ μ΄μμ
μ¬μ©μ μΈν°νμ΄μ€λ₯Ό λ§λ€ λ μμΉ λ° ν¬κΈ°λ³λ‘ 컨νΈλ‘€μ μ λ ¬νμ¬ λ μ΄μμμ ꡬμ±ν©λλ€.
λ°μΈλ©
λ κ° μ΄μμ κ°μ²΄ κ° μμ±μ μ°κ²°νλ©΄ ν κ°μ²΄μ μμ±μ΄ λ³κ²½λμμ λ λ°μΈλ© λ λ€λ₯Έ κ°μ²΄μ μμ± λν λ³κ²½λλλ‘ νλ μμ
λ°μ΄ν° λ°μΈλ©
μ± UIμ ν΄λΉ UIκ° νμνλ λ°μ΄ν°λ₯Ό μ°κ²°νλ νλ‘μΈμ€
- μ μ μΈν°νμ΄μ€μ λΉμ€λμ€ λͺ¨λΈ μ¬μ΄μ μλμΌλ‘ λ°μ΄ν°λ₯Ό μ λ°μ΄νΈ ν΄μ£Όλ λ°©λ²
λ°μ΄ν°μ μ‘μΈμ€νκ³ μ ν리μΌμ΄μ
μ κ΄λ¦¬λλ κ°μ²΄μ λ‘λν ν WPF μ ν리μΌμ΄μ
μμ μμ
- κ΄λ¦¬λλ κ°μ²΄μμ λ°μ΄ν°λ₯Ό νμ λ° νΈμ§ν μ μλ 컨νΈλ‘€λ‘ λ°μ΄ν° 볡μ¬
- 컨νΈλ‘€μ μ¬μ©ν λ°μ΄ν° λ³κ²½ λ΄μ©μ΄ κ΄λ¦¬λλ κ°μ²΄μ λ€μ 볡μ¬λλμ§ νμΈ
Binding ν΄λμ€λ‘ 컨νΈλ‘€(λ°μΈλ© λμ)μ λ°μ΄ν° κ°μ²΄(λ°μΈλ© μμ€)μ λ°μΈλ©νλ μμ
μ μν
- ꡬμ±μμ
- λ°μΈλ© λμ κ°μ²΄
- λμ μμ±
- λ°μΈλ© μμ€
- μ¬μ©ν λ°μΈλ© μμ€μ μλ κ° κ²½λ‘
λ°μ΄ν° νλ¦ λ°©ν₯
- OneWay λ°μΈλ©μ μ¬μ©νλ©΄ μμ€ μμ±μ΄ λ³κ²½λ κ²½μ° λμ μμ±μ΄ μλμΌλ‘ μ λ°μ΄νΈ λμ§λ§ λμ μμ±μ΄ λ³κ²½λ κ²½μ° λ³κ²½ λ΄μ©μ΄ λ€μ μμ€ μμ±μΌλ‘ μ ν X
- TwoWay λ°μΈλ©μΌλ‘ μΈν΄ μμ€ μμ± λλ λμ μμ±μ΄ λ³κ²½λμ΄ λ€λ₯Έ νλͺ©μ΄ μλμΌλ‘ μ λ°μ΄νΈ
- OneWayToSourceλ OneWay λ°μΈλ©μ λ°λλ‘ λμ μμ±μ΄ λ³κ²½λλ©΄ μμ€ μμ±μ μ λ°μ΄νΈ
μμ€ μ λ°μ΄νΈλ₯Ό νΈλ¦¬κ±°νλ νλͺ©
TwoWay λλ OneWayToSourceμΈ λ°μΈλ©μ λμ μμ±μ λ³κ²½ λ΄μ©μ μμ νκ³ λ€μ μμ€μ μ νν©λλ€.
TemplateBinding
TemplateBindingμ ν
νλ¦Ώμ μΌλΆλ₯Ό 컨νΈλ‘€μ μμ±μ λ°μΈλ©νλλ° μ μ©
XAML(μλ©)μμ λ°μΈλ© μ μΈ
λ°μΈλ© νμ₯μ μ¬μ©νμ¬ λ°μΈλ©μ μ μΈν λ μ μΈμ
Binding
ν€μλ λ€μ μΌλ ¨μ μ μ΄ μΌνλ‘ κ΅¬λΆλ ννλ‘ κ΅¬μ±ν νλ¦Ώ
μκ°μ μΌλ‘ 맀λ ₯μ μΈ ν¨κ³Όμ μΌκ΄λ λͺ¨μμ λ§λ€ μ μλ κΈ°λ₯ μ§ν©
WPF 컨νΈλ‘€μ κΈ°λ³Έ μ¬μ©μ μΈν°νμ΄μ€λ μΌλ°μ μΌλ‘ λ€λ₯Έ 컨νΈλ‘€ λ° λνμμ ꡬμ±
ContorlTemplates
컨νΈλ‘€μ μ¬λ¬ μΈμ€ν΄μ€μμ 곡μ ν μ μλ Control μκ°μ ꡬ쑰 λ° λμ μΈ‘λ³μ μ§μ
- λ μ΄μμ, ν λ리, λ°°κ²½ λ° κΈ°ν μκ°μ μμμ κ°μ 컨νΈλ‘€μ ꡬ쑰 λ° μκ°μ λͺ¨μμ μ μ
- Button, CheckBox
DataTemplates
λ°μ΄ν°μ μκ°μ νμλ₯Ό μ μ
- λͺ©λ‘μ΄λ 그리λμ νλͺ© λͺ¨μμ νμνλ κ²κ³Ό κ°μ΄ νΉμ νμμΌλ‘ λ°μ΄ν°λ₯Ό νμνλλ° μ¬μ©
- ListBox, ListView, ItemsControl
ItemsPanelTemplate
ItemControlμ νλͺ©μ ν¬ν¨νλ ν¨λμ μ μνλλ° μ¬μ©
- κ°λ‘ or μΈλ‘ μ€ν ν¨λ, 그리λ or 컨λ²μ€μ κ°μ νλͺ©μ λ μ΄μμμ μ μ
TemplateBinding
ν
νλ¦Ώ μλ리μ€μ λν λ°μΈλ©μ μ΅μ νλ νν
κΈ°λ³Έμ μΈ ν
<Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <Grid> <ItemsControl Grid.Row="1" Margin="5" ItemsSource="{Binding DigitElements}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid IsItemsHost="True"> <Grid.RowDefinitions> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> </Grid> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Button Grid.Row="{Binding Row}" Grid.Column="{Binding Column}" Margin="3" Grid.ColumnSpan="{Binding ColumnSpan}" Grid.RowSpan="{Binding RowSpan}" Content="{Binding Text}" Command="{Binding Command}" CommandParameter="{Binding DataContext.Calculation, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" /> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Grid.Row" Value="{Binding Row}" /> <Setter Property="Grid.Column" Value="{Binding Column}" /> <Setter Property="Grid.ColumnSpan" Value="{Binding ColumnSpan}" /> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl> </Grid>
λΌμ°νΈλ μ΄λ²€νΈ
κΈ°λ₯μ κ΄μ
μμ λΌμ°νΈλ μ΄λ²€νΈλ μ΄λ²€νΈ μλ³ΈλΏλ§ μλλΌ μμ νΈλ¦¬μ μ¬λ¬ μμ κΈ°μμ μ²λ¦¬κΈ°λ₯Ό νΈμΆν μ μλ μ΄λ²€νΈ μ ν
ꡬν κ΄μ
μμ λΌμ°νΈλ μ΄λ²€νΈλ WPF μ΄λ²€νΈ μμ€ν μ λ±λ‘λκ³ RoutedEvent ν΄λμ€μ μΈμ€ν΄μ€λ‘ λ·λ°μΉ¨λκ³ WPF μ΄λ²€νΈ μμ€ν μμ μΉλ¦¬λλ μ΄λ²€νΈ
WPF μ ν리μΌμ΄μ
μλ μΌλ°μ μΌλ‘ XAMLμμ μ μΈλκ±°λ μ½λμμ μΈμ€ν΄μ€νλ λ§μ μμκ° ν¬ν¨
μ΄λ²€νΈκ° μλ³Έ μμμμ λ°μνλ κ²½μ°
- μλ³Έ μμμμ λ£¨νΈ μμ(μΌλ°μ μΌλ‘ νμ΄μ§ λλ μ°½)λ‘ μμ νΈλ¦¬λ₯Ό ν΅ν΄ λ²λΈ μ λ¨
- μμ νΈλ¦¬λ₯Ό ν΅ν΄ λ£¨νΈ μμμμ μλ³Έ μμλ‘ ν°λλ§ λ¨
- μμ νΈλ¦¬λ₯Ό ν΅κ³Όνμ§ μκ³ μλ³Έ μμμμλ§ λ°μν¨
λΌμ°νΈ μ λ΅
λ²λΈλ§
: μ΄λ²€νΈ μμ€μμ μ΄λ²€νΈ μ²λ¦¬κΈ°κ° νΈμΆλ¨ β λΌμ°νΈλ μ΄λ²€νΈλ μ°μ λΆλͺ¨ μμλ‘ λΌμ°ν λμ΄ μμ νΈλ¦¬ 루νΈμ λλ¬ν λκΉμ§ ν΄λΉ μ΄λ²€νΈ μ²λ¦¬κΈ°λ₯Ό μ°¨λ‘λ‘ νΈμΆ
ν°λλ§
: μ²μμ μμ νΈλ¦¬ 루νΈμ μλ μ΄λ²€νΈ μ²λ¦¬κΈ° νΈμΆ β λΌμ°νΈλ μ΄λ²€νΈλ μ°μλ μμ μμλ‘ λΌμ°ν λμ΄ μ΄λ²€νΈ μλ³Έμ λλ¬ν λκΉμ§ ν΄λΉ μ΄λ²€νΈ μ²λ¦¬κΈ°λ₯Ό μ°¨λ‘λ‘ νΈμΆ
μ΄λ²€νΈ μ²λ¦¬κΈ° μ°κ²° λ° κ΅¬ν
<StackPanel Name="StackPanel1" Button.Click="Button_Click"> <Button>Click me</Button> </StackPanel>
private void Button_Click(object sender, RoutedEventArgs e) { // Click event logic. }
RoutedEventHandler
λ리μμ sender
맀κ°λ³μλ μ΄λ²€νΈ μ²λ¦¬κΈ°κ° μ°κ²°λ μμλ₯Ό μ§μ RoutedEventHandler
μ κΈ°λ³Έ λΌμ°νΈλ μ΄λ²€νΈ μ²λ¦¬κΈ° μ΄μ§λ§ μΌλΆ 컨νΈλ‘€ λλ ꡬν μλ리μ€μλ νΉμνλ μ΄λ²€νΈ λ°μ΄ν°λ₯Ό μ§μνλ λ€λ₯Έ λ리μκ° νμex) DragEnter β DragEventHandler
μ½λλ₯Ό μ¬μ©νμ¬ λΌμ°νΈλ μ΄λ²€νΈμ λν μ΄λ²€νΈ μ²λ¦¬κΈ°λ₯Ό μμμ μ°κ²°ν λ
AddHandler
λ©μλλ₯Ό μ§μ νΈμΆButton1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
- λΌμ°νΈλ μ΄λ²€νΈκ° CLR μ΄λ²€νΈ λνΌλ₯Ό ꡬννλ κ²½μ°
Button1.Click += Button_Click;
Handled κ°λ
λΌμ°νΈλ λͺ¨λ μ΄λ²€νΈλ RoutedEventArgs ν΄λμ€μΈ μ΄λ²€νΈ λ°μ΄ν°μ λν κ³΅ν΅ κΈ°λ³Έ ν΄λμ€λ₯Ό 곡μ
RoutedEventArgs
ν΄λμ€λ λΆμΈHandled
μμ±μ μ μ
Handled
μμ±μ λͺ©μ μ μ΄λ²€νΈ κ²½λ‘λ₯Ό λ°λΌ μλ μ΄λ²€νΈ μ²λ¦¬κΈ°κ° λΌμ°νΈ λ μ΄λ²€νΈλ₯Ό μ²λ¦¬λ¨μ νμν μ μκ² νλ κ²
DataContext
νλμ κ³΅μ© μμ€μμ μ¬λ¬ κ°μ μμ±μ λ°μΈλ©νλ κ²½μ°
<Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext>
ItemsControl ν΄λμ€
νλͺ©μ 컬λ μ
μ νμνλ λ° μ¬μ©ν μ μλ 컨νΈλ‘€
<ListBox ItemsSource="{Binding Source={StaticResource dataList}}"/> // ItemSource => ItemsControlμ μ½ν μΈ λ₯Ό μμ±νλλ° μ¬μ©λλ 컬λ μ μ κ°μ Έμ€κ±°λ μ€μ
ItemsControl.ItemContainerStyle
κ° νλͺ©μ λν΄ μμ±λ 컨ν
μ΄λ μμμ μ μ©λ
Style
μ κ°μ Έμ€κΈ°λ μ€μ <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Grid.Row" Value={Binding Row} /> <Setter Property="Grid.Column" Value={Binding Column} /> </Style> </ItemsControl.ItemContainerStyle>
INotifyPropertyChanged μΈν°νμ΄μ€
μμ±κ°μ΄ λ³κ²½λμμμ ν΄λΌμ΄μΈνΈμ μλ¦Ό
- λ°μΈλ©μ λ€μ μ€μ ν νμμμ΄ λ°μΈλ©λ DataGridView 컨νΈλ‘€μ΄ λ°μ΄ν° μλ³Έμ λ³κ²½ μ¬νμ λ°μ
CallerMemberName
νΉμ±μ μ¬μ©νλ κ²½μ°NotifyPropertyChanged
λ©μλμ λν νΈμΆμ μμ± μ΄λ¦μ λ¬Έμμ΄ μΈμλ‘ μ§μ ν νμκ° μλ€.
public void RaisePropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
Setter
μμ± κ°μ μ μ©νλ ν΄λμ€
<Style TargetType="{x:Type TextBlock}"> <Setter Property="FontFamily" Value="Hong Gil Dong" /> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="FonSize" Value="12pt" /> </Style>
XAML 리μμ€
리μμ€λ μ±μ μ¬λ¬ μμΉμμ λ€μ μ¬μ©ν μ μλ κ°μ²΄
- 리μμ€ λλ ν°λ¦¬μ κ° λ¦¬μμ€μλ κ³ μ ν€κ° μμ΄μΌν¨ (ex :
x:Key
)
<Button Backgroud="{StaticResource MyBrush}" /> <Ellipse Fill="{StaticResource MyBrush}" />
μ μ λ° λμ 리μμ€
μ μ 리μμ€
- μ¬μ© κ°λ₯ν λͺ¨λ 리μμ€ μ¬μ μμ ν΄λΉ ν€μ κ°μ κ²μνμ¬ ν€λ₯Ό μ²λ¦¬
- μ²λ¦¬λ λ‘λνλ λμ μ΄λ£¨μ΄μ§
- μ± λμμΈμμλ λλΆλΆμ 리μμ€λ₯Ό νμ΄μ§ λλ μ ν리μΌμ΄μ μμ€ λ¦¬μμ€ μ¬μ μ μ§μ€
- νμ΄μ§ λ€μ λ‘λ λ±μ λ°νμ λμμ λ°λΌ λ€μ νκ°λμ§ μμ
μ μ 리μμ€ μ‘°ν λμ
- μ‘°ν νλ‘μΈμ€κ° μμ±μ μ€μ νλ μμλ₯Ό ν΅ν΄ μ μλ 리μμ€ μ¬μ μμ μμ²λ ν€λ₯Ό νμΈ
- μ‘°ν νλ‘μΈμ€κ° λ Όλ¦¬μ νΈλ¦¬λ₯Ό μν₯μμΌλ‘ ν΅κ³Όνμ¬ λΆλͺ¨ μμμ ν΄λΉ 리μμ€ μ¬μ μΌλ‘ μ΄λ (루νΈμ λλ¬ν λκΉμ§)
- μ± λ¦¬μμ€λ₯Ό νμΈ
λμ 리μμ€
- μμ€ν 리μμ€ λλ μ¬μ©μκ° μ€μ ν μ μλ 리μμ€λ₯Ό ν¬ν¨νμ¬ λ¦¬μμ€μ κ°μ λ°νμκΉμ§ μλ €μ§μ§ μμ 쑰건μ λ°λΌ λ¬λΌμ§
- λ°νμ μ λ³κ²½λ μ¬μ§κ° μλ 리μμ€
Binding.RelativeSource
λ°μΈλ© λμ μμΉλ₯Ό κΈ°μ€μΌλ‘ ν΄λΉ μμΉλ₯Ό μ§μ νμ¬ λ°μΈλ© μμ€λ₯Ό κ°μ Έμ€κ±°λ μ€μ ν©λλ€.
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="true"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)/ErrorContent}"/> </Trigger> </Style.Triggers> </Style>
κΈ°λ³Έμ μΌλ‘ λ°μΈλ©μ λ°μ΄ν° 컨ν
μ€νΈλ₯Ό μμν©λλ€.
WPF Trigger
- Triggerλ μ΄λ€ 쑰건, μ΄λ²€νΈλ± μ£Όμ΄μ‘μ λ 묡μμ μΌλ‘ 컨νΈλ‘€μ μν λλ μ΄λ²€νΈ νΈλ€λ¬ λ±μ νΈμΆνλ κΈ°λ₯
- Triggerλ₯Ό μ¬μ©νλ©΄ μ리먼νΈμ νλ‘νΌν°λ λ°μ΄ν° λ°μΈλ©, μ΄λ²€νΈμμ λ°μνλ λ³νμ μ리먼νΈμ 컨νΈλ‘€μ΄ μ΄λ»κ² λ°μν μ§λ₯Ό μ ν μ μλ€.