http://www.codeproject.com/Articles/124644/Basic-Understanding-of-Tree-View-in-WPF


Basic Understanding of Tree View in WPF

3 Nov 2010
This article explains different ways to show contents in the Tree View control.

Introduction

This article describes the use of TreeView control provided by WPF. It will give you a knowledge to create simple Tree, customization, Template and Binding. It mainly focuses on how to show contents in the tree view. This article will help you to understand the basics of tree view and also gives you deep knowledge to show content on tree according to your requirement.

I had to use tree view in one of my projects where I had to show image and text as most of tree has in windows. This problem started my journey to learn features provided by WPF. At the start of my journey, I found many difficulties and sometimes I said Windows application contains more user friendly controls to work with, but with the passage of time I found WPF controls more user friendly.

This article will cover the following 6 main areas:

Create Simple Tree

If you want to create a simple tree then WPF provides you an easy way draw tree. Just add TreeView control on your page and add items in either using XAML or from code behind.

Using XAML

You can easily create a tree using XAML.

 <TreeView>
    <TreeViewItem Header="North America">
        <TreeViewItem Header="USA"></TreeViewItem>
        <TreeViewItem Header="Canada"></TreeViewItem>
        <TreeViewItem Header="Mexico"></TreeViewItem>
    </TreeViewItem>
    <TreeViewItem Header="South America">
        <TreeViewItem Header="Argentina"></TreeViewItem>
        <TreeViewItem Header="Brazil"></TreeViewItem>
        <TreeViewItem Header="Uruguay"></TreeViewItem>
    </TreeViewItem>
 </TreeView>

Using code

If you want to populate tree from code behind, then simply place your tree on form and add tree item according to your tree hierarchy.

 <TreeView Name="tvMain">
 </TreeView>
 TreeViewItem treeItem = null;
         
 // North America 
 treeItem = new TreeViewItem();
 treeItem.Header = "North America";

 treeItem.Items.Add(new TreeViewItem() { Header = "USA" });
 treeItem.Items.Add(new TreeViewItem() { Header = "Canada" });
 treeItem.Items.Add(new TreeViewItem() { Header = "Mexico" });

 tvMain.Items.Add(treeItem);

Customize Tree

If you want to add some other controls with the content e.g. checkbox, image, etc., then you can easily design your tree without any big effort. You just need to customize the HeaderTemplate of the TreeViewItem. You can also create class derived from TreeViewItem and change its Header according to your requirement.

Using XAML

For customizing the Tree item, simply change item’s Header.

 <TreeView >
    <TreeViewItem >
        <TreeViewItem.Header>
            <StackPanel Orientation="Horizontal">
                <Border Background="Green" Width="8" Height="12" 
                        BorderBrush="#00000000"></Border>
                <Label Content="North America"></Label>
            </StackPanel>
        </TreeViewItem.Header>

        <!-- Child Item -->

        <TreeViewItem>
            <TreeViewItem.Header>
                <StackPanel Orientation="Horizontal">
                    <Image Source="../Images/usa.png"></Image>
                    <Label Content="USA"></Label>
                </StackPanel>
            </TreeViewItem.Header>
        </TreeViewItem>
    </TreeViewItem>
 </TreeView>

Using Code

If you want to create header from code behind, then WPF will not disappoint you. You can change header template very smartly.

 private TreeViewItem GetTreeView(string text, string imagePath)
 {
    TreeViewItem item = new TreeViewItem();

    item.IsExpanded = true;

    // create stack panel
    StackPanel stack = new StackPanel();
    stack.Orientation = Orientation.Horizontal;

    // create Image
    Image image = new Image();
    image.Source = new BitmapImage
		(new Uri("pack://application:,,/Images/" + imagePath));

    // Label
    Label lbl = new Label();
    lbl.Content = text;


    // Add into stack
    stack.Children.Add(image);
    stack.Children.Add(lbl);

    // assign stack to header
    item.Header = stack;
    return item;        
 }

Using overriding TreeViewItem

You can also customize TreeViewItem by writing a new derived class for custom item. It is also pretty easy. Just create header template and assign it to Header property if TreeViewItem.

 public class ImageTreeViewItem : TreeViewItem
 {
    #region Data Member

    Uri _imageUrl = null;
    Image _image = null;
    TextBlock _textBlock = null;

    #endregion

    #region Properties

    public Uri ImageUrl
    {
        get { return _imageUrl; }
        set
        {
            _imageUrl = value;
            _image.Source = new BitmapImage(value);
        }
    }

    public string Text
    {
        get { return _textBlock.Text; }
        set { _textBlock.Text = value; }
    }

    #endregion

    #region Constructor

    public ImageTreeViewItem()
    {
        CreateTreeViewItemTemplate();
    }

    #endregion

    #region Private Methods

    private void CreateTreeViewItemTemplate()
    {
        StackPanel stack = new StackPanel();
        stack.Orientation = Orientation.Horizontal;

        _image = new Image();
        _image.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
        _image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
        _image.Width = 16;
        _image.Height = 16;
        _image.Margin = new Thickness(2);

        stack.Children.Add(_image);

        _textBlock = new TextBlock();
        _textBlock.Margin = new Thickness(2);
        _textBlock.VerticalAlignment = System.Windows.VerticalAlignment.Center;

        stack.Children.Add(_textBlock);

        Header = stack;
    }

    #endregion
 }

Header Template

If the style of all the elements is the same, then it is better to create header template at once. Because the problem with the last example was for the same design, we add template for each tree item.

Using XAML

For creating general TreeViewItem item template, create Template resource at application level, window level or at control level resource. In this example, I have created resource at control level and set theTargetType=”TreeViewItem” and also set the “HeaderTemplate” property of the TreeViewItem.

 <TreeView Name="tvMain">
    <TreeView.Resources>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="HeaderTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">                                 
                            <CheckBox Name="chk" Margin="2" Tag="{Binding}" >
			</CheckBox>
                            <Image  Margin="2"  Source="{Binding Converter=
			{StaticResource CustomImagePathConvertor}}"></Image>
                            <TextBlock Text="{Binding}"></TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TreeView.Resources>

    <TreeViewItem Header="North America" IsExpanded="True">
        <TreeViewItem Header="USA"></TreeViewItem>
        <TreeViewItem Header="Canada"></TreeViewItem>
        <TreeViewItem Header="Mexico"></TreeViewItem>
    </TreeViewItem>

    <TreeViewItem Header="South America"  IsExpanded="True">
        <TreeViewItem Header="Argentina"></TreeViewItem>
        <TreeViewItem Header="Brazil"></TreeViewItem>
        <TreeViewItem Header="Uruguay"></TreeViewItem> 
 </TreeView>

It is a very interested point here that I did not pass Image path for each country, but TreeView shows flag with each country. I achieved by writing custom converter CustomImagePathConverter.

  <Image  Margin="2"  Source="{Binding Converter=
	{StaticResource CustomImagePathConverter}}"></Image>     

