첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

728x90
반응형

■ ContentControl 클래스 : 텍스트 리스트 컨트롤 만들기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip


ListControl.cs

 

 

using System;

using System.Collections;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

 

namespace TestProject

{

    /// <summary>

    /// 리스트 컨트롤

    /// </summary>

    public class ListControl : ContentControl

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Declaration

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Event

 

        /// <summary>

        /// 선택 변경시

        /// </summary>

        public event EventHandler SelectionChanged;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 스크롤 뷰어

        /// </summary>

        private ScrollViewer scrollViewer;

 

        /// <summary>

        /// 스택 패널

        /// </summary>

        private StackPanel stackPanel;

 

        /// <summary>

        /// 배열 리스트

        /// </summary>

        private ArrayList arrayList = new ArrayList();

 

        /// <summary>

        /// 선택 인덱스

        /// </summary>

        private int selectedIndex = -1;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 수 - Count

 

        /// <summary>

        ///

        /// </summary>

        public int Count

        {

            get

            {

                return this.arrayList.Count;

            }

        }

 

        #endregion

        #region 선택 인덱스 - SelectedIndex

 

        /// <summary>

        /// 선택 인덱스

        /// </summary>

        public int SelectedIndex

        {

            set

            {

                if(value < -1 || value >= Count)

                {

                    throw new ArgumentOutOfRangeException("SelectedIndex");

                }

 

                if(value == this.selectedIndex)

                {

                    return;

                }

 

                if(this.selectedIndex != -1)

                {

                    TextBlock textBlock = this.stackPanel.Children[this.selectedIndex] as TextBlock;

 

                    textBlock.Background = SystemColors.WindowBrush;

                    textBlock.Foreground = SystemColors.WindowTextBrush;

                }

 

                this.selectedIndex = value;

 

                if(this.selectedIndex > -1)

                {

                    TextBlock textBlock = this.stackPanel.Children[this.selectedIndex] as TextBlock;

 

                    textBlock.Background = SystemColors.HighlightBrush;

                    textBlock.Foreground = SystemColors.HighlightTextBrush;

                }

 

                ScrollIntoView();

 

                OnSelectionChanged(EventArgs.Empty);

            }

            get

            {

                return this.selectedIndex;

            }

        }

 

        #endregion

        #region 선택 항목 - SelectedItem

 

        /// <summary>

        /// 선택 항목

        /// </summary>

        public object SelectedItem

        {

            set

            {

                SelectedIndex = this.arrayList.IndexOf(value);

            }

            get

