XAML 기초 마스터하기: 구조와 사용법

XAML 기본 원칙

XAML은 직관적인 구조를 따릅니다. 핵심 원칙은 다음과 같습니다.

  • XAML 문서의 각 요소는 .NET 클래스의 인스턴스에 매핑됩니다. 요소 이름은 클래스 이름과 정확히 일치합니다. 예를 들어, <Button> 요소는 Button 객체입니다.
  • XML 문서처럼 요소 내부에 다른 요소를 중첩할 수 있습니다. XAML은 각 클래스가 이 상황을 유연하게 처리하도록 합니다. 일반적으로 중첩은 포함 관계를 나타냅니다. 예를 들어 Grid 요소 안에 Button이 있으면, 사용자 인터페이스에 버튼을 포함하는 그리드가 있습니다.
  • 특성을 통해 각 클래스의 속성을 설정할 수 있습니다. 하지만 속성이 복잡한 경우 특성만으로 처리가 불가능할 수 있습니다. 이때는 특별한 문법을 가진 중첩 태그를 사용합니다.

XAML 네임스페이스

XAML 파서는 클래스 이름 외에도 해당 클래스가 속한 .NET 네임스페이스를 알아야 합니다. 파서는 요소에 적용된 XML 네임스페이스를 확인하여 실제 클래스를 결정합니다.

xmlns 특성은 XML 네임스페이스를 선언하는 데 사용됩니다.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

이 네임스페이스는 WPF 핵심 네임스페이스입니다. 모든 WPF 클래스(UI 컨트롤 포함)를 포함하며, 기본 네임스페이스 역할을 합니다. 접두사가 없는 요소는 자동으로 이 네임스페이스에 속합니다.

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

이 네임스페이스는 XAML 네임스페이스로, 문서 해석에 영향을 주는 다양한 유틸리티 기능을 포함합니다. 접두사 x를 사용하며, 요소 이름 앞에 네임스페이스 접두사를 붙여 사용합니다.

코드 비하인드 클래스

최상위 요소에 Class 특성을 설정하여 XAML 문서를 코드 비하인드 클래스와 연결합니다.

<Window x:Class="WindowsApplication1.Window1">

x 접두사는 Class 특성을 XAML 네임스페이스에 배치합니다. Class 특성은 XAML 파서에게 지정된 이름으로 새 클래스를 생성하도록 지시합니다. 이 클래스는 XML 요소가 지정한 클래스에서 파생됩니다. 즉, 위 예제는 Window 클래스에서 파생된 Window1이라는 새 클래스를 생성합니다.

Window1 클래스는 컴파일 시 자동 생성됩니다. Window1.xaml 파일은 디자이너가 자동 생성한 코드 비하인드 partial 클래스와 프로그래머가 직접 작성한 코드 비하인드 partial 클래스를 연결합니다.

InitializeComponent() 메서드

Window1 클래스의 인스턴스를 생성할 때 기본 생성자는 InitializeComponent() 메서드를 호출합니다. 이 메서드는 컴파일러에 의해 자동 생성됩니다.

요소 이름 지정

코드 비하인드에서 요소를 처리하려면 이름을 지정해야 합니다.

<Grid x:Name="grid1">

파서는 Window1 클래스에 grid1이라는 필드를 추가합니다.

private System.Windows.Controls.Grid grid1;

이제 grid1을 사용하여 Grid와 상호작용할 수 있습니다.

MessageBox.Show(String.Format("The grid is {0}x{1} units in size.",
  grid1.ActualWidth, grid1.ActualHeight));

FrameworkElement에서 파생된 클래스의 경우 Name 특성 앞의 x 접두사는 선택 사항입니다. 접두사를 생략해도 코드 의미는 변하지 않습니다.

XAML 속성과 이벤트

단순 속성과 타입 변환

XAML 요소의 특성 값은 항상 일반 텍스트 문자열이지만, 객체의 속성은 모든 .NET 타입이 될 수 있습니다.