Implement CustomImagePathConverter from IValueConverter. You can associate a value converter with binding. In this example, I am getting image path from the country name, as you can see in code.

 public class CustomImagePathConverter : IValueConverter
 {
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, 
                                    System.Globalization.CultureInfo culture)
    {
        return "../Images/" + GetImageName(value.ToString());
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
                                    System.Globalization.CultureInfo culture)
    {
        return "";
    }

    #endregion

    private string GetImageName(string text)
    {
        string name = "";
        name = text.ToLower() + ".png";
        return name;
    }
 } 

Using Code

You can easily create template from code behind. FrameworkElementFactory provides you a facility to create templates. Let's see how can we achieve this exciting feature.

 private DataTemplate GetHeaderTemplate()
 {
    //create the data template
    DataTemplate dataTemplate = new DataTemplate();

    //create stack pane;
    FrameworkElementFactory stackPanel = new FrameworkElementFactory(typeof(StackPanel));
    stackPanel.Name = "parentStackpanel";
    stackPanel.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

    // Create check box
    FrameworkElementFactory checkBox = new FrameworkElementFactory(typeof(CheckBox));
    checkBox.Name = "chk";
    checkBox.SetValue(CheckBox.NameProperty, "chk");
    checkBox.SetValue(CheckBox.TagProperty , new Binding());
    checkBox.SetValue(CheckBox.MarginProperty, new Thickness(2));
    stackPanel.AppendChild(checkBox);

    // Create Image 
    FrameworkElementFactory image = new FrameworkElementFactory(typeof(Image));
    image.SetValue(Image.MarginProperty, new Thickness(2));
    image.SetBinding(Image.SourceProperty, new Binding() 
		{ Converter = new CustomImagePathConverter() });
    stackPanel.AppendChild(image);

    // create text
    FrameworkElementFactory label = new FrameworkElementFactory(typeof(TextBlock));
    label.SetBinding(TextBlock.TextProperty, new Binding());
    label.SetValue(TextBlock.ToolTipProperty, new Binding());          
    stackPanel.AppendChild(label);

          
    //set the visual tree of the data template
    dataTemplate.VisualTree = stackPanel;

    return dataTemplate;
 } 

Simply assign this template to HeaderTemplate of each TreeViewitem.

 DataTemplate template = GetHeaderTemplate();

 foreach (WorldArea area in WorldArea.GetAll())
 {
    TreeViewItem item = new TreeViewItem();
    item.HeaderTemplate = template;
    item.Header = area.Name;

    .
    .
    .
    .
 }

Get selected checked items

You can easily get the child items from the template. Just for the example, I am showing you how to get the selected check boxes from the tree view. WPF manage control in hierarchical structure, you can access any child usingVisualTreeHelper class.

 private List<CheckBox> GetSelectedCheckBoxes(ItemCollection items)
 {
    List<CheckBox> list = new List<CheckBox>();
    foreach (TreeViewItem item in items)
    {
        UIElement elemnt = GetChildControl(item, "chk");
        if (elemnt != null)
        {
            CheckBox chk = (CheckBox)elemnt;
            if (chk.IsChecked.HasValue && chk.IsChecked.Value)
            {
                list.Add(chk);
            }
        }

        List<CheckBox> l = GetSelectedCheckBoxes(item.Items);
        list = list.Concat(l).ToList();
    }

    return list;
 }

 private UIElement GetChildControl(DependencyObject parentObject, string childName)
 {

    UIElement element = null;

    if (parentObject != null)
    {
        int totalChild = VisualTreeHelper.GetChildrenCount(parentObject);
        for (int i = 0; i < totalChild; i++)
        {
            DependencyObject childObject = VisualTreeHelper.GetChild(parentObject, i);

            if (childObject is FrameworkElement && 
		((FrameworkElement)childObject).Name == childName)
            {
                element = childObject as UIElement;
                break;
            }

            // get its child 
            element = GetChildControl(childObject, childName);
            if (element != null) break;
        }
    }

    return element;
 }

Custom Objects

WPF provides you many ways to populate tree. You can directly add your object as a TreeViewItem in the tree and WPF gives respect to your objects and display it as you want. You just need to tell him which field will be shown in item.

Using XAML

For populating custom object in tree, you just need to create template for your object. I usedHierarchicalDataTemplate for designing template.

 <TreeView Name="tvMain">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Countries}">
            <StackPanel Orientation="Horizontal" Margin="4" Background="LightSeaGreen">
                <CheckBox Name="chk" Margin="2" Tag="{Binding Path=Name}" ></CheckBox>
                <Image  Margin="2" Source="{Binding Path=ImageUrl}" ></Image>
                <TextBlock Text="{Binding Path=Name}" Margin="2" >
                </TextBlock>
                <StackPanel.Effect>
                    <DropShadowEffect BlurRadius="2" Color="LightGray" 
			 Opacity=".2" ></DropShadowEffect>
                </StackPanel.Effect>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>         
 </TreeView>

After creating template, you just need to add custom object from your code behind file, as you can see code below. I am just putting parent object in the tree. But when you will run this code, you will also see child countries are also being shown. The reason is that because I have define template in XAML for child items using ItemsSource="{Binding Path=Countries}".

 private void FillTree()
 {  
    foreach (WorldArea area in WorldArea.GetAll())
    {
        tvMain.Items.Add(area);        
    }
 }

Using Code

You can also create template for your object from code behind file, as we created in previous example. The tricky part here, how can we add custom objects in the hierarchical way? Because using XAML we can write create hierarchical template. We can also create hierarchical template using code behind, but in this example I am not doing that, I am achieving the solution from other way. This technique will give you a new way to work and you can implement it in other Items controls like ListViewListBox etc. But in the last example, I will create hierarchical template from code behind.

 private void FillTree()
 {
    tvMain.ItemTemplate = GetHeaderTemplate();
    tvMain.ItemContainerGenerator.StatusChanged += 
		new EventHandler(ItemContainerGenerator_StatusChanged);

    foreach (WorldArea area in _list)
    {
        tvMain.Items.Add(area);        
    }
 }

 void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
 {
    if (tvMain.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
    {
        foreach (WorldArea area in _list)
        {
            TreeViewItem item = 
		(TreeViewItem)tvMain.ItemContainerGenerator.ContainerFromItem(area);
            if (item == null) continue;
            item.IsExpanded = true;
            if (item.Items.Count == 0)
            {

                foreach (Country country in area.Countries)
                {
                    item.Items .Add(country);
                }
            }
        }
    }
 } 

As you can see in code after adding setting template, I have registeredtvMain.ItemContainerGenerator.StatusChanged event. ItemContainerGenerator generates the containers for each custom object. When we add custom object in the TreeView ItemContainerGenerator starts to generate container in separate thread. So we cannot get container in the next line after adding object. So you need to registerStatusChanged event, which fires after the status change and you can get container after that.

Data Binding

You can also bind your tree with any source as you can bind DataGridListView, etc. You just need to create a template for your items as you create in other binding controls.

Using XAML

Create your hierarchical template as you created in the previous example. You may need to add inner hierarchical template for different example. But it is working fine for my example.

 <TreeView Name="tvMain"  >
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Countries}">

            <Grid Background="LightSkyBlue"  Margin="2" Width="100" Height="24">     
                <Image Margin="2" Width="32" Height="18" 
			Source="{Binding Path=ImageUrl}" 
		HorizontalAlignment="Right" 
               	VerticalAlignment="Center" ></Image>
                <TextBlock Margin="2" Text="{Binding Path=Name}" 
			VerticalAlignment="Center" FontWeight="Bold" />
            </Grid>

        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>            
 </TreeView>