            {

                if(SelectedIndex > -1)

                {

                    return this.arrayList[SelectedIndex];

                }

 

                return null;

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - ListControl()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public ListControl()

        {

            Focusable = false;

 

            Border border = new Border();

 

            border.BorderThickness = new Thickness(1);

            border.BorderBrush     = SystemColors.ActiveBorderBrush;

            border.Background      = SystemColors.WindowBrush;

 

            Content = border;

 

            this.scrollViewer = new ScrollViewer();

 

            this.scrollViewer.Focusable = false;

            this.scrollViewer.Padding   = new Thickness(2, 0, 0, 0);

 

            border.Child = this.scrollViewer;

 

            this.stackPanel = new StackPanel();

 

            this.scrollViewer.Content = this.stackPanel;

 

            AddHandler

            (

                TextBlock.MouseLeftButtonDownEvent,

                new MouseButtonEventHandler(textBlock_MouseLeftButtonDown)

            );

 

            Loaded += ListControl_Loaded;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 지우기 - Clear()

 

        /// <summary>

        /// 지우기

        /// </summary>

        public void Clear()

        {

            SelectedIndex = -1;

 

            this.stackPanel.Children.Clear();

 

            this.arrayList.Clear();

        }

 

        #endregion

        #region 추가하기 - Add(value)

 

        /// <summary>

        /// 추가하기

        /// </summary>

        /// <param name="value"></param>

        public void Add(object value)

        {

            this.arrayList.Add(value);

 

            TextBlock textBlock = new TextBlock();

 

            textBlock.Text = value.ToString();

 

            this.stackPanel.Children.Add(textBlock);

        }

 

        #endregion

        #region 삽입하기 - Insert(index, value)

 

        /// <summary>

        /// 삽입하기

        /// </summary>

        /// <param name="index">인덱스</param>

        /// <param name="value"></param>

        public void Insert(int index, object value)

        {

            this.arrayList.Insert(index, value);

 

            TextBlock textBlock = new TextBlock();

 

            textBlock.Text = value.ToString();

 

            this.stackPanel.Children.Insert(index, textBlock);

        }

 

        #endregion

        #region 포함 여부 조사하기 - Contains(value)

 

        /// <summary>

        /// 포함 여부 조사하기

        /// </summary>

        /// <param name="value"></param>

        /// <returns>포함 여부</returns>

        public bool Contains(object value)

        {

            return this.arrayList.Contains(value);

        }

 

        #endregion

        #region 글자로 이동하기 - GoToLetter(character)

 

        /// <summary>

        /// 글자로 이동하기

        /// </summary>

        /// <param name="character">문자</param>

        public void GoToLetter(char character)

        {

            int offset = SelectedIndex + 1;

 

            for(int i = 0; i < Count; i++)

            {

                int index = (i + offset) % Count;

 

                if(Char.ToUpper(character) == Char.ToUpper(this.arrayList[index].ToString()[0]))

                {

                    SelectedIndex = index;

 

                    break;

                }

            }

        }

 

        #endregion

        #region 페이지 위로 이동하기 - PageUp()

 

        /// <summary>

        /// 페이지 위로 이동하기

        /// </summary>

        public void PageUp()

        {

            if(SelectedIndex == -1 || Count == 0)

            {

                return;

            }

 

            int index = SelectedIndex - (int)(Count * this.scrollViewer.ViewportHeight / this.scrollViewer.ExtentHeight);

 

            if(index < 0)

            {

                index = 0;

            }

 

            SelectedIndex = index;

        }

 

        #endregion

        #region 페이지 아래로 이동하기 - PageDown()

 

        /// <summary>

        /// 페이지 아래로 이동하기

        /// </summary>

        public void PageDown()

        {

            if(SelectedIndex == -1 || Count == 0)

            {

                return;

            }

 

            int index = SelectedIndex + (int)(Count * this.scrollViewer.ViewportHeight / this.scrollViewer.ExtentHeight);

 

            if(index > Count - 1)

            {

                index = Count - 1;

            }

 

            SelectedIndex = index;

        }

 

        #endregion

        #region 선택 항목 보이기 - ScrollIntoView()

 

        /// <summary>

        /// 선택 항목 보이기

        /// </summary>

        public void ScrollIntoView()

        {

            if(Count == 0 || SelectedIndex == -1 || this.scrollViewer.ViewportHeight > this.scrollViewer.ExtentHeight)

            {

                return;

            }

 

            double itemHeight       = this.scrollViewer.ExtentHeight / Count;

            double itemTopOffset    = SelectedIndex * itemHeight;

            double itemBottomOffset = (SelectedIndex + 1) * itemHeight;

 

            if(itemTopOffset < this.scrollViewer.VerticalOffset)

            {

                this.scrollViewer.ScrollToVerticalOffset(itemTopOffset);

            }

            else if(itemBottomOffset > this.scrollViewer.VerticalOffset + this.scrollViewer.ViewportHeight)

            {

                this.scrollViewer.ScrollToVerticalOffset

                (

                    this.scrollViewer.VerticalOffset + itemBottomOffset - this.scrollViewer.VerticalOffset - this.scrollViewer.ViewportHeight

                );

            }

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 선택 변경시 처리하기 - OnSelectionChanged(e)

 

        /// <summary>

        /// 선택 변경시 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected virtual void OnSelectionChanged(EventArgs e)

        {

            if(SelectionChanged != null)

            {

                SelectionChanged(this, e);

            }

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 리스트 컨트롤 로드시 처리하기 - ListControl_Loaded(sender, e)

 

        /// <summary>

        /// 리스트 컨트롤 로드시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void ListControl_Loaded(object sender, RoutedEventArgs e)

        {

            ScrollIntoView();

        }

 

        #endregion

        #region 텍스트 블럭 마우스 왼쪽 버튼 DOWN 처리하기 - textBlock_MouseLeftButtonDown(sender, e)

 

        /// <summary>

        /// 텍스트 블럭 마우스 왼쪽 버튼 DOWN 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void textBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            if(e.Source is TextBlock)

            {

                SelectedIndex = this.stackPanel.Children.IndexOf(e.Source as TextBlock);

            }

        }

 

        #endregion

    }

}

 

 

TextListControl.cs

 

 

using System;

using System.Windows.Controls;

using System.Windows.Input;

 

namespace TestProject

{

    /// <summary>

    /// 텍스트 리스트 컨트롤

    /// </summary>

    public class TextListControl : ContentControl

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Declaration

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Event

 

        /// <summary>

        /// 텍스트 변경시

        /// </summary>

        public event TextChangedEventHandler TextChanged;

 

        /// <summary>

        /// 선택 변경시

        /// </summary>

        public event EventHandler SelectionChanged;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 텍스트 상자

        /// </summary>

        private TextBox textBox;

 

        /// <summary>

        /// 리스트 컨트롤

        /// </summary>

        private ListControl listControl;

 

        /// <summary>

        /// 읽기 전용 여부

        /// </summary>

        private bool isReadOnly;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 텍스트 - Text

 

        /// <summary>

        /// 텍스트

        /// </summary>

        public string Text

        {

            get

            {

                return this.textBox.Text;

            }

            set

            {

                this.textBox.Text = value;

            }

        }

 

        #endregion

        #region 읽기 전용 여부 - IsReadOnly

 

        /// <summary>

        /// 읽기 전용 여부

        /// </summary>

        public bool IsReadOnly

        {

            get

            {

                return this.isReadOnly;

            }

            set

            {

                this.isReadOnly = value;

 

                this.textBox.IsReadOnly = this.isReadOnly;

            }

        }

 

        #endregion

        #region 선택 인덱스 - SelectedIndex

 

        /// <summary>

        /// 선택 인덱스

        /// </summary>

        public int SelectedIndex

        {

            get

            {

                return this.listControl.SelectedIndex;

            }

            set

            {

                this.listControl.SelectedIndex = value;

 

                if(this.listControl.SelectedIndex == -1)

                {

                    this.textBox.Text = string.Empty;

                }

                else

                {

                    this.textBox.Text = this.listControl.SelectedItem.ToString();

                }

            }

        }

 

        #endregion

        #region 선택 항목 - SelectedItem

 

        /// <summary>

        /// 선택 항목

        /// </summary>

        public object SelectedItem

        {

            get

            {

                return this.listControl.SelectedItem;

            }

            set

            {

                this.listControl.SelectedItem = value;

 

                if(this.listControl.SelectedItem != null)

                {

                    this.textBox.Text = this.listControl.SelectedItem.ToString();

                }

                else

                {

                    this.textBox.Text = string.Empty;

                }

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - TextListControl()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public TextListControl()

        {

            DockPanel dockPanel = new DockPanel();

 

            Content = dockPanel;

 

            this.textBox = new TextBox();

 

            this.textBox.TextChanged += this.textBox_TextChanged;

 

            dockPanel.Children.Add(this.textBox);

 

            DockPanel.SetDock(this.textBox, Dock.Top);

 

            this.listControl = new ListControl();

 

            this.listControl.SelectionChanged += this.listControl_SelectionChanged;

 

            dockPanel.Children.Add(this.listControl);

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 지우기 - Clear()

 

        /// <summary>

        /// 지우기

        /// </summary>

        public void Clear()

        {

            this.listControl.Clear();

        }

 

        #endregion

        #region 추가하기 - Add(value)

 

        /// <summary>

        /// 추가하기

        /// </summary>

        /// <param name="value"></param>

        public void Add(object value)

        {

            this.listControl.Add(value);

        }

 

        #endregion

        #region 삽입하기 - Insert(index, value)

 

        /// <summary>

        /// 삽입하기

        /// </summary>

        /// <param name="index">인덱스</param>

        /// <param name="value"></param>

        public void Insert(int index, object value)

        {

            this.listControl.Insert(index, value);

        }

 

        #endregion

        #region 포함 여부 조사하기 - Contains(value)

 

        /// <summary>

        /// 포함 여부 조사하기

        /// </summary>

        /// <param name="value"></param>

        /// <returns>포함 여부</returns>

        public bool Contains(object value)

        {

            return this.listControl.Contains(value);

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

 

        #region 마우스 DOWN 처리하기 - OnMouseDown(e)

 

        /// <summary>

        /// 마우스 DOWN 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnMouseDown(MouseButtonEventArgs e)

        {

            base.OnMouseDown(e);

 

            Focus();

        }

 

        #endregion

        #region 키보드 포커스 획득시 처리하기 - OnGotKeyboardFocus(e)

 

        /// <summary>

        /// 키보드 포커스 획득시 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)

        {

            base.OnGotKeyboardFocus(e);

 

            if(e.NewFocus == this)

            {

                this.textBox.Focus();

 

                if(SelectedIndex == -1 && this.listControl.Count > 0)

                {

                    SelectedIndex = 0;

                }

            }

        }

 

        #endregion

        #region 텍스트 입력시 사전 처리하기 - OnPreviewTextInput(e)

 

        /// <summary>

        /// 텍스트 입력시 사전 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnPreviewTextInput(TextCompositionEventArgs e)

        {

            base.OnPreviewTextInput(e);

 

            if(IsReadOnly)

            {

                if(e.Text.Length > 0)

                {

                    this.listControl.GoToLetter(e.Text[0]);

 

                    e.Handled = true;

                }

            }

        }

 

        #endregion

        #region 키 DOWN 사전 처리하기 - OnPreviewKeyDown(e)

 

        /// <summary>

        /// 키 DOWN 사전 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected override void OnPreviewKeyDown(KeyEventArgs e)

        {

            base.OnPreviewKeyDown(e);

 

            if(SelectedIndex == -1)

            {

                return;

            }

 

            switch(e.Key)

            {

                case Key.Home :

 

                    if(this.listControl.Count > 0)

                    {

                        SelectedIndex = 0;

                    }

 

                    break;

 

                case Key.End :

 

                    if(this.listControl.Count > 0)

                    {

                        SelectedIndex = this.listControl.Count - 1;

                    }

 

                    break;

 

                case Key.Up :

 

                    if(SelectedIndex > 0)

                    {

                        SelectedIndex--;

                    }

 

                    break;

 

                case Key.Down :

 

                    if(SelectedIndex < this.listControl.Count - 1)

                    {

                        SelectedIndex++;

                    }

 

                    break;

 

                case Key.PageUp :

 

                    this.listControl.PageUp();

 

                    break;

 

                case Key.PageDown :

 

                    this.listControl.PageDown();

 

                    break;

 

                default :

 

                    return;

            }

 

            e.Handled = true;

        }

 

        #endregion

        #region 선택 변경시 처리하기 - OnSelectionChanged(e)

 

        /// <summary>

        /// 선택 변경시 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected virtual void OnSelectionChanged(EventArgs e)

        {

            if(SelectionChanged != null)

            {

                SelectionChanged(this, e);

            }

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region 텍스트 상자 텍스트 변경시 처리하기 - textBox_TextChanged(sender, e)

 

        /// <summary>

        /// 텍스트 상자 텍스트 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void textBox_TextChanged(object sender, TextChangedEventArgs e)

        {

            if(TextChanged != null)

            {

                TextChanged(this, e);

            }

        }

 

        #endregion

        #region 리스트 컨트롤 선택 변경시 처리하기 - listControl_SelectionChanged(sender, e)

 

        /// <summary>

        /// 리스트 컨트롤 선택 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void listControl_SelectionChanged(object sender, EventArgs e)

        {

            if(SelectedIndex == -1)

            {

                this.textBox.Text = string.Empty;

            }

            else

            {

                this.textBox.Text = this.listControl.SelectedItem.ToString();

            }

 

            OnSelectionChanged(e);

        }

 

        #endregion

    }

}

 

 

MainWindow.xaml

 

 

<Window x:Class="TestProject.MainWindow"

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

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

    xmlns:local="clr-namespace:TestProject"

    Width="800"

    Height="600"

    Title="ContentControl 클래스 : 텍스트 리스트 컨트롤 만들기"

    FontFamily="나눔고딕코딩"

    FontSize="16">

    <Grid Margin="10">

        <StackPanel

            HorizontalAlignment="Center"

            VerticalAlignment="Center">

            <local:TextListControl x:Name="listControl"

                Margin="10"

                Width="200"

                Height="300" />

            <UniformGrid

                Margin="10"

                Columns="4">

                <Button Name="clearButton"

                    Margin="10"

                    Width="100"

                    Height="30"

                    Content="지우기" />

                <Button Name="addButton"

                    Margin="10"

                    Width="100"

                    Height="30"

                    Content="추가하기" />

                <Button Name="insertButton"

                    Margin="10"

                    Width="100"

                    Height="30"

                    Content="삽입하기" />

                <Button Name="containsButton"

                    Margin="10"

                    Width="100"

                    Height="30"

                    Content="포함 여부" />

            </UniformGrid>

        </StackPanel>

    </Grid>

</Window>

 

 

MainWindow.xaml.cs

 

 

using System.Windows;

 

namespace TestProject

{

    /// <summary>

    /// 메인 윈도우

    /// </summary>

    public partial class MainWindow : Window

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - MainWindow()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainWindow()

        {

            InitializeComponent();

 

            this.listControl.IsReadOnly = true;

 

            this.clearButton.Click    += clearButton_Click;

            this.addButton.Click      += addButton_Click;

            this.insertButton.Click   += insertButton_Click;

            this.containsButton.Click += containsButton_Click;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region 지우기 버튼 클릭시 처리하기 - clearButton_Click(sender, e)

 

        /// <summary>

        /// 지우기 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void clearButton_Click(object sender, RoutedEventArgs e)

        {

            this.listControl.Clear();

        }

 

        #endregion

        #region 추가하기 버튼 클릭시 처리하기 - addButton_Click(sender, e)

 

        /// <summary>

        /// 추가하기 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void addButton_Click(object sender, RoutedEventArgs e)

        {

            this.listControl.Clear();

 

            for(int i = 200; i < 300; i++)

            {

                this.listControl.Add(i);

            }

        }

 

        #endregion

        #region 삽입하기 버튼 클릭시 처리하기 - insertButton_Click(sender, e)

 

        /// <summary>

        /// 삽입하기 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void insertButton_Click(object sender, RoutedEventArgs e)

        {

            for(int i = 199; i >= 100; i--)

            {

                this.listControl.Insert(0, i);

            }

        }

 

        #endregion

        #region 포함 여부 버튼 클릭시 처리하기 - containsButton_Click(sender, e)

 

        /// <summary>

        /// 포함 여부 버튼 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void containsButton_Click(object sender, RoutedEventArgs e)

        {

            bool result = this.listControl.Contains(250);

 

            MessageBox.Show("250 포함 여부 : " + result.ToString());

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

728x90
반응형
Posted by 사용자 icodebroker
TAG , ,

댓글을 달아 주세요