WPF는 타입 변환기를 정의하여 일반 텍스트 문자열과 .NET 타입 간의 연결을 제공합니다.

타입 변환기에 대해 XAML 파서는 다음 순서로 처리합니다.

  1. 파서는 속성 선언에서 TypeConverter 특성을 찾습니다.
  2. 파서는 해당 타입 선언에서 TypeConverter 특성을 찾습니다.

파서가 TypeConverter를 찾지 못하면 오류가 발생합니다.

복합 속성

일반적으로 클래스의 속성은 요소의 특성에 매핑되며, 속성-특성 문법(property-attribute syntax)을 사용합니다. 하지만 속성이 복잡한 경우 속성-요소 문법(property-element syntax)을 사용합니다.

속성 요소 문법을 사용하면 Parent.PropertyName 형식의 이름을 가진 자식 요소를 추가합니다. 예를 들어 GridBrush 객체를 받는 Background 속성이 있습니다. 브러시가 복잡한 경우 다음과 같이 Grid.Background라는 자식 태그를 추가합니다.

<Grid Name="grid1">
  <Grid.Background>
    ...   
  </Grid.Background>
  ...
</Grid>

핵심은 요소 이름의 점(.)입니다. 이것이 속성과 다른 유형의 중첩 콘텐츠를 구분합니다.

속성 요소를 설정하는 방법은 중첩 요소 내부에 특정 클래스를 인스턴스화하는 태그를 추가하는 것입니다. 예를 들어 그라데이션 브러시로 배경을 설정하려면 LinearGradientBrush 객체를 생성해야 합니다.

XAML 규칙에 따라 LinearGradientBrush 이름의 요소를 사용하여 객체를 생성할 수 있습니다.

<Grid Name="grid1">
  <Grid.Background>
    <LinearGradientBrush>
    </LinearGradientBrush>
  </Grid.Background>
  ...
</Grid>

다음으로 그라데이션 색상을 지정해야 합니다. LinearGradientBrush.GradientStops 속성을 GradientStop 객체 컬렉션으로 채웁니다. GradientStops 속성은 특성 값만으로 설정하기에는 너무 복잡하므로 속성 요소 문법을 사용합니다.

<Grid Name="grid1">
  <Grid.Background>
    <LinearGradientBrush>
      <LinearGradientBrush.GradientStops>
      </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
  </Grid.Background>
  ...
</Grid>

마지막으로 GradientStop 객체 목록으로 GradientStops 컬렉션을 채웁니다. 각 GradientStop 객체에는 OffsetColor 속성이 있으며, 일반적인 속성-특성 문법을 사용하여 이 두 값을 제공할 수 있습니다.

<Grid Name="grid1">
  <Grid.Background>
    <LinearGradientBrush>
      <LinearGradientBrush.GradientStops>
        <GradientStop Offset="0.00" Color="Red" />
        <GradientStop Offset="0.50" Color="Indigo" />
        <GradientStop Offset="1.00" Color="Violet" />
      </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
  </Grid.Background>
  ...
</Grid>

모든 XAML 태그 집합은 동일한 작업을 수행하는 코드 문장으로 대체될 수 있습니다. 위의 태그로 그라데이션 배경을 채우는 것은 다음 코드와 동일합니다.

var brush = new LinearGradientBrush();
var stop1 = new GradientStop();
stop1.Offset = 0;
stop1.Color = Colors.Red;
brush.GradientStops.Add(stop1);

var stop2 = new GradientStop();
stop2.Offset = 0.5;
stop2.Color = Colors.Indigo;
brush.GradientStops.Add(stop2);

var stop3 = new GradientStop();
stop3.Offset = 1;
stop3.Color = Colors.Violet;
brush.GradientStops.Add(stop3);

grid1.Background = brush;

마크업 확장

마크업 확장은 중첩 태그 또는 XML 특성에서 사용할 수 있습니다. 특성에서 사용될 때는 항상 중괄호 {}로 묶입니다. 다음은 다른 클래스의 정적 속성을 참조하는 마크업 확장 사용 예입니다.