Simply bind tree using ItemsSource property:

 private void BindTree()
 {
    tvMain.ItemsSource = WorldArea.GetAll(); 
 }

Using Code

For creating Hierarchical template from code behind simply create object of HierarchicalDataTemplate class and fill childs according to your requirement and assign this template to tree.

 private void BindTree()
 {
    tvMain.ItemTemplate = GetTemplate(); 
    tvMain.ItemsSource = WorldArea.GetAll(); 
 }

 private HierarchicalDataTemplate GetTemplate()
 {              
    //create the data template
    HierarchicalDataTemplate dataTemplate = new HierarchicalDataTemplate();

    //create stack pane;
    FrameworkElementFactory grid = new FrameworkElementFactory(typeof(Grid));
    grid.Name = "parentStackpanel";
    grid.SetValue(Grid.WidthProperty, Convert.ToDouble(100));
    grid.SetValue(Grid.HeightProperty, Convert.ToDouble(24) );
    grid.SetValue(Grid.MarginProperty, new Thickness(2));
    grid.SetValue(Grid.BackgroundProperty, new SolidColorBrush( Colors.LightSkyBlue));
              
    // Create Image 
    FrameworkElementFactory image = new FrameworkElementFactory(typeof(Image));
    image.SetValue(Image.MarginProperty, new Thickness(2));
    image.SetValue(Image.WidthProperty, Convert.ToDouble(32));
    image.SetValue(Image.HeightProperty, Convert.ToDouble(24));
    image.SetValue(Image.VerticalAlignmentProperty, VerticalAlignment.Center );
    image.SetValue(Image.HorizontalAlignmentProperty, HorizontalAlignment.Right);
    image.SetBinding(Image.SourceProperty, new Binding() 
		{ Path = new PropertyPath("ImageUrl") });
    
    grid.AppendChild(image);

    // create text
    FrameworkElementFactory label = new FrameworkElementFactory(typeof(TextBlock));
    label.SetBinding(TextBlock.TextProperty, 
		new Binding() { Path = new PropertyPath("Name") });
    label.SetValue(TextBlock.MarginProperty, new Thickness(2));
    label.SetValue(TextBlock.FontWeightProperty, FontWeights.Bold);
    label.SetValue(TextBlock.ToolTipProperty, new Binding());

    grid.AppendChild(label);

    dataTemplate.ItemsSource = new Binding("Countries"); 

    //set the visual tree of the data template
    dataTemplate.VisualTree = grid;

    return dataTemplate;
 }

Template By Data Type

A very nice flexibility provided by WPF is you can create your template by data type. Suppose you want have to show different type of objects in tree and you want to differentiate them on UI. It is not a big problem in WPF. Simply create Template by Data type and bind source with tree or manually add objects. Your tree will pick template according to data type.

Using Data Template

Simply create data template in any resource as I created in tree resource. And set its data type as I did usingDataType="{x:Type loc:WorldArea}".

 <TreeView Name="tvMain">
    <TreeView.Resources>

        <DataTemplate DataType="{x:Type loc:WorldArea}">
            <Border Width="150" BorderBrush="RoyalBlue" 
		Background="RoyalBlue"  BorderThickness="1" 
		CornerRadius="2" Margin="2" Padding="2" >
                <StackPanel Orientation="Horizontal" >
                    <TextBlock  Text="{Binding Path=Name}" 
			FontWeight="Bold" Foreground="White"></TextBlock>
                </StackPanel>
            </Border>
        </DataTemplate>

        <DataTemplate  DataType="{x:Type loc:Country}">
            <Border Width="132"  Background="LightBlue" CornerRadius="2" Margin="1" >
                <StackPanel Orientation="Horizontal" >
                    <Image Margin="2" Source="{Binding Path=ImageUrl}"></Image>
                    <TextBlock Margin="2"  Text="{Binding Path=Name}"></TextBlock>
                </StackPanel>
            </Border>
        </DataTemplate>

    </TreeView.Resources>
 </TreeView>

Using Hierarchical Template

You can also create hierarchical template by data type.

 <TreeView Name="tvMain">
    <TreeView.Resources>

        <HierarchicalDataTemplate DataType="{x:Type loc:WorldArea}" 
			ItemsSource="{Binding Path=Countries}">
            <Border Width="150" BorderBrush="RoyalBlue" Background="RoyalBlue" 
		 BorderThickness="1" CornerRadius="2" Margin="2" Padding="2" >
                <StackPanel Orientation="Horizontal" >
                    <TextBlock  Text="{Binding Path=Name}" 
			FontWeight="Bold" Foreground="White"></TextBlock>
                </StackPanel>
            </Border>
        </HierarchicalDataTemplate>
                
        <HierarchicalDataTemplate DataType="{x:Type loc:Country}">
            <Border Width="132"  Background="LightBlue" CornerRadius="2" Margin="1" >
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"></ColumnDefinition>
                        <ColumnDefinition Width="26"></ColumnDefinition>
                    </Grid.ColumnDefinitions>

                    <TextBlock Margin="2"  Text="{Binding Path=Name}"></TextBlock>

                    <Image Grid.Column="1" Margin="2" 
			Source="{Binding Path=ImageUrl}"></Image>
                </Grid>
            </Border>
        </HierarchicalDataTemplate>

    </TreeView.Resources>
 </TreeView>


This article, along with any associated source code and files, is licensed under 
The Code Project Open License (CPOL)



반응형

http://dotnetmvp.tistory.com/37

15. [WPF 기초 강좌] 사용자입력 1


안녕하세요.

WPF 기초강좌를 진행하는 김창민입니다.

 

 

이제 완전 따듯한 봄이되었내요...

아직 꽃들만 피지 않았지 바람은 따듯한 바람이 불어옵니다.

좀더 더워지기전에 스피치를 올려 강좌에 집중해야겠내요.. ㅋㅋ

 

 

그럼 오늘 강좌를 시작하겠습니다.


오늘 같이 알아볼 내용은 사용자 입력으로 사용자가 직접 키보드나 마우스스타일러스 펜 등을 이용하여 값을 추가변경하거나 이벤트를 발행 시키고자 할 때 사용하는 입력 방식을 말합니다.

