6주차 강 대 기
컨트롟
◦ 지원되지 않는 실버라이트 컨트롟
◦ 레이아웃 관렦 컨트롟
◦ 기타 기본 컨트롟
컨트롟
◦ 지원되지 않는 실버라이트 컨트롟
◦ 레이아웃 관렦 컨트롟
StackPanel 컨트롟, Grid 컨트롟, Canvas 컨트롟, Panel 컨 트롟, ScrollViewer 컨트롟
◦ 기타 기본 컨트롟
TextBox/PasswordBox 컨트롟, Button/ToggleButton (PushButton) 컨트롟, HyperlinkButton 컨트롟,
CheckBox/RadioButton 컨트롟, TextBlock 컨트롟,
ProgressBar/Slider 컨트롟, Border 컨트롟, ListBox 컨트롟, Image 컨트롟
윈도우폰이 실버라이트를 기반으로 하고 있으나, 데스크탑에서 지원되는 모든 컨트롟을 윈도우폰에 서 동일하게 사용할 수는 없음 ◦ 예를 들어 Calendar 같은 컨트롟들은 아예 윈도우폰에서 사용할 수 없음 ◦ 윈도우폰의 디자인 컨셉트와 다소 일관성이 떨어지는 ComboBox와 같은 컨트롟들은 도구 상자 (ToolBox)에 는 숨겨져 있음 이번 강의에서는 윈도우폰에서 사용할 수 있는 실 버라이트 컨트롟에 대해 알아보겠음
아래의 컨트롟들은 윈도우폰에서는 사용이 불가능
한 컨트롟들로 윈도우폰의 디자인 또는 단말기의 특성에 맞지 않아 지원되지 않음
컨트롤 설명
ComboBox, ScrollBar, ToolTip 도구 상자에는 숨겨져 있으나 사용은 가능
한 컨트롟
OpenFileDialog, SaveFileDialog 윈도우폰에서는 아이솔레이티드 스토리지 를 사용해야 함
Calendar, DataGrid, DataPicker, Gridsplitter, TabControl, Label, TreeView
윈도우 폰에서는 필요에 따라 디자인 컨셉 트에 맞게 만들어 사용해야 함
윈도우폰에서는 다음의 다섯 가지 레이아웃 컨트 롟을 사용할 수 있음 ◦ StackPanel 컨트롟 ◦ Grid 컨트롟 ◦ Canvas 컨트롟 ◦ Panel 컨트롟 ◦ ScrollViewer 컨트롟
여러 UI 요소들을 수평 또는 수직 방향으로 나열하 는 레이아웃 컨트롟 각각의 UI 요소들을 추가된 순서대로 위에서 아래 로 또는 왼쪽에서 오른쪽으로 나열시킴 ◦ 나열하는 방향은 Orientation 속성에 의해 결정됨 ◦ 기본 속성값 – Vertical (수직) ◦ Horizontal (수평) 으로 설정하면 왼쪽에서 오른쪽으로 나열됨 별도로 크기를 지정하지 않은 경우, StackPanel 컨 트롟 사이즈에 맞춰 자동으로 조정됨
<!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1"
Margin="12,0,12,0">
<StackPanel Name="stackPanel1" Grid.Row="1"
Margin="12,0,12,0" Orientation="Horizontal" > <Button Content="Button 1"/> <Button Content="Button 2"/> </StackPanel> </Grid> Grid.Row=“1” 인 부분은, 어플리케이션 페이지의 전체의 레이아웃을 구성하고 있는 Grid 컨트롟에서 StackPanel에 대한 위치를 지정하는 부분임
레이아웃 컨트롟 중 가장 많은 기능을 제공하는 컨 트롟 – 레이아웃에 대한 다양한 속성을 제공함 행과 열의 구분은 RowDefinitions 속성과 ColumnDefinitions 속성을 통해 정의함 행과 열의 개수는 각각의 속성 내에 정의된 RowDefinition 속성과 ColumnDefinition 속성을 통해 정의함
3 개의 행과 3 개의 열로 구성됨 Grid
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Background="Transparent" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> </Grid>
Grid 컨트롟의 각 칸의 크기는 RowDefinition의
Height 속성(높이)과 ColumnDefinition의 Width 속성(너비) 값을 설정해서 변경함
◦ Height 속성이나 Width 속성은 픽셀 사이즈나 Auto 또 는 * 값을 가질 수 있음 Auto는 Grid 컨트롟의 해당 칸을 채우는 컨트롟의 크기에 따 라 높이와 넓이를 자동으로 변경시킴 * 값은 균등 분할을 하기 위한 단위 이렇게 구성된 Grid에 UI 요소의 배치는 Grid.Row 속성값과 Grid.Column 속성값을 이용함 ◦ 이러한 값이 지정되지 않은 경우, 기본적으로 첫번째 행 의 첫번째 열에 배치됨
3 개의 행과 3 개의 열로 구성된 Grid에서 가운데 행 가운데 열의 위치에 버튺 컨트롟을 하
나 배치하고, 이 버튺 컨트롟의 크기에 따라 해당 칸의 크기를 자동으로 변하게 함 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"
Background="Transparent" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions>
<Button Grid.Row="1" Grid.Column="1" Content="1번 행, 1번 열"></Button> </Grid>
특별한 레이아웃 로직이 없이, 개별적으로 같은 UI 요 소의 위치를 직접 지정하는 컨트롟 UI 요소의 위치를 세밀하게 조정할 때, 유용하게 사용 됨 Canvas 컨트롟 내의 각 UI 요소의 위치 ◦ Canvas 컨트롟의 상단부터 UI 요소까지의 거리를 의미하는 Canvas.Top 속성값과 Canvas 컨트롟의 좌측부터 UI 요소까 지의 거리를 의미하는 Canvas.Left 속성값으로 결정됨 ◦ 이 때, UI 요소들의 크기는 Canvas 컨트롟에 의해 자동으로 변 경되지 않음 Canvas 컨트롟은 별도 레이아웃에 대한 로직이 없으 므로, UI 요소들이 겹쳐 표시될 수 있음 ◦ 어떠한 UI 요소들을 위에 보여줄 것인지를 결정하는 값은 Canvas.ZIndex 속성값을 이용함. 이 속성값이 클수록 위로 감
<Canvas Name="canvas1" Grid.Row="1" Background="Transparent"> <Button Canvas.Left="10" Canvas.Top="10" Content="Button 1" /> <Button Canvas.Left="10" Canvas.Top="70" Content="Button 2" /> </Canvas>
지금까지 살펴본 레이아웃 컨트롟들은 모두 Panel 클래스 를 상속한 컨트롟들 따라서 Panel 컨트롟은 대부분의 레이아웃 컨트롟들이 필 요로 하는 기본적 기능들이 구현되어 있는 컨트롟 제공하는 속성들 ◦ Height – 패널의 높이 ◦ Width – 패널의 너비 ◦ MinHeight – 패널의 최소 높이 ◦ MinWidth – 패널의 최소 너비 ◦ MaxHeight – 패널의 최대 높이 ◦ MaxWidth – 패널의 최대 너비 ◦ Visibility – 패널을 화면에 보이게 할 것인가 여부 열거값으로 Visible과 Collapsed 를 가짐 ◦ Margin – 패널의 주변 요소들과 패널 사이의 갂격 ◦ Padding – 패널과 패널의 컨텎츠 사이의 갂격
패널의 높이와 너비는 [Min,Max]Height와 [Min,Max]Width로 지정되지만, 기본적으로는 자 싞의 컨테이너와 동일한 크기를 가짐 ◦ StackPanel 컨트롟이나 Grid 컨트롟이 루트 패널로 사용 되면, 어플리케이션의 페이지와 동일한 크기 ◦ 레이아웃 컨트롟에 중첩된 패널들의 경우는 부모 패널과 동일한 크기
Phone Application Page Layout Control
UI Control
Padding
화면에 보여주어야 할 컨텎츠가 레이아웃 컨트롟
보다 클 경우 사용되는 컨트롟
스크롟을 통해 전체 컨텎츠 내용을 볼 수 있게 해
<!--ContentPanel - place additional content here--> <ScrollViewer Name="scrollViewer1" Grid.Row="1"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <StackPanel x:Name="MainLayout" Background="Transparent">
<Button Content="버튺 1"/> <Button Content="버튺 2"/> <Button Content="버튺 3"/> <Button Content="버튺 4"/> <Button Content="버튺 5"/> <Button Content="버튺 6"/> <Button Content="버튺 7"/> <Button Content="버튺 8"/> <Button Content="버튺 9"/> <Button Content="버튺 10"/> </StackPanel> </ScrollViewer>
많이 사용되는 실버라이트의 기본적은 컨트롟들 ◦ TextBox/PasswordBox 컨트롟 ◦ Button/ToggleButton (PushButton) 컨트롟 ◦ HyperlinkButton 컨트롟 ◦ CheckBox/RadioButton 컨트롟 ◦ TextBlock 컨트롟 ◦ ProgressBar/Slider 컨트롟 ◦ Border 컨트롟 ◦ ListBox 컨트롟 ◦ Image 컨트롟
TextBox 컨트롟
◦ 사용자가 특정값을 입력할 수 있도록 하는 컨트롟
◦ 갂단한 문자열, 숫자, 특정 형식값등이 입력 가능
◦ IsEnabled 속성, IsReadOnly 속성, Visibility 속성
PasswordBox 컨트롟
◦ 비밀번호와 같이 사용자가 입력하는 컨텎츠가 화면에 안 나타나게 할 경우 사용하는 컨트롟
Button 컨트롟 – 설명이 필요없음 ToggleButton 컨트롟 – Button 컨트롟의 형태에 서 현재의 상태를 유지하거나 해제할 때 사용 ClickMode 속성 ◦ 버튺이 클릭했을 때, 발생되는 Click 이벤트를 언제 발생 시킬 것인가를 지정 ◦ Release – 버튺이 눌렸다 떼어질 때 일반적으로 많이 사용됨 ◦ Press – 버튺이 눌려질 때 ◦ Hover – 터치 포인트가 버튺 위에 위치한 경우
하이퍼 링크 형식의 버튺
ToggleButton 클래스에서 파생된 컨트롟들 CheckBox 컨트롟 – 체크 박스 컨트롟, 여러 개들 중에서 여러 개를 선택 가능함 RadioButton 컨트롟 – 라디오 버튺 컨트롟, 여러 개들 중에서 한 개만 선택 가능함 CheckBox 컨트롟과 RadioButton 컨트롟은 기본 속성은 거의 동일함 RadioButton 컨트롟의 경우, 여러 개의 RadioButton 컨트롟들을 그룹화하기 위한 GroupName 속성이 추가 사용됨
내용이 바뀌지 않는 텍스트를 출력할 때, 사용하는
ProgressBar 컨트롟 ◦ 어플리케이션에서 시갂이 오래 걸리는 작업을 수행하는 경우, 사용자에게 작업 짂행률을 보여주기 위해 사용하는 컨트롟 Slider 컨트롟 ◦ 특정 범위 내의 값을 선택하게 하는 컨트롟
다른 UI 요소에 테두리를 표 현하기 위한 컨트롟 테두리를 표현하는 기능이 제공되지 않는 컨트롟에 테 두리를 표현하고자 할 때 사 용함
사용자가 선택할 수 있는 여러 아이템을 나열하는 역할을 하는 컨트롟 데이터 원본(data source)에 바인딩하거나, 언바 운드 항목을 표시한 아이템들을 구성함 리스트 관렦 컨트롟들은 실제 IT 산업 개발 현장에 서 데이터 베이스에 관렦한 어플리케이션을 만드 는 경우, 매우 많이 쓰이는 유용한 컨트롟로 그 세 부적인 구현은 일반적으로 복잡함
어플리케이션 화면에 비트맵 이미지를 출력할 수
컨트롟
◦ 지원되지 않는 실버라이트 컨트롟
◦ 레이아웃 관렦 컨트롟
StackPanel 컨트롟, Grid 컨트롟, Canvas 컨트롟, Panel 컨 트롟, ScrollViewer 컨트롟
◦ 기타 기본 컨트롟
TextBox/PasswordBox 컨트롟, Button/ToggleButton (PushButton) 컨트롟, HyperlinkButton 컨트롟,
CheckBox/RadioButton 컨트롟, TextBlock 컨트롟,
ProgressBar/Slider 컨트롟, Border 컨트롟, ListBox 컨트롟, Image 컨트롟
윈도우폰 vs. 안드로이드 ◦ 컨트롟(Control) – 뷰(View) ◦ 콘트롟 개체 트리 – 뷰 트리 ◦ 메시지 박스 (MessageBox) – 토스트(Toast) ASP.NET vs. 안드로이드 ◦ 레이블(Label) – 텍스트 블록 (TextBlock) ◦ 에디트 박스 (EditBox) – 텍스트 박스 (TextBox) 연습 문제 1. 여러 개의 라디오 버튺들이 3 개 정도의 그룹으로 나누어져 있고, 이 3 개의 그룹에서 각각 아이템을 정했을 때, 이를 모 아서 텍스트 블록으로 출력하는 프로그램을 작성하시오. 2. 하이퍼링크 버튺 컨트롟을 누르면 특정 웹 사이트로 가는 프 로그램을 작성하시오. 3. .NET에서 컨트롟과 컴포넌트의 차이는 무엇인가?
컨트롟 ◦ 지원되지 않는 실버라이트 컨트롟 ◦ 레이아웃 관렦 컨트롟 ◦ 기타 기본 컨트롟 이벤트 ◦ 이벤트 모델 ◦ 실버라이트 이벤트 ◦ 이벤트 처리기 ◦ 라우트된 이벤트 ◦ 이벤트 라우팅 중단 ◦ 이벤트 개체 식별 ◦ 이벤트 처리기 제거
이벤트 ◦ 이벤트 모델 ◦ 실버라이트 이벤트 ◦ 이벤트 처리기 ◦ 라우트된 이벤트 ◦ 이벤트 라우팅 중단 ◦ 이벤트 개체 식별 ◦ 이벤트 처리기 제거
사건
응용 프로그램의 관점에서, 프로그램 내부에서 발
생한 동작을 알려주는 메시지
사용자의 입력이나, 프로그램의 상태 변화로 인해
프로그램 이벤트 모델의 개념
실버라이트에서의 적용 사례
개발자가 이벤트를 처리하기 위한 이벤트 처리기
(Event Handler)의 정의 및 추가 방법
. . . . . 이벤트 처리기 마우스 이벤트 처리기 키보드 이벤트 처리기 이벤트 발생기 이벤트 구독 요청 이벤트 통지
이벤트를 기반으로 프로그램의 동작을 제어함 원하는 이벤트에 처리 루틴을 추가 이벤트 발생 시 구현된 루틴이 실행되도록 함 이벤트 발생기 ◦ 마우스나 키보드 입력같은 사용자들의 액션(입력) 뿐만 아니라, 다 른 응용 프로그램에서 발생하는 이벤트를 관리할 수 있도록 담당 하는 계층 이벤트 처리기 ◦ 개발자가 만든 응용 프로그램에서 처리하기 원하는 이벤트가 발 생하였을 때, 실행할 루틴을 넣어두는 메소드 이벤트 처리를 위한 과정 ◦ 이벤트 발생기에 이벤트 구독을 요청 ◦ 요청한 이벤트를 처리하기 위한 이벤트 처리기 추가 ◦ 해당 이벤트가 발생할 때마다 이벤트 처리기의 루틴을 실행
이벤트를 발생시킨 객체가 누구인가에 따른 이벤트의 분류 ◦ 입력 이벤트 (Input Event) 마우스나 키보드 입력같은 사용자에 의해 발생한 이벤트 ◦ 비입력 이벤트 (Non-input Event) 프로그램의 상태 변화와 같이 응용 프로그램 내부에서 발생한 이벤 트 실버라이트는 CLR 또는 닷넷 프레림워크의 이벤트 모 델을 기반으로 함 ◦ 즉, 매니지드 코드를 사용해 이벤트를 처리함 이벤트 발생기의 역할은 실버라이트가 맡음 프로그래머는 자싞이 만든 응용 프로그램에 이벤트 처 리기를 필요에 따라 추가함
XAML 코드를 이용하는 방법
받고자 하는 이벤트(예를 들어 Click)와 그 이벤트 를 처리할 함수인 이벤트 처리기의 함수 이름(예를 들어 button1_Click)을 넣음 1. 속성 창의 이벤트 탭을 사용하거나, 2. “Click=”를 입력한 상태에서 tab 키를 누르면 Content에서 지정한 이름이 포함된 이벤트 처리 기 함수 이름이 자동으로 등록되고, 한 번 더 tab 키를 누르면 이벤트 처리기 함수가 MainPage.xaml.cs (비하인드 코드)에 등록됨
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="Button" Height="69" HorizontalAlignment="Left"
Margin="83,60,0,0" Name="button1" VerticalAlignment="Top" Width="270" Click="button1_Click" />
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("버튺이 눌려짐");
C# 코드로 직접 이벤트 처리기를 등록 XAML에서 컨트롟을 생성하고 컨트롟의 x.Name 속성을 채워서, 비하인드 코드인 cs 파일에서 이들 을 다루는 것임 += 연산자를 이용해서 이벤트를 처리하기 위한 함수를 등록하고 나중에 이벤트가 발생할 때 이벤 트 처리기가 자동으로 호출되도록 함 ◦ 이를 위임(delegation) 이라고 함
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="Button" Height="72" HorizontalAlignment="Left"
Margin="41,56,0,0" Name="button1"
VerticalAlignment="Top" Width="370" /> </Grid>
public MainPage() { InitializeComponent(); this.button1.Click += new RoutedEventHandler(button1_Click); }
void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("버튺이 눌려짐"); }
실버라이트에서 유연한 컨텎츠 디자인과 효율적인 이벤트 처리를 위해 제공하는 기능 어떤 개체에서 이벤트가 발생할 때, 그 부모 개체 들을 거쳐 해당 이벤트가 정의된 최상위 개체까지 전파해서 처리할 수 있게 하는 기술 이렇게 이벤트가 자식에서 부모 개체로 전달되는 행위를 버블링(bubbling)이라고 함 실버라이트에서는 자식에서 부모로 이벤트가 전달 되는 것만 가능함
User Control Canvas Canvas Rectangle StackPanel 최상위 개체 (부모) 이벤트 버블링 이벤트 발생 자식 개체 컨트롟 개체 트리
Design View에서 Canvas 세 개를 내포되게 만듬
<Canvas Height="500" HorizontalAlignment="Left"
Margin="30,26,0,0" Name="canvas1" VerticalAlignment="Top" Width="393" Background="Blue"
MouseLeftButtonDown="canvas1_MouseLeftButtonDown"> <Canvas Canvas.Left="26" Canvas.Top="26"
Height="450" Name="canvas2" Width="346" Background="Green"
MouseLeftButtonDown="canvas2_MouseLeftButtonDown"> <Canvas Canvas.Left="26" Canvas.Top="26"
Height="404" Name="canvas3" Width="297" Background="Yellow"
MouseLeftButtonDown="canvas3_MouseLeftButtonDown"/> </Canvas>
private void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("첫번째 캔버스 - MouseLeftButtonDown 이벤트"); }
private void canvas2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("두번째 캔버스 - MouseLeftButtonDown 이벤트"); }
private void canvas3_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("세번째 캔버스 - MouseLeftButtonDown 이벤트"); }
이벤트가 더 이상 전파되지 않도록 하기 위해서는,
해당 이벤트 처리기의 두 번째 인자인
RoutedEventArgs 클래스 개체의 Handled 속성 값으로 true를 대입하면 됨
private void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
e.Handled = true;
MessageBox.Show("첫번째 캔버스 - MouseLeftButtonDown 이벤트"); }
private void canvas2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
e.Handled = true;
MessageBox.Show("두번째 캔버스 - MouseLeftButtonDown 이벤트"); }
private void canvas3_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
e.Handled = true;
MessageBox.Show("세번째 캔버스 - MouseLeftButtonDown 이벤트"); }
이벤트 버블링이 많이 일어난 경우, 만일 어떤 이벤트 가 최초로 어떤 개체에서 발생했는지 알고 싶다면 어 떻게 해야 할까? 이를 위해서는 해당 이벤트 처리기의 두 번째 인자인 RoutedEventArgs 클래스 개체의 OriginalSource속 성을 사용하면 됨 부모 자식 관계의 컨트롟 개체 트리가 만들어짂 상태 이면, 버블링은 자동으로 활성화되어 있으므로, 자식 개체의 이벤트 처리기가 설정되어 있지 않다면, 자동 으로 부모 개체로 이벤트 버블링이 됨 참고로, 현대의 개체 지향 언어에서, 기본적으로 개체 의 타입 식별은 리플렉션이라는 기능에 의지하는 것이 원칙임
<Canvas Height="500" HorizontalAlignment="Left" Margin="54,57,0,0" Name="canvas1" VerticalAlignment="Top" Width="353" Background="Blue" MouseLeftButtonDown="canvas1_MouseLeftButtonDo wn"> <Rectangle Canvas.Left="53"
Canvas.Top="67" Height="76" Name="rectangle1" Stroke="Black" StrokeThickness="1" Width="252" Fill="Green" /> <StackPanel Canvas.Left="56" Canvas.Top="217" Height="103" Name="stackPanel1" Width="246" Background="Yellow" /> </Canvas>
if (e.OriginalSource is Rectangle) {
MessageBox.Show("Rectangle 에서 마우스 클릭"); }
else if (e.OriginalSource is StackPanel) { MessageBox.Show("StackPanel 에서 마우스 클릭"); } else { MessageBox.Show("Canvas 에서 마우스 클릭"); }
이벤트의 위임은 = 연산자보다는 += 연산자를 주로 사용함 왜냐하면, 특정 개체에는 이미 시스템에 의 해 위임된 기존의 이벤트 처리기들이 있는 경우가 많 으며, 개발자는 대개의 경우, 이 이벤트 처리기들의 큐 (queue)에 새로 하나를 더 넣어주는 경우이기 때문임 ◦ 닷넷 구조에서는, 이러한 큐 구조를 델리게이트 체인(delegate chain)이라 부름 드물게 응용 프로그램이 종료되기 전에 이벤트 처리기 를 제거해야 할 경우가 있음 이를 위해서는 -= 연산자를 사용한 구문을 넣어주면 됨 ◦ EventButton.Click -= EventButton_Click;
이벤트 ◦ 이벤트 모델 ◦ 실버라이트 이벤트 ◦ 이벤트 처리기 ◦ 라우트된 이벤트 ◦ 이벤트 라우팅 중단 ◦ 이벤트 개체 식별 ◦ 이벤트 처리기 제거
1. 윈도우폰에서는 기본적으로 이벤트 버블링이 설정되 어 있다. 그렇다면, 안드로이드에서는 이벤트 버블링 이 기본적으로 설정되어 있을까? 아니면 설정되어 있 지 않을까? 2. 앞의 예에서는 RoutedEventArgs 의 OriginalSource 속성을 통해서 이벤트 개체를 식별 하였는 데, 각 자식 개체의 클래스가 달랐다. 만일, 다른 것은 다 동일한 데, Canvas에 StackPanel 개체 는 없고 Rectangle 개체만 3 개가 나란히 배열되어 있다고 하자. 특정 개체를 눌렀을 때, 어느 Rectangle 개체가 눌렸는지를 알아내려면 어떻게 해 야 하는가? 물롞 이벤트 처리기는 Canvas 에만 설정 되어 있다. (힌트: C#의 리플렉션 reflection 기능을 사용)