<Button x:Name="cmdAnswer" Foreground="{x:Static SystemColors.ActiveCaptionBrush}" />

마크업 확장의 문법은 {MarkupExtensionClass Argument}입니다. 이 예제에서 마크업 확장은 StaticExtension 클래스입니다. (관례상 확장 클래스를 참조할 때 마지막 단어 Extension은 생략 가능합니다.)

마크업 확장의 기본 클래스는 System.Windows.Markup.MarkupExtension입니다. 원하는 클래스 인스턴스를 얻기 위한 ProvideValue 메서드 하나만 있습니다. 위 예제에서 XAML 파서는 먼저 StaticExtension 클래스를 생성하고(생성자 인수로 "SystemColors.ActiveCaptionBrush" 문자열 전달), 클래스의 ProvideValue() 메서드를 호출하여 SystemColors.ActiveCaption.Brush 정적 속성이 반환하는 객체를 얻습니다.

위 XAML 블록은 다음 코드와 동일합니다.

cmdAnswer.Foreground = SystemColors.ActiveCaptionBrush;

마크업 확장은 클래스에 매핑되므로 중첩 속성으로도 사용할 수 있습니다. 예를 들어 위 예제의 동등한 표현은 다음과 같습니다.

<Button x:Name="cmdAnswer">
    <Button.Foreground>
        <x:Static Member="SystemColors.ActiveCaptionBrush"></x:Static>
    </Button.Foreground>
</Button>

마크업 확장의 복잡성과 설정하려는 속성의 개수에 따라 이 문법이 더 간단할 수 있습니다.

대부분의 마크업 확장(StaticExtension 등)은 런타임에 평가되어야 합니다. 일부 마크업 확장(NullExtension 포함)은 컴파일 타임에 평가될 수 있습니다.

연결 속성

일반 속성 외에도 XAML은 연결 속성(attached property) 개념을 포함합니다. 이는 여러 컨트롤에 적용할 수 있지만 다른 클래스에 정의된 속성입니다. WPF에서 연결 속성은 레이아웃을 제어하는 데 자주 사용됩니다.

컨트롤을 컨테이너에 배치하면 컨테이너 유형에 따라 추가 특성을 얻습니다. (예를 들어 텍스트 상자를 그리드에 배치하면 그리드 셀을 선택할 수 있어야 합니다.) 이러한 추가 세부 사항은 연결 속성을 사용하여 설정됩니다.

연결 속성은 항상 DefiningType.PropertyName의 두 부분으로 된 이름을 사용합니다. 이 두 부분으로 된 이름 문법은 일반 속성과 연결 속성을 구분합니다.

예를 들어 연결 속성을 사용하면 각 컨트롤이 그리드의 행에 자신을 배치할 수 있습니다.

<TextBox ... Grid.Row="0">
  [Place question here.]
</TextBox>
<Button ... Grid.Row="1">
  Ask the Eight Ball
</Button>
<TextBox ... Grid.Row="2">
  [Answer will appear here.]
</TextBox>

연결 속성은 실제 속성이 아니라 메서드 호출로 변환됩니다. XAML 파서는 DefiningType.SetPropertyName() 형식의 정적 메서드를 호출합니다. 예를 들어 위 XAML 조각에서 정의 타입은 Grid 클래스이고 속성은 Row이므로, 파서는 Grid.SetRow()를 호출합니다.

SetPropertyName()을 호출할 때 파서는 두 개의 인수(수정할 객체와 지정된 속성 값)를 전달합니다. 예를 들어 텍스트 상자 컨트롤의 Grid.Row 속성을 설정하면 XAML 파서는 다음 코드를 실행합니다.

Grid.SetRow(txtQuestion, 0);

이 코드는 행 번호가 Grid 객체에 저장됨을 암시하지만, 실제로는 txtQuestion 텍스트 상자 객체에 저장됩니다.

모든 WPF 컨트롤은 DependencyObject이며, DependencyObject는 종속 속성의 무한 컬렉션을 보유하도록 설계되었습니다.