그 중에서 오늘은 키보드를 이용한 입력방식에 대해 알아볼 것이며 간단한 예제로 키입력 이벤트나 클래스에서 지원하는 메서드의 사용방법에 대해 알아보겠습니다.



기본적으로 입력을 관리하는 입력API는 UIElement, ContentElement, FrameworkElement  FrameworkContentElement의 영향을 받으며 키 입력마우스 버튼클릭마우스 휠 Up-Down, 마우스 이동 등에 관한 입력 이벤트를 제공해 줍니다입력 이벤트는 라우트된 이벤트를 지원하기 때문에 버블링터널링이 모두 가능합니다예를 들어 키보드를 누르는 입력 이벤트는  KeyDown, PreviewKeyDown 이벤트가 연결되어 있습니다.

기본 입력 API 이외에도 Keyboard 클래스와 Mouse 클래스는 키보드 및 마우스 입력을 처리하기 위해 추가 API를 제공합니다.

 

 

- Keyboard 클래스

 

Keyboard 클래스는 키보드 관련 이벤트메서드키보드상태에 관한 속성을 제공해 줍니다. 

다음 표는 키보드 입력 이벤트들을 나열해 놓은 것입니다.

 

이벤트

설명

PreviewGodKeyboardFocus,

GodKeyboardFocus

컨트롤이 포커스를 받았을 때

PreviewLostKeyboardFocus,

LostKeyboardFocus

컨트롤이 포커스를 잃었을 때

GotFocus

컨트롤이 논리적 포커스를 받았을 때

LostFocus

컨트롤이 논리적 포커스를 잃었을 때

PreviewKeyDown, KeyDown

키가 눌렸을  

PreviewKeyUp, KeyUp

키가 눌렸다 떼어졌을 때

PreviewTextInput, TextInput

컨트롤이 텍스트 입력을 받았을 때



예제를 통해 간단한 키입력에 관한 이벤트와 몇가지 속성메서드에 대해 알아보고 Keyboard 클래스에서 제공하는 이벤트외에 UIElement에서 제공하는 이벤트에 대해 함께 알아보고 Keyboard 이벤트의 라우트된 이벤트가 지원이 되는를 터널링 이벤트를 통해 알아보도록 하겠습니다.

 

우선 VisualStudio 2008을 실행시켜 “UserInput”이라는 새 프로젝트를 생성합니다.






Window1.xaml Label 컨트롤 2개와 Textbox 컨트롤 1개를 Window Grid 안에 배치하겠습니다.

 

 

<!--Window1.xaml -->

<Window x:Class="UserInput.Window1"

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

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

    Title="Window1" Height="300" Width="300" Keyboard.KeyDown="Window_Keyboard_KeyDown"  KeyDown="Window_KeyDown" >

    <Grid>

        <Label    Height="28" Margin="12,12,12,0" Name="label1" VerticalAlignment="Top">Label1</Label>

        <Label    Height="28" Margin="12,46,12,0" Name="label2" VerticalAlignment="Top">Label2</Label>

        <TextBox Margin="12,80,12,0" Name="textBox1" PreviewKeyUp="textBox1_PreviewKeyUp" Height="23" VerticalAlignment="Top" />

    </Grid>

</Window>

 

 

XAML 코드를 보시면 Window Keyboard 클래스에서 지원하는 “Keyboard.KeyDown” 이벤트와UIElement에서 지원하는 “KeyDown” 이벤트를 추가하였고 Label 컨트롤은 Grid안에 Margin으로 위치만 잡아주었습니다그리고 Textbox는 라우트된 이벤트의 tunneling을 테스트해 보기위해 라우트된 이벤트를 추가하였습니다.

 

다음으로 “Window1.xaml.cs”로 이동하여 이벤트핸들러에서 실행될 코드를 작성하겠습니다.

이벤트 핸들러에서 실행할 코드는 이벤트의 실행여부를 확인하기 위해 Label, Textbox 컨트롤에 텍스트로 표시해 주려합니다.

 

<!--Window1.xaml.cs -->

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

 

namespace UserInput

{

    public partial class Window1 : Window

    {

        public Window1()

        {

            InitializeComponent();

        }

 

        private void Window_Keyboard_KeyDown(object sender, KeyEventArgs e)

        {

            label1.Content = "Keyboard.KeyDown";

        }

 

        private void Window_KeyDown(object sender, KeyEventArgs e)

        {

            label2.Content = "UIElement.KeyDown";

        }

 

        private void textBox1_PreviewKeyUp(object sender, KeyEventArgs e)

        {

            textBox1.Text = "textBox1_PreviewKeyUp";

        }

    }

}

 

이제 간단한 테스트 코드 작성은 끝이 났습니다 실행을 시켜 키입력 이벤트를 확인해 보시고 Textbox에 키입력으로 라우트된 이벤트 역시 실행이 되는지도 확인해 보세요




 

 

 

실행 결과로 키보드 입력이 되면 Keyboard 클래스에서 지원하는 이벤트와 UIElement에서 지원하는 이벤트가 전부 실행되며 TextBox의 라우트된 이벤트 역시 실행되는 것을 알 수 있습니다.

 

이번에는 Keyboard 클래스에서 지원하는 “GetKeyStates”메서드를 사용해 보겠습니다.

위 프로젝트에서 Keyboard.KeyDown이벤트를 그대로 사용하기 위해 이벤트 핸들러 안에 사용했던 코드(label1.Content = "Keyboard.KeyDown";)를 주석 처리하겠습니다.

그리고 아래 코드를 입력하겠습니다.



 

if (Keyboard.GetKeyStates(Key.Enter) == KeyStates.Down)

label1.Content = "Enter 키를 누르셨습니다.";

else

label1.Content = "Enter 키를 누르세요";

 

“GetKeyStates”메서드는 지정한 키의 상태를 가져오는 메서드로 “KeyStates”  Down, None, Toggled의 값을 가져옵니다.

위 코드는 Keyboard Down 이벤트시 ‘Enter Key’의 상태를 GetKeyStates로 확인하여 엔터키가 “Down”이 되었다면 그에 해당하는 메시지를 Label에 보여주는 동작을 하게 될 것입니다.

 

그럼 실행을 시켜  결과를 보도록 하죠..

 

 

Enter Key 입력시




Enter Key 아닌 Key 입력시


 

지금까지 키보드 입력에 대해 간단히 알아보았습니다.

사용자 입력중에 키보드입력과 마우스입력은 가장 많이 사용됨으로 꼭 알아두실 필요가 있고 그리 어렵지 않으니 쉽게 익숙해지실 것입니다.

 

Keyboard 클래스에 대해서는 MSDN에서 개인적인 공부를 꼭 하시길 바랍니다.

http://msdn.microsoft.com/ko-kr/library/system.windows.input.keyboard.aspx

 

 

 

이번 시간은 사용자 입력의 첫번째 시간으로 키보드 입력에 대해 알아보았습니다.

다음 시간에는 계속해서 마우스를 이용한 이벤트즉 마우스 입력에 대해 알아보도록 하겠습니다.

수고하셨습니다.








 

 

 

Posted by 强太風
  1. ADC

    포커스나 논리적 포커스가 키보드에서 무슨 의미를 하는건가요??

    2009/08/04 16:48 ADDR : EDIT/ DEL : REPLY ]
    • 키보드입력을 이용해 키보드 클래스의 관련 이벤트를 사용하려면 우선 키보드입력을 받은 요소(Control)가 무엇인지 알려줘야겠죠?.. 포커스는 요소가 입력받을 준비?, 입력 받을 요소가 입력 받을 상태가 가능한 상태로 만들어주는 것입니다..

      2009/08/04 22:34 ADDR : EDIT/ DEL ]
  2. 이영민

    엔터키 예제에서... 엔터키를 누를때 마다 키 상태가 토글되는것 같은데, 제가 뭘 잘못 입력해서일까요?

    2010/08/13 10:57 ADDR : EDIT/ DEL : REPLY ]
    • 안녕하세요 제가 휴가를 다녀오느라 답변이 늦었내요..
      질문하신 내용은 Enter키를 누를때 마다 키 상태가 토글되는 것을 말씀하신것 같은데 이영민님이 잘못하신것은 없구요. Enter키가 토글 되는것은 테스트를 해보시면 아시겠지만 Enter키 뿐만 아니라 다른키 역시 key 상태가 Down -> Toggle -> Down -> Toggle 로 변경되게 되는 것을 아실 수 있을 겁니다. 예제에서 If 문을 다음과 같이 수정해보세요.. if (Keyboard.IsKeyDown(Key.Enter) == true) 그럼 Enter Key가 Down되었을때의 Key상태를 알 수 있을 것입니다.

      2010/08/30 13:36 ADDR : EDIT/ DEL ]
  3. 나며기

    강좌완 크게 관계가 없겠지만 Enter키 체크 부분을 아래와 같이 고쳐주시면 정확하게 체크가 가능합니다
    ^_^;
    (엔터키 누르고 다시 누르면 비정상 동작합니다 ㅎㅎ;)

    if((Keyboard.GetKeyStates(Key.Enter) & KeyStates.Down) > 0)

    강의 잘 듣고있습니다.

    마지막 강의를 향하여!!! 고고싱

    2012/08/23 14:47 ADDR : EDIT/ DEL : REPLY ]
  4. 다른 cs 파일에서 textBox1를 조작할려면 어떻게 해야 하나요?

    2013/03/25 16:19 ADDR : EDIT/ DEL : REPLY ]


반응형

http://goo.gl/FHk67U



마우스 더블클릭


void image_MouseDown(object sender, MouseButtonEventArgs e)
{
 if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
 {
  Close();                
 }
}










http://dotnetmvp.tistory.com/38

16. [WPF 기초 강좌] 사용자입력 2



안녕하세요.

WPF 기초강좌를 진행하는 김창민입니다.

 

 

오늘 강좌를 시작하겠습니다.

 

 

 

오늘은 사용자 입력 두번째 시간으로 마우스 입력에 대해 알아보도록 하겠습니다.

 

 

 

 

키보드 입력은 어떤 요소(컨트롤)가 포커스(Focus)되어있는냐에 따라서 이벤트를 실행하는 요소가 정해지는데 마우스 입력은 어떤 요소가 마우스 화살표 또는 포인터 바로 아래에 위치하고 있느냐에 따라 마우스 이벤트를 실행하게될 요소가 달라집니다마우스 포인터 바로 아래있는 요소가 이벤트를 받게 될 대상이 되는 것입니다.

 

마우스 입력 역시 UIElement 클래스로부터 파생되어지며 이벤트와 속성등이 제공되어집니다.

 

 

- Mouse 클래스

 

다음 표는 마우스 입력 이벤트들을 나열해 놓은 것입니다.

 

이벤트

설명

GotMouseCapture

컨트롤에 마우스 캡처 중임

LostMouseCapture

컨트롤에서 마우스 캡처를 잃음

MouseEnter

마우스 포인터가 컨트롤의 영역으로 들어왔음

MouseLeave

마우스 포인터가 컨트롤의 영역 밖으로 나감

PreviewMouseLeftButtonDown,

MouseLeftButtonDown

마우스 포인터가 컨트롤의 영역안에 있을 때 마우스 왼쪽 버튼이 눌렸을 때

PreviewMouseLeftButtonUp,

MouseLeftButtonUp

마우스 포인터가 컨트롤의 영역안에 있을 때 마우스 왼쪽 버튼이 눌렸다 떼어졌을 때

PreviewMouseRightButtonDown,

MouseRightButtonDown

마우스 포인터가 컨트롤의 영역안에 있을 때 마우스 오른쪽 버튼이 눌렸을 때

PreviewMouseRightButtonUp,

MouseRighttButtonUp

마우스 포인터가 컨트롤의 영역안에 있을 때 마우스 오른쪽 버튼이 눌렸다 떼어졌을 때

PreviewMouseDown,

MouseDown

마우스 포인터가 컨트롤의 영역안에 있을 때 마우스 버튼이 눌렸을 때

PreviewMouseUp,

MouseUp

마우스 포인터가 컨트롤의 영역안에 있을 때 마우스 버튼이 눌렸다 떼어졌을 때

PreviewMouseMove,

MouseMove

마우스 포인터가 컨트롤 영역내어서 움직였을 때

PreviewmouseWheel,

MouseWheel

마우스 포인터가 컨트롤 영역내에 있으면서 마우스 휠이 움직였을 때

QueryCursor

마우스 포인터가 컨트롤 영역내에 있는 동안 마우스 포인터의 모양이 결정되었을 때

 

 

위 이벤트들은 아마 특별히 설명을 하지 않아도 많이 사용하고 많이 보아왔기 때문에 어떤 이벤트들인지 휜히 다 아시리라 생각합니다.

위 표에있는 이벤트들 말고도 Mouse클래스에는 몇가지 속성들과 메서드를을 제공해 줍니다.

 

다음 예제를 통해서 몇가지 이벤트메서드속성에 대해 설명을 진행하겠습니다.

 

우선 VisualStudio 2008을 실행시켜 “MouseInput”이라는 새 프로젝트를 생성합니다.

그리고 프로젝트에 생성된 “Window1”을 “MouseEvent”로 변경하겠습니다.

 

 

이번 예제에서는 Mouse의 이벤트 중 MouseDown, MouseMove, MouseUp, MouseRightButtonDown 이벤트를 등록해 사용하고 Mouse Capture, GetPosition 메서드에 대해 알아보도록 하겠습니다.

 

 

 

다음으로 MouseEvent.xaml Ellipse 컨트롤과 Button 컨트롤, Label 컨트롤을 Window Grid 안에 배치하겠습니다.

 

 