사실 위 코드는 다음 코드의 축약형입니다.

txtQuestion.SetValue(Grid.RowProperty, 0);

연결 속성은 WPF의 핵심 구성 요소입니다. 다목적 확장성 시스템 역할을 합니다. 예를 들어 Row 속성을 연결 속성으로 정의하면 모든 컨트롤에서 사용할 수 있습니다.

중첩 요소

XAML은 각 요소가 중첩 요소를 처리하는 방식을 결정할 수 있습니다. 이 상호 작용은 다음 순서로 평가되는 세 가지 메커니즘 중 하나를 통해 중재됩니다.

  1. 부모 요소가 IList를 구현하면 파서는 IList.Add(자식_요소)를 호출합니다.
  2. 부모 요소가 IDictionary를 구현하면 파서는 IDictionary.Add(자식_요소)를 호출합니다. 사전 컬렉션을 사용할 때는 각 항목의 x:Key 특성에 키를 설정해야 합니다.
  3. 부모 요소가 ContentProperty 특성으로 데코레이팅되면 파서는 자식 요소를 사용하여 해당 속성을 설정합니다.

예를 들어 LinearGradientBrushGradientStop 객체의 컬렉션을 보유할 수 있습니다.

<LinearGradientBrush>
  <LinearGradientBrush.GradientStops>
    <GradientStop Offset="0.00" Color="Red" />
    <GradientStop Offset="0.50" Color="Indigo" />
    <GradientStop Offset="1.00" Color="Violet" />
  </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

XAML 파서는 LinearGradientBrush.GradientStops 요소가 점을 포함하므로 복합 속성임을 인식합니다. 하지만 파서는 태그 내부(세 개의 GradientStop 요소)를 다르게 처리합니다. 이 경우 GradientStops 속성이 GradientStopCollection 객체를 반환하고, GradientStopCollectionIList 인터페이스를 구현한다는 것을 압니다. 따라서 각 GradientStopIList.Add() 메서드를 사용하여 컬렉션에 추가되어야 한다고 가정합니다.

GradientStop stop1 = new GradientStop();
stop1.Offset = 0;
stop1.Color = Colors.Red;
IList list = brush.GradientStops;
list.Add(stop1);

일부 속성은 둘 이상의 컬렉션 타입을 지원할 수 있습니다. 이 경우 다음과 같이 컬렉션 클래스를 지정하는 태그를 추가해야 합니다.

<LinearGradientBrush>
  <LinearGradientBrush.GradientStops>
    <GradientStopCollection>
      <GradientStop Offset="0.00" Color="Red" />
      <GradientStop Offset="0.50" Color="Indigo" />
      <GradientStop Offset="1.00" Color="Violet" />
    </GradientStopCollection>
  </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

참고: 컬렉션이 기본적으로 비어 있으면 컬렉션 클래스를 지정하는 태그를 포함하여 컬렉션 객체를 생성해야 합니다. 기본 인스턴스가 존재하는 경우 채우기만 하면 되므로 해당 부분을 생략할 수 있습니다.

중첩 콘텐츠가 항상 컬렉션을 의미하는 것은 아닙니다. 예를 들어 여러 컨트롤을 포함하는 Grid 요소를 고려해 보세요.

<Grid Name="grid1">
  ...
  <TextBox Name="txtQuestion" ... >
    ...
  </TextBox>
  <Button Name="cmdAnswer" ... >
    ...  
  </Button>
  <TextBox Name="txtAnswer" ... >
    ...
  </TextBox>
</Grid>

이 중첩 태그는 점을 포함하지 않으므로 복합 속성이 아닙니다. 또한 Grid 컨트롤은 IListIDictionary를 구현하지 않으므로 컬렉션이 아닙니다. Grid가 실제로 지원하는 것은 ContentProperty 특성으로, 중첩 콘텐츠를 받을 수 있는 속성을 지정합니다. 기술적으로 ContentProperty 특성은 Grid의 기본 클래스인 Panel 클래스에 다음과 같이 적용됩니다.