<!-- MouseEvent.xaml -->

<Window x:Class="MouseInput.MouseEvent"

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

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

    Title="Window1" Height="300" Width="300">

    <Grid>

        <Ellipse Margin="70,47,72,79" Name="ellipse1" Stroke="Black" Fill="Red" />

        <Label Height="28" Margin="0,0,0,12" Name="label1" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" >Label</Label>

<Button Name="button1" Height="23" HorizontalAlignment="Right" VerticalAlignment="Top" Width="75">Button</Button>

    </Grid>

</Window>

 

 

다음은 “MouseEvent.xaml.cs”로 이동하여 이벤트를 등록하고 핸들러부분에 실행될 코드를 작성하겠습니다.

 

 

<!-- MouseEvent.xaml.cs -->

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

 

namespace MouseInput

{

    public partial class MouseEvent : Window

    {

        public MouseEvent()

        {

            InitializeComponent();   

            ellipse1.MouseDown += new MouseButtonEventHandler(ellipse1_MouseDown);

            ellipse1.MouseMove += new MouseEventHandler(ellipse1_MouseMove);

            ellipse1.MouseUp += new MouseButtonEventHandler(ellipse1_MouseUp);

            this.MouseRightButtonDown += new MouseButtonEventHandler(Window1_MouseRightButtonDown);

        }

 

        void ellipse1_MouseDown(object sender, MouseButtonEventArgs e)

        {

            label1.Content = "MouseDown";

            Mouse.Capture(ellipse1);

        }

 

        void ellipse1_MouseMove(object sender, MouseEventArgs e)

        {

            label1.Content = "MouseMove" + Mouse.GetPosition(ellipse1);

        }

 

        void ellipse1_MouseUp(object sender, MouseButtonEventArgs e)

        {

            label1.Content = "MouseUp" + Mouse.GetPosition(ellipse1);

        }

 

        void Window1_MouseRightButtonDown(object sender, MouseButtonEventArgs e)

        {

            Mouse.Capture(null);

        }

 

        private void button1_Click(object sender, RoutedEventArgs e)

        {

            if (ellipse1.Fill == Brushes.Red)

                ellipse1.Fill = Brushes.Blue;

            else

                ellipse1.Fill = Brushes.Red;

        }

    }

}

 

 

 

위 코드에서 Window생성자 부분에 이벤트를 등록하는 코드가 있지요?..


ellipse1.MouseDown += new MouseButtonEventHandler(ellipse1_MouseDown);


 

 

전에 강좌를 통해 이벤트를 코드에서 자동등록하는 방법을 설명드렸는데 혹시 모르시거나 잊어버리신 분들이 있으까봐 다시 간단히 설명해 드리겠습니다. 

 

코드에서 이벤트를 등록하려할 때 이벤트를 발생시킬 요소의 아이디를 치시고 ‘.’을 치면 IntelleSence기능이 작동하여 코드를 쉽게 입력할 수 있을 것입니다.




 

IntelleSence 창에서 원하는 코드를 찾는데 저는 ellipse1 MouseDown이벤트를 만들어주기 위해 MouseDown 이벤트를 찾아 선택을 해주겠습니다.





 

다음으로 “ellipse1.MouseDown” 이 코드 뒤에 이벤트 핸들러를 연결해 주기위해 +=” 를 입력하면 다음과 같은 설명이 나오게 될 것입니다.

 





우리는 생각 할 것 없이 시키는데로 “Tab”키를 눌러주면 됩니다.

그러면 코드가 자동으로 생성이되며 이벤트 핸들러 메서드를 작성할 것이라는 안내문이 나옵니다.

 

 

 

 

그럼 또 우린 생각할 것 없이  “Tab”키를 한번 더 눌러줍니다.

ㅋㅋ 너무나도 친절하게 다음과 같은 이벤트 핸들러 역시 자동으로 생성되어지는 것을 보실 수 있을 것입니다.




이 처럼 VisualStudio에서는 자동으로 이벤트를 쉽게 등록할 수 있는 기능을 제공해 줌으로 써 개발자가 좀 더 쉽고 간단하게 이벤트를 만들어 사용할 수 있도록 도와 주고 있습니다.

 

 

그럼 다시 본론으로 들어가서 위에 작성한 코드를 잠깐 같이 보고 실행 결과를 보도록 하겠습니다.


코드를 보시면 “ellipse1.MouseDown” 이벤트 핸들러를 보시면 Mouse.Capture(ellipse1);” 이라는 코드가 있습니다마우스의 캡처는 요소(컨트롤)가 모든 마우스의 입력 명령을 받는 시점을 나타내는데 이말은 요소를 캡처하면 마우스 포인터가 요소의 영역안에 있던지 관계없이 마우스의 입력을 받게됩니다. 그러면서 다른 요소즉 최상위 요소인 Window까지 Lock이 걸린 것 같은 현상을 보실 수 있습니다.

다음으로 MouseMove, MouseUp 이벤트 핸들러의 코드를 보시면 Mouse.GetPosition(ellipse1)”라는 메서드를 이용하여 마우스 포인터가 있는 위치 좌표를 Label에 표시해 주게 되어있습니다그런데 메서드의 인자로 “ellipse1”이란 요소를 넘겨주게 되어있는데 이는 “GetPosition”메서드에서 마우스 포인터를 좌표상으로 나타내는데 그 기준을 위에 인자로 넘긴 요소를 기준으로 좌표를 나타내게 된다는 것을 의미하게 됩니다.

그리고 Window의 “MouseRightButtonDown” 이벤트 핸들러에서는 “ellipse”에서 설정한 캡처를 해제함을 나타낸는 코드로 되어있습니다.

Button컨트롤의 역할은 마우스 캡처가 되었을 때 버튼 이벤트가 실행되는 지를 보기 위함으로 버튼이 눌려지게 된다면 ellipse의 배경색을 바꾸어주는 역할을 하게 됩니다.

 

그럼 빌드하여 실행을 시켜 보겠습니다.

(혹시 제 강좌를 보시던 분들 중에 App.xaml파일의 StartupUri를 변경하지 않고 실행이 안되신다고 하시는 분들은 이제 없으시겠죠?~ ㅋㅋ)





위와 같은 실행 창이 나오면 우선 버튼(ID:button1)이 정상적으로 동작을 하는지 버튼을 클릭해 보겠습니다.

 






원의 배경색이 파란색빨간색으로 변하는 것으로 보아 버튼 클릭 이벤트는 정삭적으로 동작하는 것을 알 수 있습니다.

 

그럼 이제 Mouse.Capture를 하기위해 빨간색 원(ID:ellipse1)을 마우스로 포인터를 가져가 보겠습니다.







마우스 포인터는 원안으로 가져가자 밑에 있는 Label에 “MouseMove”라는 글과 함께 좌표가 표시됨을 알 수 있습니다그런데 원 밖으로 마우스 포인터를 가져가면 원 밖에서는 마우스가 움직이는 동안 MouseMove이벤트는 동작하지 않는 것알 수 있습니다현재는 “MouseMove” 이벤트는 요소 영역안에 있을 때만 동작하게 됩니다.

다시 마우스 포인터는 원안으로 가져가서 원을 클릭해 보겠습니다.








원안에서 마우스를 클릭하자 Label에 “MouseUp”이란 글자와 좌표가 표시됩니다.

MouseUp은 원 안에서 마우스가 “Down”되었다가 바로 “Up”이 되자 “MouseUp”이벤트가 실행된 결과입니다.

우리는 “MouseDown” 이벤트에서 “ellipes1” 요소를 캡처하는 동작을 지정하였습니다.

당연히 마우스가 클릭되는 동작에서 MouseDown 이벤트도 실행이 되었겠지요?~


마우스캡처의 동작을 보기위해 버튼에 마우스 포인터를 이동해 보겠습니다.






그런데 한가지

 

위에 결과에서 뭔가 이상한 것이 있지 않은 가요?...

그건 바로 전에 원 밖에서 마우스 포인터이 이동했을 경우에는 MouseMove이벤트가 실행이 되지 않았는데 지금은 원 밖에서 마우스 포인터를 이동시켜도 MouseMove이벤트가 실행이 되어 Label의 좌표가 계속 변경됨을 알 수 있습니다.

처음에 마우스 캡처를 설명한 부분을 잘 기억하고 있으시다면 눈치 채셨을 것입니다.

마우스의 캡처는 요소(컨트롤)가 모든 마우스의 입력 명령을 받는 시점을 나타내는데 이말은 요소를 캡처하면 마우스 포인터가 요소의 영역안에 있던지 관계없이 마우스의 입력을 받게됨니다.” 라고 위부분에 설명을 해 놓았습니다이것으로만 봐도 현재 마우스 요소가 캡처되있는 상태임을 알 수 있습니다.

그럼 계속해서 버튼을 클릭해 보겠습니다.






버튼이 클릭이 되십니까?... 

버튼을 클릭하면서 밑에 Label을 보세요. Label에 “MouseDown”, “MouseUp”이란 글자가 변경되는 것으로보아 마우스는 클릭이 되어지는 것을 알 수 있습니다.

하지만 버튼은 클릭 되어지지 않게 됩니다.

이것은 지금 “ellipse1” 요소가 캡처되어 있는 상태이기 때문에 모든 마우스 이벤트는 “ellipse1”이 다 받게 되어있기 때문입니다.

 

간단한 예제를 통해서 “Mouse.Capture”를 알아보았는데 쉽게 이해가 되셨는지 모르겠습니다.

아직 실행하지 않은 이벤트가 하나 있는데 “MouseRightButtonDown”이벤트 입니다.

결과 창에서 마우스 오른쪽을 클릭해서 마우스 캡처를 해제한 다음에 원 밖에서의 “MouseMove”, 버튼의 클릭이 정상적으로 되는지 확인해 보세요

 

 

 

 

이번 예제는 IsMouseOver 속성과 IsMouseDirectlyOver속성에 대해 알아보고 마우스 휠 이벤트에 대한 예제를 만들어보겠습니다.

 

예제를 만들기전에 간단히 설명을 진행하고 예제의 결과를 보고 이해하는 순서로 진행을 이어가겠습니다.

IsMouseOver 속성과 IsMouseDirectlyOver은 공통점이 있습니다그것은 마우스 포인터가 현재 요소 영역안에 위치하고 있는지의 여부를 Boolean 값으로 반환해줍니다.

다시말해 요소영역안에 마우스 포인터가 위치한다면 true, 그렇지 않으면 false를 반환해 준다는 것입니다.

그러나 이둘은 약간의 차이점이 있습니다. IsMouseOver 속성은 요소의 영역안에 마우스 포인터가 위치하면 true를 반환합니다요소안에 자식 요소가 있어서 마우스 포인터가 자식요소 영역안에 있다 하더라도 true를 반환한다는 것입니다.

하지만 IsMouseDirectlyOver속성은 다릅니다이 속성은 요소의 자식요소 영역안에 마우스 포인터가 위치한다면 false를 반환하게 됩니다다시말해 현재 마우스 포인터가 자식요소가 아닌 자기자신 요소 위에 있어야만 true를 반환하게 됩니다.

 

그리고 휠 이벤트는 마우스 휠이 움직일 때 발생하는 이벤트인데 MouseWheel 이벤트 핸들러의 MouseWheelEventArgs에서 “Delta” 값을 보고 휠이 위로 움직였는지 아래로 움직였는지를 알 수 있습니다.

 

그럼 다음 예제를 작성하고 실행해 결과를 보세요..

 

 

기존 프로젝트에 “MouseEvent1”이라는 이름으로 새 창을 추가하겠습니다.









그런 다음 “MouseEvent1.xaml”페이지에 디자인을 하겠습니다.

 

 

<!—MouseEvent1.xaml -->

<Window x:Class="MouseInput.MouseEvent1"

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

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

    Title="MouseEvent1" Height="300" Width="300" MouseWheel="Window_MouseWheel">

    <Grid>

        <Button x:Name="button1" Margin="24,37,0,129" HorizontalAlignment="Left" Width="107" MouseEnter="button1_MouseEnter" MouseLeave="button1_MouseLeave" >

            <Ellipse x:Name="ellipse1" Height="40" Width="40" Fill="Red" MouseEnter="Ellipse_MouseEnter" MouseLeave="ellipse1_MouseLeave" />

        </Button>

        <Button x:Name="button2" Margin="0,37,25,129" HorizontalAlignment="Right" Width="107" MouseEnter="button2_MouseEnter" MouseLeave="button2_MouseLeave" >

            <Rectangle x:Name="Rectangle1" Height="40" Width="40" Fill="Blue" MouseEnter="Rectangle_MouseEnter" MouseLeave="Rectangle1_MouseLeave" />

        </Button>

        <Label Margin="0,0,0,69" Name="label1" FontSize="16" HorizontalContentAlignment="Center" Height="42" VerticalAlignment="Bottom">Label</Label>

        <Label FontSize="18" Height="42" HorizontalContentAlignment="Center" Name="label2" VerticalAlignment="Bottom">0</Label>

    </Grid>

</Window>

 

 

 

그리고 “MouseEvent1.xaml.cs” 페이지에 다음 코드를 작성합니다.

 

 

<!-- MouseEvent.xaml.cs -->

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Shapes;

 

namespace MouseInput

{

    public partial class MouseEvent1 : Window

    {

        public MouseEvent1()

        {

            InitializeComponent();           

        }

 

        private void button1_MouseEnter(object sender, MouseEventArgs e)

        {

            label1.Content = "button1 MouseOver : " + button1.IsMouseOver.ToString();

        }

 