[ContentPropertyAttribute("Children")] 
public abstract class Panel

이는 모든 중첩 요소가 Children 속성을 설정하는 데 사용되어야 함을 의미합니다. 콘텐츠 속성이 컬렉션 속성인지(IList 또는 IDictionary 인터페이스 구현)에 따라 XAML 파서의 처리 방식이 다릅니다. Panel.Children 속성은 UIElementCollection을 반환하고 UIElementCollectionIList를 구현하므로, 파서는 IList.Add() 메서드를 사용하여 중첩 콘텐츠를 그리드에 추가합니다.

즉, XAML 파서가 이전 마크업을 만나면 각 중첩 요소의 인스턴스를 생성하고 Grid.Children.Add() 메서드를 사용하여 그리드에 전달합니다.

txtQuestion = new TextBox();
...
grid1.Children.Add(txtQuestion);
cmdAnswer = new Button();
...
grid1.Children.Add(cmdAnswer);
txtAnswer = new TextBox();
...
grid1.Children.Add(txtAnswer);

다음에 발생하는 일은 컨트롤이 콘텐츠 속성을 구현하는 방식에 따라 완전히 달라집니다. Grid는 보이지 않는 행과 열로 구성된 레이아웃에 보유한 모든 컨트롤을 표시합니다.

ContentProperty 특성은 WPF에서 자주 사용됩니다. 컨테이너 컨트롤(Grid 등), 시각적 항목 컬렉션을 포함하는 컨트롤(ListBox, TreeView 등), 단일 콘텐츠를 포함하는 컨트롤(TextBox, Button 등)에 사용됩니다. 텍스트 상자와 버튼 컨트롤은 단일 요소나 텍스트를 보유할 수 있으며, 다음과 같이 콘텐츠 속성을 사용하여 중첩 콘텐츠를 처리합니다.

<TextBox Name="txtQuestion" ... >
  [Place question here.]
</TextBox>
<Button Name="cmdAnswer" ... >
  Ask the Eight Ball  
</Button>
<TextBox Name="txtAnswer" ... >
  [Answer will appear here.]
</TextBox>

TextBox 클래스는 ContentPropertyAttribute를 사용하여 TextBox.Text 속성을 표시합니다. Button 클래스는 ContentPropertyAttribute를 사용하여 Button.Content 속성을 표시합니다. XAML 파서는 제공된 텍스트를 사용하여 이러한 속성을 설정합니다.

TextBox.Text 속성은 문자열만 허용합니다. 그러나 Button.Content 속성은 모든 요소를 허용합니다.

TextContent 속성은 컬렉션을 사용하지 않으므로 둘 이상의 콘텐츠를 포함할 수 없습니다. 예를 들어 버튼 내부에 여러 요소를 중첩하려고 하면 XAML 파서가 예외를 throw합니다. 텍스트가 아닌 콘텐츠(예: 사각형)를 제공하면 파서도 예외를 throw합니다.

참고: ContentControl은 단일 중첩 요소를 허용하고, ItemsControl은 항목 컬렉션을 허용하며, Panel은 컨트롤 그룹을 배치하는 컨테이너입니다. ContentControl, ItemsControl, Panel 기본 클래스는 모두 ContentPropertyAttribute를 사용합니다. 해당 ContentProperty 속성은 각각 Panel.Children, TextBox.Text, Button.Content입니다.

특수 문자와 공백

XAML에서 특수 문자와 공백을 처리하는 방법은 38페이지를 참조하세요.

이벤트

특성은 속성에 매핑될 뿐만 아니라 이벤트 핸들러를 연결하는 데도 사용될 수 있습니다. 문법은 EventName="EventHandlerMethodName"입니다.

예를 들어 버튼에 클릭 이벤트 핸들러를 연결합니다.

<Button ... Click="cmdAnswer_Click">

대부분의 경우 동일한 요소에서 특성을 사용하여 속성을 설정하고 이벤트 핸들러를 연결합니다. WPF는 항상 동일한 순서를 따릅니다. 먼저 Name 속성을 설정하고, 그다음 모든 이벤트 핸들러를 연결하고, 마지막으로 속성을 설정합니다. 즉, 속성이 처음 설정될 때 해당 이벤트 핸들러가 트리거됩니다.

다른 네임스페이스 사용

WPF 네임스페이스에 정의되지 않은 클래스를 사용하려면 .NET 네임스페이스를 XML 네임스페이스에 매핑해야 합니다.

xmlns:Prefix="clr-namespace:Namespace;assembly=AssemblyName"

세 가지 기울임꼴 정보는 다음과 같습니다.

  • Prefix: XAML 마크업에서 네임스페이스를 참조하는 데 사용할 XML 접두사입니다. 예를 들어 XAML 언어는 x 접두사를 사용합니다.
  • Namespace: 정규화된 .NET 네임스페이스 이름입니다.
  • AssemblyName: 타입이 선언된 어셈블리로, .dll 확장자는 포함하지 않습니다. 프로젝트에서 이 어셈블리를 참조해야 합니다. 프로젝트 어셈블리를 사용하려면 비워 둡니다.

예를 들어 System 네임스페이스의 기본 타입에 접근하여 sys 접두사에 매핑하는 방법입니다.

xmlns:sys="clr-namespace:System;assembly=mscorlib"

현재 프로젝트의 MyProject 네임스페이스에 선언된 타입에 접근하여 local 접두사에 매핑하는 방법입니다.

xmlns:local="clr-namespace:MyNamespace"

특정 네임스페이스의 클래스 인스턴스를 생성하려면 네임스페이스 접두사를 사용합니다.

<local:MyObject ...></local:MyObject>

XAML에서 사용되는 클래스는 매개변수 없는 생성자가 있어야 합니다. 또한 클래스의 공개 속성만 사용할 수 있습니다. XAML은 공개 필드 설정이나 메서드 호출을 허용하지 않습니다.

원시 타입(문자열, 날짜, 숫자 타입 등)의 인스턴스를 생성하려면 태그 콘텐츠로 데이터의 문자열 표현을 제공할 수 있습니다. XAML 파서는 타입 변환기를 사용하여 문자열을 적절한 객체로 변환합니다.

<sys:DateTime>10/30/2013 4:30 PM</sys:DateTime>

DateTime 클래스는 TypeConverterAttribute를 사용하여 DateTimeConverter에 연결됩니다. DateTimeConverter는 이 문자열을 유효한 DateTime 객체로 인식하고 변환합니다. 이 기술을 사용할 때는 특성을 사용하여 객체의 속성을 설정할 수 없습니다.

XAML 로드 및 컴파일

코드 전용

순수 코드 WPF는 데이터베이스 레코드로 창을 채우거나 창 컨트롤을 동적으로 추가/교체하는 데 자주 사용됩니다.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

public class Window1 : Window
{
    private Button button1;

    public Window1()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        // 폼 구성
        this.Width = this.Height = 285;
        this.Left = this.Top = 100;
        this.Title = "Code-Only Window";

        // 버튼을 담을 컨테이너 생성
        var panel = new DockPanel();

        // 버튼 생성
        button1 = new Button();
        button1.Content = "Please click me.";
        button1.Margin = new Thickness(30);

        // 이벤트 핸들러 연결
        button1.Click += button1_Click;

        // 패널에 버튼 배치
        IAddChild container = panel;
        container.AddChild(button1);
        // 대안
        panel.Children.Add(button1);

        // 폼에 패널 배치
        container = this;
        container.AddChild(panel);
        // 대안
        this.AddChild(panel);
        // 대안
        this.Content = panel;
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        button1.Content = "Thank you.";
    }
}

창을 시작하는 코드:

public class Program : Application
{
    [STAThread()]
    static void Main()
    {
        Program app = new Program();
        app.MainWindow = new Window1();
        app.MainWindow.ShowDialog();
    }
}

코드와 미컴파일 XAML

"TextFile1.txt"와 같은 임의의 텍스트 파일:

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Button Name="button1" Margin="30">Please click me.</Button>
</DockPanel>