        private void Ellipse_MouseEnter(object sender, MouseEventArgs e)

        {

            label1.Content = "Ellipse MouseOver : " + ellipse1.IsMouseOver.ToString();

        }

 

        private void button2_MouseEnter(object sender, MouseEventArgs e)

        {

            label1.Content = "button2 MouseDirectlyOver : " + button2.IsMouseDirectlyOver.ToString();

        }

 

        private void Rectangle_MouseEnter(object sender, MouseEventArgs e)

        {

            label1.Content = "Rectangle1 MouseDirectlyOver : " + Rectangle1.IsMouseDirectlyOver.ToString();

        }

 

        private void button1_MouseLeave(object sender, MouseEventArgs e)

        {

            label1.Content = "";

        }

 

        private void button2_MouseLeave(object sender, MouseEventArgs e)

        {

            label1.Content = "";

        }

 

        private void ellipse1_MouseLeave(object sender, MouseEventArgs e)

        {

            label1.Content = "button1 MouseOver : " + button1.IsMouseOver.ToString();

        }

 

        private void Rectangle1_MouseLeave(object sender, MouseEventArgs e)

        {

            label1.Content = "button2 MouseDirectlyOver : " + button2.IsMouseDirectlyOver.ToString();

        }

 

        private void Window_MouseWheel(object sender, MouseWheelEventArgs e)

        {

            if (e.Delta == 120)

            {

                label2.Content = Convert.ToInt32(label2.Content) + 1;

            }

            else if (e.Delta == -120)

            {

                label2.Content = Convert.ToInt32(label2.Content) - 1;

            }

        }

 

    }

}

 

 

 

간단히 코드 설명을 드리자면 디자인에는 두개의 Button과 두개의 Label이 배치되어 있습니다.

그리고 두개의 버튼에는 각각 Rectangle Ellipse가 자식요소로 포함이 되어있습니다.

버튼들과 그 자식요소에는 “MouseEnter” 이벤트 들이 선언되어있어 마우스 포인터가 요소위에 올라가면 이벤트를 실행 시킬 것이고 그 이벤트안에는 IsMouseOver 속성과 IsMouseDirectlyOver속성의 반환 값을 label1에 보여주게 되있습니다.

그리고 마우스 휠 움직일 때는 lable2 1씩 증가, 1씩 감소하는 코드를 작성해 두었습니다.

그럼 App.xaml 파일을 수정하시여 결과를 보도록 하겠습니다..

 



IsMouseOver 속성




IsMouseDirectlyOver속성




MouseWhee 이벤트





이제 이해가 되시죠?...

 

정말 긴 강좌였습니다.

그리 어렵지않은 내용이였지만 나름 할 말이 많았었내요...

 

Mouse 클래스 역시 여럽지 않은 개념임으로 MSDN에서 꼭 한번 읽고 넘어 가시길 바랍니다.

http://msdn.microsoft.com/ko-kr/library/system.windows.input.mouse_members.aspx 

 

 

 

 

이번 시간까지 사용자 입력의 키보드 입력과 마우스 입력에 대해 알아보았습니다.

그리고 사용자 입력중에 하나인 스타일러스 펜 입력이 있는데 이 내용은 Tablet PC나 Digitizing PC 에서 테스트 가능한 기능임으로 현재 강좌에서는 진행을 하지 않으려 합니다.

 

다음 시간에는 처음 강좌를 시작할 때 WPF시작하기라는 주제로 잠깐 훌터보고 넘어갔단 컨트롤들에 대해 좀 더 구체적으로 알아보는 시간을 갖도록 하겠습니다.

그리고 앞으로 진행될 강좌는 가능하면 Expression Blend를 사용하여 컨트롤의 모양을 변경한다던지 개발자로서 간단히 할 수 있는 디자인 예제를 함께 만들어보는 시간도 많이 갖도록 하겠습니다.

   

긴 강좌 함께 하시느라 수고하셨습니다











Posted by 强太風
  1. 박성욱

    좋은 강좌 감사합니다. 
    그런데 이번 isMouseDirectlyOver 예제는 조금 동작이 다른 것 같아요.
    button1 위에 올라섰을때 동작은 맞고 , button1의 자식요소인 ellips1에 마우스가 올라가면,
    라벨에는 "Ellipse MouseOver : true"가 표시됩니다. 
    또한 button2 위에 올라섰을 때는 "button2 MouseDirectlyOver : false"가 표시되고 그 자식요소 rectangle1에 마우스가 올라가면, "Rectangle1 MouseDirectlyOver : true"가 표시됩니다. 
    cs소스의 이벤트처리 안의 내용을 조금 바꿔서 자식요소의 이벤트에서도 button의 isMouseDirectlyOver 를 참조 해 보아도 false이고요.
    기본적으로 button2에 마우스가 올라가서 자식요소 안까지 가지 않아도 false가 나오는 것이 이상합니다.
    자식요소 위에서 마우스를 다운한 상태를 유지 해 주면 true가 나오네요.


반응형

http://hewoo.tistory.com/5




3. CompositionTarget의 Redering 이벤트 핸들러 이용

마지막으로 CompositionTarget의 Rendering 이벤트 핸들러는 이용하는 방법이 있다. 
이것은 모니터의 수직 주파수 보다 빠른 랜더링을 요구 할때 DispatcherTimer를 포기 하고 쓰는 방법이다. 
이것도 예제 코드를 보면서 이야기 하겠다. 

 public Window1()

        {

            InitializeComponent();

            InitUI();

            SetCompositionTarget();

        }

 

 

        private void SetCompositionTarget()

        {

            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);

        }

 

        void CompositionTarget_Rendering(object sender, EventArgs e)

        {

            Canvas.SetLeft(MyEllipseCanvas, XPos);

 

            if (XPos > this.Width)

                XPos = 0;

            else

                XPos += 0.5;

        }


엄청 간단하다.^^

CompositonTarget은 System.Windows.Media 네임 스페이스를 쓰면 이용가능하다.
한가지 특이한 사실은 DispatcherTimer를 쓸때는 Thread.Sleep(1); 해줘야 CPU 점유율이 떨어지는데, 
이건 Sleep를 쓰지 않아도 CPU 점유율이 높이않다. (오히려 DispatcherTimer 쓸때 보다 낮다.)

그렇다면  CompositonTarget은 어디서 온걸까?
바로 WPF가 화면에 객체를 뿌리는 Reder 방식인 것이다. 
WPF를 내부적으로 보면 화면에 GPU 가속을 이용하여 Thread 형식으로 오브젝트를 계속 뿌린다. 
GDI 방식과 차이가 있다. 그렇기 때문에 화면에 뿌려지는 속도가 매우 빠르다는 것을 알 수 있다.


이제 까지 3가지 애니메이션 시키는 방법에 대해 알아 보았다.
사용은 여러분 자유다.^^


추신 : 전체 소스코드를 같이 올린다. 참고하시길~!

반응형

+ Recent posts