창을 생성하는 코드:

using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

public class Window2 : Window
{
    private Button button1;

    public Window2(string xamlFile)
    {
        // 폼 구성
        this.Width = this.Height = 285;
        this.Left = this.Top = 100;
        this.Title = "Dynamically Loaded XAML";

        // 외부 파일에서 XAML 콘텐츠 가져오기
        DependencyObject rootElement;
        using (FileStream fs = new FileStream(xamlFile, FileMode.Open))
        {
            rootElement = (DependencyObject)XamlReader.Load(fs);
        }

        // 마크업을 이 창에 삽입
        this.Content = rootElement;

        // 적절한 이름의 컨트롤 찾기
        button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1");

        // 대안
        var frameworkElement = (FrameworkElement)rootElement;
        button1 = (Button)frameworkElement.FindName("button1");

        // 이벤트 핸들러 연결
        button1.Click += button1_Click;
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        button1.Content = "Thank you.";
    }
}

창 시작:

public class Program : Application
{
    [STAThread()]
    static void Main()
    {
        Program app = new Program();
        System.Environment.CurrentDirectory = @"D:\Application Data\Visual Studio\Projects\WpfApplication2";
        app.MainWindow = new Window2("TextFile1.txt");
        app.MainWindow.ShowDialog();
    }
}

코드와 컴파일된 XAML

WPF 애플리케이션을 컴파일할 때 Visual Studio는 2단계 컴파일 프로세스를 사용합니다. 첫 번째 단계는 XAML 파일을 BAML로 컴파일하는 것입니다. 예를 들어 프로젝트에 Window1.xaml 파일이 있으면 컴파일러는 Window1.baml 임시 파일을 생성하고 obj\Debug 하위 폴더에 배치합니다. 동시에 선택한 언어로 창에 대한 partial 클래스가 생성됩니다. 예를 들어 C#을 사용하는 경우 컴파일러는 obj\Debug 폴더에 Window1.g.cs 파일을 생성합니다(g는 generated를 의미).

public partial class Window1 : System.Windows.Window,
  System.Windows.Markup.IComponentConnector
{
    // 컨트롤 필드
    internal System.Windows.Controls.TextBox txtQuestion;
    internal System.Windows.Controls.Button cmdAnswer;
    internal System.Windows.Controls.TextBox txtAnswer;

    private bool _contentLoaded;

    // BAML 로드
    public void InitializeComponent()
    {
        if (_contentLoaded)
        {
            return;
        }
        _contentLoaded = true;
        System.Uri resourceLocater = new System.Uri("window1.baml",
          System.UriKind.RelativeOrAbsolute);
        System.Windows.Application.LoadComponent(this, resourceLocater);
    }

    // 각 컨트롤 연결
    void System.Windows.Markup.IComponentConnector.Connect(int connectionId,
      object target)
    {
        switch (connectionId)
        {
            case 1:
                txtQuestion = ((System.Windows.Controls.TextBox)(target));
                return;

            case 2:
                cmdAnswer = ((System.Windows.Controls.Button)(target));
                cmdAnswer.Click += new System.Windows.RoutedEventHandler(
                  cmdAnswer_Click);
                return;

            case 3:
                txtAnswer = ((System.Windows.Controls.TextBox)(target));
                return;
        }
        this._contentLoaded = true;
    }
}

XAML 전용

코드를 전혀 생성하지 않고 XAML 파일만 사용하는 것을 느슨한 XAML(loose XAML) 파일이라고 합니다. 느슨한 XAML 파일은 IE 브라우저에서 직접 열 수 있습니다.

느슨한 XAML 페이지를 시도하려면 일반 XAML 파일을 다음과 같이 변경합니다.

  • 루트 요소의 Class 특성을 제거합니다.
  • 이벤트 핸들러에 바인딩된 모든 특성을 제거합니다.
  • Window 요소를 Page 요소로 변경합니다.

태그: XAML WPF .NET XML 마크업 확장

7월 15일 02:37에 게시됨