첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ ColorToBrushConverter.cs

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 색상↔브러시 변환자
    /// </summary>
    [ValueConversion(typeof(Color), typeof(SolidColorBrush))]
    public class ColorToBrushConverter : IValueConverter
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 변환하기 - Convert(sourceValue, targetType, parameter, cultureInfo)

        /// <summary>
        /// 변환하기
        /// </summary>
        /// <param name="sourceValue">소스 값</param>
        /// <param name="targetType">타겟 타입</param>
        /// <param name="parameter">매개 변수</param>
        /// <param name="cultureInfo">컬처 정보</param>
        /// <returns>타겟 값</returns>
        public object Convert(object sourceValue, Type targetType, object parameter, CultureInfo cultureInfo)
        {
            Color color = (Color)sourceValue;

            return new SolidColorBrush(color);
        }

        #endregion
        #region 역 변환하기 - ConvertBack(targetValue, sourceType, parameter, cultureInfo)

        /// <summary>
        /// 역 변환하기
        /// </summary>
        /// <param name="targetValue">타겟 값</param>
        /// <param name="sourceType">소스 타입</param>
        /// <param name="parameter">매개 변수</param>
        /// <param name="cultureInfo">컬처 정보</param>
        /// <returns>소스 값</returns>
        public object ConvertBack(object targetValue, Type sourceType, object parameter, CultureInfo cultureInfo)
        {
            SolidColorBrush brush = targetValue as SolidColorBrush;

            return brush.Color;
        }

        #endregion
    }
}

 

728x90

 

▶ ColorItem.cs

using System.ComponentModel;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 색상 항목
    /// </summary>
    public class ColorItem : INotifyPropertyChanged
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 속성 변경시 - PropertyChanged

        /// <summary>
        /// 속성 변경시
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 색상
        /// </summary>
        private Color? color;

        /// <summary>
        /// 텍스트
        /// </summary>
        private string text;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 색상 - Color

        /// <summary>
        /// 색상
        /// </summary>
        public Color? Color
        {
            get
            {
                return this.color;
            }
            set
            {
                if(this.color == value)
                {
                    return;
                }

                this.color = value;

                FirePropertyChanged("Color");
            }
        }

        #endregion
        #region 텍스트 - Text

        /// <summary>
        /// 텍스트
        /// </summary>
        public string Text
        {
            get
            {
                return this.text;
            }
            set
            {
                if(this.text == value)
                {
                    return;
                }

                this.text = value;

                FirePropertyChanged("Text");
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 생성자 - ColorItem()

        /// <summary>
        /// 생성자
        /// </summary>
        public ColorItem()
        {
        }

        #endregion
        #region 생성자 - ColorItem(color, text)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="color">색상</param>
        /// <param name="text">텍스트</param>
        public ColorItem(Color color, string text)
        {
            this.color = color;
            this.text  = text;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 문자열 구하기 - ToString()

        /// <summary>
        /// 문자열 구하기
        /// </summary>
        /// <returns>문자열</returns>
        public override string ToString()
        {
            return this.text;
        }

        #endregion

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

        #region 속성 변경시 이벤트 발생시키기 - FirePropertyChanged(propertyName)

        /// <summary>
        /// 속성 변경시 이벤트 발생시키기
        /// </summary>
        /// <param name="propertyName">속성명</param>
        protected void FirePropertyChanged(string propertyName)
        {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

 

300x250

 

▶ ColorList.xaml

<ListBox x:Class="TestProject.ColorList"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:local="clr-namespace:TestProject"
    mc:Ignorable="d"
    d:DesignWidth="200"
    d:DesignHeight="200"
    ScrollViewer.HorizontalScrollBarVisibility="Hidden"
    SelectionMode="Single">
    <ListBox.Resources>
        <local:ColorToBrushConverter x:Key="ColorToBrushConverterKey" />
        <ItemsPanelTemplate x:Key="ItemsPanelTemplateKey1">
            <StackPanel />
        </ItemsPanelTemplate>
        <ItemsPanelTemplate x:Key="ItemsPanelTemplateKey2">
            <UniformGrid Columns="8" />
        </ItemsPanelTemplate>
        <DataTemplate x:Key="DataTemplateKey1">
            <StackPanel Orientation="Horizontal">
                <Rectangle
                    Margin="2"
                    Width="16"
                    Height="16"
                    Stroke="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
                    Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverterKey}}" />
                <TextBlock Name="itemTextBlock"
                    Margin="2 0 0 0"
                    Width="{Binding RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}, Path=ActualWidth}"
                    VerticalAlignment="Center"
                    Text="{Binding Text}" />
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="DataTemplateKey2">
            <Rectangle
                Margin="2"
                Width="16"
                Height="16"
                Stroke="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
                Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverterKey}}"
                ToolTip="{Binding Path=Text}" />
        </DataTemplate>
    </ListBox.Resources>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Rectangle
                    Margin="2"
                    Width="16"
                    Height="16"
                    Stroke="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
                    Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverterKey}}" />
                <TextBlock Name="itemTextBlock"
                    Margin="2 0 0 0"
                    Width="{Binding RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}, Path=ActualWidth}"
                    VerticalAlignment="Center" Text="{Binding Text}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

 

▶ ColorList.xaml.cs

using System;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 색상 리스트
    /// </summary>
    public partial class ColorList : ListBox
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 색상 변경시 - ColorChanged

        /// <summary>
        /// 색상 변경시
        /// </summary>
        public event EventHandler ColorChanged;

        #endregion
        #region 항목 마우스 UP시 - ItemMouseUp

        /// <summary>
        /// 항목 마우스 UP시
        /// </summary>
        public event EventHandler ItemMouseUp;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 색상 의존 속성
        /// </summary>
        public static readonly DependencyProperty ColorProperty;

        /// <summary>
        /// 텍스트 의존 속성
        /// </summary>
        public static readonly DependencyProperty TextProperty;

        /// <summary>
        /// 알려진 색상들 채우기 여부 의존 속성
        /// </summary>
        public static readonly DependencyProperty FillKnownColorsProperty;

        /// <summary>
        /// 색상 그리드 사용 여부 의존 속성
        /// </summary>
        public static readonly DependencyProperty UseColorGridProperty;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 값 변경시 이벤트 발생 여부
        /// </summary>
        private bool canFireValueChanged = true;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 색상 - Color

        /// <summary>
        /// 색상
        /// </summary>
        public Color? Color
        {
            get
            {
                return (Color?)GetValue(ColorProperty);
            }
            set
            {
                SetValue(ColorProperty, value);
            }
        }

        #endregion
        #region 텍스트 - Text

        /// <summary>
        /// 텍스트
        /// </summary>
        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        #endregion
        #region 알려진 색상들 채우기 여부 - FillKnownColors

        /// <summary>
        /// 알려진 색상들 채우기 여부
        /// </summary>
        public bool FillKnownColors
        {
            get
            {
                return (bool)GetValue(FillKnownColorsProperty);
            }
            set
            {
                SetValue(FillKnownColorsProperty, value);
            }
        }

        #endregion
        #region 색상 그리드 사용 여부 - UseColorGrid

        /// <summary>
        /// 색상 그리드 사용 여부
        /// </summary>
        public bool UseColorGrid
        {
            get
            {
                return (bool)GetValue(UseColorGridProperty);
            }
            set
            {
                SetValue(UseColorGridProperty, value);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - ColorList()

        /// <summary>
        /// 생성자
        /// </summary>
        static ColorList()
        {
            ColorProperty = RegisterDependencyProperty(null, false, false, false, true, colorPropertyChangedCallback,
                "Color", typeof(Color?), typeof(ColorList), null);
            TextProperty = RegisterDependencyProperty(string.Empty, false, false, false, true, null,
                "Text", typeof(string), typeof(ColorList), null);
            FillKnownColorsProperty = RegisterDependencyProperty(false, false, false, false, true, fillKnownColorsPropertyChangedCallback,
                "FillKnownColors", typeof(bool), typeof(ColorList), null);
            UseColorGridProperty = RegisterDependencyProperty(false, false, false, false, true, useColorGridPropertyChangedCallback,
                "UseColorGrid", typeof(bool), typeof(ColorList), null);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - ColorList()

        /// <summary>
        /// 생성자
        /// </summary>
        public ColorList()
        {
            InitializeComponent();

            SelectionChanged += ListBox_SelectionChanged;

            AddHandler(TextBlock.MouseDownEvent, new MouseButtonEventHandler(itemTextBlock_MouseDown));
            AddHandler(TextBlock.MouseUpEvent  , new MouseButtonEventHandler(itemTextBlock_MouseUp  ));
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 의존 속성 등록하기 - RegisterDependencyProperty(defaultValue, affectsRender, affectsMeasure, affectsArrange,
            inherits, propertyChangedCallback, propertyName, propertyType, ownerType, validateValueCallback)

        /// <summary>
        /// 의존 속성 등록하기
        /// </summary>
        /// <param name="defaultValue">디폴트 값</param>
        /// <param name="affectsRender">렌더링 여향 여부</param>
        /// <param name="affectsMeasure">크기 측정 영향 여부</param>
        /// <param name="affectsArrange">배치 영향 여부</param>
        /// <param name="inherits">상속 여부</param>
        /// <param name="propertyChangedCallback">속성 변경 콜백</param>
        /// <param name="propertyName">속성명</param>
        /// <param name="propertyType">속성 타입</param>
        /// <param name="ownerType">소유자 타입</param>
        /// <param name="validateValueCallback">값 무결성 콜백</param>
        /// <returns>의존 속성</returns>
        private static DependencyProperty RegisterDependencyProperty(object defaultValue, bool affectsRender, bool affectsMeasure, bool affectsArrange,
            bool inherits, PropertyChangedCallback propertyChangedCallback, string propertyName, Type propertyType, Type ownerType,
            ValidateValueCallback validateValueCallback)
        {
            FrameworkPropertyMetadata frameworkPropertyMetadata = new FrameworkPropertyMetadata();

            frameworkPropertyMetadata.DefaultValue   = defaultValue;
            frameworkPropertyMetadata.AffectsRender  = affectsRender;
            frameworkPropertyMetadata.AffectsMeasure = affectsMeasure;
            frameworkPropertyMetadata.AffectsArrange = affectsArrange;
            frameworkPropertyMetadata.Inherits       = inherits;

            frameworkPropertyMetadata.PropertyChangedCallback += propertyChangedCallback;

            return DependencyProperty.Register(propertyName, propertyType, ownerType, frameworkPropertyMetadata, validateValueCallback);
        }

        #endregion
        #region 색상 속성 변경시 콜백 처리하기 - colorPropertyChangedCallback(dependencyObject, e)

        /// <summary>
        /// 색상 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="dependencyObject">의존 객체</param>
        /// <param name="e">이벤트인자</param>
        private static void colorPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            ColorList colorList = dependencyObject as ColorList;

            Color? newValue = (Color?)e.NewValue;

            colorList.SetColor(newValue);

            string text = colorList.GetText();

            colorList.Text = text;

            colorList.FireColorChanged();
        }

        #endregion
        #region 알려진 색상들 채우기 여부 속성 변경시 콜백 처리하기 - fillKnownColorsPropertyChangedCallback(dependencyObject, e)

        /// <summary>
        /// 알려진 색상들 채우기 여부 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="dependencyObject">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void fillKnownColorsPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            ColorList colorList = dependencyObject as ColorList;

            bool newValue = (bool)e.NewValue;

            colorList.ItemsSource = null;

            colorList.Color = null;

            if(newValue)
            {
                colorList.SetKnownColorsCollection();
            }
        }

        #endregion
        #region 색상 그리드 사용 여부 속성 변경시 콜백 처리하기 - useColorGridPropertyChangedCallback(dependencyObject, e)

        /// <summary>
        /// 색상 그리드 사용 여부 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="dependencyObject">의존 객체</param>
        /// <param name="e">이벤트인자</param>
        private static void useColorGridPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            ColorList colorList = dependencyObject as ColorList;

            bool newValue = (bool)e.NewValue;

            if(newValue)
            {
                DataTemplate dataTemplate = colorList.FindResource("DataTemplateKey2") as DataTemplate;

                colorList.ItemTemplate = dataTemplate;

                ItemsPanelTemplate itemsPanelTemplate = colorList.FindResource("ItemsPanelTemplateKey2") as ItemsPanelTemplate;

                colorList.ItemsPanel = itemsPanelTemplate;
            }
            else
            {
                DataTemplate dataTemplate = colorList.FindResource("DataTemplateKey1") as DataTemplate;

                colorList.ItemTemplate = dataTemplate;

                ItemsPanelTemplate itemsPanelTemplate = colorList.FindResource("ItemsPanelTemplateKey1") as ItemsPanelTemplate;

                colorList.ItemsPanel = itemsPanelTemplate;
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 항목 구하기 - GetItem()

        /// <summary>
        /// 항목 구하기
        /// </summary>
        /// <returns>항목</returns>
        public ColorItem GetItem()
        {
            ColorItem item = SelectedItem as ColorItem;

            return item;
        }

        #endregion

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

        #region 색상 변경시 이벤트 발생시키기 - FireColorChanged()

        /// <summary>
        /// 색상 변경시 이벤트 발생시키기
        /// </summary>
        protected void FireColorChanged()
        {
            if(ColorChanged != null)
            {
                ColorChanged(this, EventArgs.Empty);
            }
        }

        #endregion
        #region 항목 마우스 UP 이벤트 발생시키기 - FireItemMouseUp()

        /// <summary>
        /// 항목 마우스 UP 이벤트 발생시키기
        /// </summary>
        public void FireItemMouseUp()
        {
            if(ItemMouseUp != null)
            {
                ItemMouseUp(this, EventArgs.Empty);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private
        ////////////////////////////////////////////////////////////////////// Event

        #region 리스트 박스 선택 변경시 처리하기 - ListBox_SelectionChanged(sender, e)

        /// <summary>
        /// 리스트 박스 선택 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if(this.canFireValueChanged)
            {
                ColorItem item = SelectedItem as ColorItem;

                if(item == null)
                {
                    Color = Colors.Transparent;

                    return;
                }

                Color = item.Color;
            }
        }

        #endregion
        #region 항목 텍스트 블럭 마우스 DOWN 처리하기 - itemTextBlock_MouseDown(sender, e)

        /// <summary>
        /// 항목 텍스트 블럭 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void itemTextBlock_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.canFireValueChanged = false;
        }

        #endregion
        #region 항목 텍스트 블럭 마우스 UP 처리하기 - itemTextBlock_MouseUp(sender, e)

        /// <summary>
        /// 항목 텍스트 블럭 마우스 UP 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void itemTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this.canFireValueChanged = true;

            Color = GetColor();

            FireColorChanged();

            FireItemMouseUp();
        }

        #endregion

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

        #region 색상 설정하기 - SetColor(color)

        /// <summary>
        /// 색상 설정하기
        /// </summary>
        /// <param name="color">색상</param>
        private void SetColor(Color? color)
        {
            try
            {
                SelectionChanged -= ListBox_SelectionChanged;

                if(color.Equals(null))
                {
                    SelectedItem = null;

                    return;
                }

                ObservableCollection<ColorItem> collection = ItemsSource as ObservableCollection<ColorItem>;

                if(collection == null || collection.Count == 0)
                {
                    return;
                }

                foreach(ColorItem item in collection)
                {
                    if(color.Equals(item.Color))
                    {
                        SelectedItem = item;

                        return;
                    }
                }

                SelectedItem = null;
            }
            finally
            {
                SelectionChanged += ListBox_SelectionChanged;
            }
        }

        #endregion
        #region 색상 구하기 - GetColor()

        /// <summary>
        /// 색상 구하기
        /// </summary>
        /// <returns>색상</returns>
        private Color? GetColor()
        {
            if(SelectedItem == null)
            {
                return null;
            }

            ColorItem item = SelectedItem as ColorItem;

            if(item != null)
            {
                return item.Color;
            }
            else
            {
                return null;
            }
        }

        #endregion
        #region 텍스트 구하기 - GetText()

        /// <summary>
        /// 텍스트 구하기
        /// </summary>
        /// <returns>텍스트</returns>
        private string GetText()
        {
            if(SelectedItem == null)
            {
                return string.Empty;
            }

            ColorItem item = SelectedItem as ColorItem;

            if(item != null)
            {
                return item.Text;
            }
            else
            {
                return string.Empty;
            }
        }

        #endregion
        #region 색상명 구하기 - GetColorName(originalName)

        /// <summary>
        /// 색상명 구하기
        /// </summary>
        /// <param name="originalName">원본명</param>
        /// <returns>색상명</returns>
        private string GetColorName(string originalName)
        {
            string name = originalName[0].ToString();

            for(int i = 1; i < originalName.Length; i++)
            {
                name += (char.IsUpper(originalName[i]) ? " " : string.Empty) + originalName[i].ToString();
            }

            return name;
        }

        #endregion
        #region 알려진 색상들 컬렉션 설정하기 - SetKnownColorsCollection()

        /// <summary>
        /// 알려진 색상들 컬렉션 설정하기
        /// </summary>
        private void SetKnownColorsCollection()
        {
            ObservableCollection<ColorItem> collection = new ObservableCollection<ColorItem>();

            PropertyInfo[] propertyInfoArray = typeof(Colors).GetProperties();

            for(int i = 0; i < propertyInfoArray.Length; i++)
            {
                ColorItem item = new ColorItem();

                item.Color = (Color)propertyInfoArray[i].GetValue(null, null);
                item.Text  = GetColorName(propertyInfoArray[i].Name);

                collection.Add(item);
            }

            ItemsSource = collection;
        }

        #endregion
    }
}

 

▶ ColorCombo.xaml

<UserControl x:Class="TestProject.ColorCombo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
    xmlns:local="clr-namespace:TestProject"
    mc:Ignorable="d"
    d:DesignWidth="300"
    d:DesignHeight="30">
    <UserControl.Resources>
        <local:ColorToBrushConverter x:Key="ColorToBrushConverterKey" />
    </UserControl.Resources>
    <Grid Name="grid"
        Margin="0">
        <Button Name="button"
            Margin="0"
            Width="{Binding ElementName=grid, Path=ActualWidth}"
            Height= "{Binding ElementName=grid, Path=ActualHeight}"
            Content="{Binding ElementName=colorList, Path=SelectedItem}"
            Padding="0">
            <Button.ContentTemplate>
                <DataTemplate>
                    <Grid Width="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=ActualWidth}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*"    />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Rectangle Name="itemRectangle" Grid.Column="0"
                            Margin="2"
                            Width="16"
                            Height="16"
                            Stroke="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
                            Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverterKey}}" />
                        <TextBlock Grid.Column="1"
                            Margin="2 0 0 0"
                            VerticalAlignment="Center"
                            TextAlignment="Left" Text="{Binding Text}" />
                        <Path Grid.Column="2"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Width="20"
                            Fill="Black">
                            <Path.Data>
                                <Geometry>M 5 0 L 8.5 4 L 12 0 Z</Geometry>
                            </Path.Data>
                        </Path>
                    </Grid>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Content}" Value="{x:Null}">
                            <Setter TargetName="itemRectangle" Property="Stroke" Value="Transparent" />
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </Button.ContentTemplate>
        </Button>
        <Popup Name="popup"
            MinWidth="{Binding ElementName=button, Path=ActualWidth}"
            Width="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=PopupWidth}"
            Height="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=PopupHeight}"
            PlacementTarget="{Binding ElementName=button}"
            Placement="Bottom"
            IsOpen="False"
            StaysOpen="False"
            AllowsTransparency="True">
            <themes:SystemDropShadowChrome Margin="0 0 3 3" Color="#80000000">
                <Border HorizontalAlignment="Stretch">
                    <local:ColorList x:Name="colorList" />
                </Border>
            </themes:SystemDropShadowChrome>
        </Popup>
    </Grid>
</UserControl>

 

▶ ColorCombo.xaml.cs

using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;

namespace TestProject
{
    /// <summary>
    /// 색상 콤보
    /// </summary>
    public partial class ColorCombo : UserControl
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 색상 변경시 - ColorChanged

        /// <summary>
        /// 색상 변경시
        /// </summary>
        public event EventHandler ColorChanged;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 항목들 소스 의존 속성
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty;

        /// <summary>
        /// 팝업 너비 의존 속성
        /// </summary>
        public static readonly DependencyProperty PopupWidthProperty;

        /// <summary>
        /// 팝업 높이 의존 속성
        /// </summary>
        public static readonly DependencyProperty PopupHeightProperty;

        /// <summary>
        /// 색상 의존 속성
        /// </summary>
        public static readonly DependencyProperty ColorProperty;

        /// <summary>
        /// 텍스트 의존 속성
        /// </summary>
        public static readonly DependencyProperty TextProperty;

        /// <summary>
        /// 알려진 색상들 채우기 여부 의존 속성
        /// </summary>
        public static readonly DependencyProperty FillKnownColorsProperty;

        /// <summary>
        /// 색상 그리드 사용 여부 의존 속성
        /// </summary>
        public static readonly DependencyProperty UseColorGridProperty;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 마우스 버튼 상위 여부
        /// </summary>
        private bool isMouseOverButton = false;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 버튼 - Button

        /// <summary>
        /// 버튼
        /// </summary>
        public Button Button
        {
            get
            {
                return this.button;
            }
        }

        #endregion
        #region 색상 리스트 - ColorList

        /// <summary>
        /// 색상 리스트
        /// </summary>
        public ColorList ColorList
        {
            get
            {
                return this.colorList;
            }
        }

        #endregion

        #region 항목들 소스 - ItemsSource

        /// <summary>
        /// 항목들 소스
        /// </summary>
        public IEnumerable ItemsSource
        {
            get
            {
                return (IEnumerable)GetValue(ItemsSourceProperty);
            }
            set
            {
                SetValue(ItemsSourceProperty, value);
            }
        }

        #endregion
        #region 팝업 너비 - PopupWidth

        /// <summary>
        /// 팝업 너비
        /// </summary>
        public double PopupWidth
        {
            get
            {
                return (double)GetValue(PopupWidthProperty);
            }
            set
            {
                SetValue(PopupWidthProperty, value);
            }
        }

        #endregion
        #region 팝업 높이 - PopupHeight

        /// <summary>
        /// 팝업 높이
        /// </summary>
        public double PopupHeight
        {
            get
            {
                return (double)GetValue(PopupHeightProperty);
            }
            set
            {
                SetValue(PopupHeightProperty, value);
            }
        }

        #endregion
        #region 색상 - Color

        /// <summary>
        /// 색상
        /// </summary>
        public Color? Color
        {
            get
            {
                return (Color?)GetValue(ColorProperty);
            }
            set
            {
                SetValue(ColorProperty, value);
            }
        }

        #endregion
        #region 텍스트 - Text

        /// <summary>
        /// 텍스트
        /// </summary>
        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        #endregion
        #region 알려진 색상들 채우기 여부 - FillKnownColors

        /// <summary>
        /// 알려진 색상들 채우기 여부
        /// </summary>
        public bool FillKnownColors
        {
            get
            {
                return (bool)GetValue(FillKnownColorsProperty);
            }
            set
            {
                SetValue(FillKnownColorsProperty, value);
            }
        }

        #endregion
        #region 색상 그리드 사용 여부 - UseColorGrid

        /// <summary>
        /// 색상 그리드 사용 여부
        /// </summary>
        public bool UseColorGrid
        {
            get
            {
                return (bool)GetValue(UseColorGridProperty);
            }
            set
            {
                SetValue(UseColorGridProperty, value);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - ColorCombo()

        /// <summary>
        /// 생성자
        /// </summary>
        static ColorCombo()
        {
            ItemsSourceProperty = RegisterDependencyProperty(null, false, false, false, true, null,
                "ItemsSource", typeof(IEnumerable), typeof(ColorCombo), null);
            PopupWidthProperty = RegisterDependencyProperty(150d, false, false, false, true, null,
                "PopupWidth", typeof(double), typeof(ColorCombo), null);
            PopupHeightProperty = RegisterDependencyProperty(100d, false, false, false, true, null,
                "PopupHeight", typeof(double), typeof(ColorCombo), null);
            ColorProperty = RegisterDependencyProperty(null, false, false, false, true, colorPropertyChangedCallback,
                "Color", typeof(Color?), typeof(ColorCombo), null);
            TextProperty = RegisterDependencyProperty(string.Empty, false, false, false, true, null,
                "Text", typeof(string), typeof(ColorCombo), null);
            FillKnownColorsProperty = RegisterDependencyProperty(false, false, false, false, true, null,
                "FillKnownColors", typeof(bool), typeof(ColorCombo), null);
            UseColorGridProperty = RegisterDependencyProperty(false, false, false, false, true, null,
                "UseColorGrid", typeof(bool), typeof(ColorCombo), null);
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - ColorCombo()

        /// <summary>
        /// 생성자
        /// </summary>
        public ColorCombo()
        {
            InitializeComponent();

            SetBinding(this, ItemsSourceProperty, this.colorList, ColorList.ItemsSourceProperty,
                BindingMode.TwoWay, UpdateSourceTrigger.PropertyChanged);
            SetBinding(this, ColorProperty, this.colorList, ColorList.ColorProperty,
                BindingMode.TwoWay, UpdateSourceTrigger.PropertyChanged);
            SetBinding(this, TextProperty, this.colorList, ColorList.TextProperty,
                BindingMode.TwoWay, UpdateSourceTrigger.PropertyChanged);
            SetBinding(this, FillKnownColorsProperty, this.colorList, ColorList.FillKnownColorsProperty,
                BindingMode.TwoWay, UpdateSourceTrigger.PropertyChanged);
            SetBinding(this, UseColorGridProperty, this.colorList, ColorList.UseColorGridProperty,
                BindingMode.TwoWay, UpdateSourceTrigger.PropertyChanged);

            this.popup.IsOpen = true;
            this.popup.IsOpen = false;

            this.button.Click          += button_Click;
            this.popup.Opened          += popup_Opened;
            this.popup.Closed          += popup_Closed;
            this.colorList.ItemMouseUp += colorList_ItemMouseUp;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 의존 속성 등록하기 - RegisterDependencyProperty(defaultValue, affectsRender, affectsMeasure,
            affectsArrange, inherits, propertyChangedCallback, propertyName, propertyType, ownerType, validateValueCallback)

        /// <summary>
        /// 의존 속성 등록하기
        /// </summary>
        /// <param name="defaultValue">디폴트 값</param>
        /// <param name="affectsRender">렌더링 여향 여부</param>
        /// <param name="affectsMeasure">크기 측정 영향 여부</param>
        /// <param name="affectsArrange">배치 영향 여부</param>
        /// <param name="inherits">상속 여부</param>
        /// <param name="propertyChangedCallback">속성 변경 콜백</param>
        /// <param name="propertyName">속성명</param>
        /// <param name="propertyType">속성 타입</param>
        /// <param name="ownerType">소유자 타입</param>
        /// <param name="validateValueCallback">값 무결성 콜백</param>
        /// <returns>의존 속성</returns>
        public static DependencyProperty RegisterDependencyProperty(object defaultValue, bool affectsRender, bool affectsMeasure,
            bool affectsArrange, bool inherits, PropertyChangedCallback propertyChangedCallback, string propertyName,
            Type propertyType, Type ownerType, ValidateValueCallback validateValueCallback)
        {
            FrameworkPropertyMetadata frameworkPropertyMetadata = new FrameworkPropertyMetadata();

            frameworkPropertyMetadata.DefaultValue   = defaultValue;
            frameworkPropertyMetadata.AffectsRender  = affectsRender;
            frameworkPropertyMetadata.AffectsMeasure = affectsMeasure;
            frameworkPropertyMetadata.AffectsArrange = affectsArrange;
            frameworkPropertyMetadata.Inherits       = inherits;

            frameworkPropertyMetadata.PropertyChangedCallback += propertyChangedCallback;

            return DependencyProperty.Register(propertyName, propertyType, ownerType, frameworkPropertyMetadata, validateValueCallback);
        }

        #endregion
        #region 바인딩 설정하기 - SetBinding(targetElement, targetProperty, source, sourceProperty, bindingMode, updateSourceTrigger)

        /// <summary>
        /// 바인딩 설정하기
        /// </summary>
        /// <param name="targetElement">타겟 엘리먼트</param>
        /// <param name="targetProperty">타겟 의존 속성</param>
        /// <param name="source">소스 객체</param>
        /// <param name="sourceProperty">소스 의존 속성</param>
        /// <param name="bindingMode">바인딩 모드</param>
        /// <param name="updateSourceTrigger">소스 업데이트 트리거</param>
        public static void SetBinding(FrameworkElement targetElement, DependencyProperty targetProperty, object source,
            DependencyProperty sourceProperty, BindingMode bindingMode, UpdateSourceTrigger updateSourceTrigger)
        {
            Binding binding = new Binding();

            binding.Source              = source;
            binding.Path                = new PropertyPath(sourceProperty);
            binding.Mode                = bindingMode;
            binding.UpdateSourceTrigger = updateSourceTrigger;

            targetElement.SetBinding(targetProperty, binding);
        }

        #endregion
        #region 색상 속성 변경시 콜백 처리하기 - colorPropertyChangedCallback(dependencyObject, e)

        /// <summary>
        /// 색상 속성 변경시 콜백 처리하기
        /// </summary>
        /// <param name="dependencyObject">의존 객체</param>
        /// <param name="e">이벤트 인자</param>
        private static void colorPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            ColorCombo colorCombo = dependencyObject as ColorCombo;

            colorCombo.FireColorChanged();
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 항목 구하기 - GetItem()

        /// <summary>
        /// 항목 구하기
        /// </summary>
        /// <returns>항목</returns>
        public ColorItem GetItem() 
        {
            return this.colorList.GetItem();
        }

        #endregion

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

        #region 색상 변경시 이벤트 발생시키기 - FireColorChanged()

        /// <summary>
        /// 색상 변경시 이벤트 발생시키기
        /// </summary>
        protected void FireColorChanged()
        {
            if(ColorChanged != null)
            {
                ColorChanged(this, EventArgs.Empty);
            }
        }

        #endregion

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

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

        /// <summary>
        /// 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void button_Click(object sender, RoutedEventArgs e)
        {
            if(this.isMouseOverButton)
            {
                this.isMouseOverButton = false;

                return;
            }

            this.popup.IsOpen = !this.popup.IsOpen;
        }

        #endregion
        #region 팝업 열리는 경우 처리하기 - popup_Opened(sender, e)

        /// <summary>
        /// 팝업 열리는 경우 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void popup_Opened(object sender, EventArgs e)
        {
            ObservableCollection<ColorItem> collection = ItemsSource as ObservableCollection<ColorItem>;

            if(collection == null || collection.Count == 0)
            {
                PopupHeight = 16;

                return;
            }

            if(!UseColorGrid)
            {
                ListBoxItem listBoxItem = this.colorList.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;

                if(listBoxItem != null)
                {
                    if(!listBoxItem.ActualHeight.Equals(Double.NaN))
                    {
                        PopupHeight = Math.Min(collection.Count * listBoxItem.ActualHeight, 20 * listBoxItem.ActualHeight) + 10;
                    }
                }
            }

            ColorItem item = this.colorList.SelectedItem as ColorItem;

            if(item == null)
            {
                return;
            }

            this.colorList.ScrollIntoView(item);
        }

        #endregion
        #region 팝업이 닫히는 경우 처리하기 - popup_Closed(sender, e)

        /// <summary>
        /// 팝업이 닫히는 경우 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void popup_Closed(object sender, EventArgs e)
        {
            Point mousePoint = Mouse.GetPosition(this.button);

            if(mousePoint.X >= 0 && mousePoint.X <= this.button.ActualWidth &&
                mousePoint.Y >= 0 && mousePoint.Y <= this.button.ActualHeight)
            {
                this.isMouseOverButton = true;
            }
            else
            {
                this.isMouseOverButton = false;
            }
        }

        #endregion
        #region 색상 리스트 항목 마우스 UP 처리하기 - colorList_ItemMouseUp(sender, e)

        /// <summary>
        /// 색상 리스트 항목 마우스 UP 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void colorList_ItemMouseUp(object sender, EventArgs e)
        {
            this.popup.IsOpen = false;
        }

        #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:src="clr-namespace:TestProject"
    Width="800"
    Height="600"
    Title="UserControl 클래스 : 색상 콤보 박스 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300" />
            <ColumnDefinition Width="10"  />
            <ColumnDefinition Width="*"   />
        </Grid.ColumnDefinitions>
        <src:ColorCombo x:Name="colorCombo" Grid.Column="0"
            VerticalAlignment="Top"
            Margin="0 50 0 0"
            Width="200"
            Height="30"
            PopupWidth="250" />
        <StackPanel Grid.Column="2" Background="#ffe5e5e5">
            <StackPanel HorizontalAlignment="Left">
                <Button Margin="10" Width="150" Height="30" Content="데이터 설정"      Click="dataButton_Click"         />
                <Button Margin="10" Width="150" Height="30" Content="색상 초기화"      Click="colorButton_Click"        />
                <Button Margin="10" Width="150" Height="30" Content="색상 그리드 사용" Click="useColorGridButton_Click" />
            </StackPanel>
            <TextBlock Name="colorComboTextBlock" Margin="10"/>
        </StackPanel>
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System;
using System.Windows;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            this.colorCombo.ColorChanged += colorCombo_ColorChanged;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 색상 콤보 색상 변경시 처리하기 - colorCombo_ColorChanged(sender, e)

        /// <summary>
        /// 색상 콤보 색상 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void colorCombo_ColorChanged(object sender, EventArgs e)
        {
            this.colorComboTextBlock.Text = this.colorCombo.Color.ToString();
        }
        
        #endregion
        #region 데이터 설정 버튼 클릭시 처리하기 - dataButton_Click(sender, e)

        /// <summary>
        /// 데이터 설정 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void dataButton_Click(object sender, RoutedEventArgs e)
        {
            this.colorCombo.FillKnownColors = true;
        }

        #endregion
        #region 색상 초기화 버튼 클릭시 처리하기 - colorButton_Click(sender, e)

        /// <summary>
        /// 색상 초기화 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void colorButton_Click(object sender, RoutedEventArgs e)
        {
            this.colorCombo.Color = null;
        }

        #endregion
        #region 색상 그리드 사용 버튼 클릭시 처리하기 - useColorGridButton_Click(sender, e)

        /// <summary>
        /// 색상 그리드 사용 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void useColorGridButton_Click(object sender, RoutedEventArgs e)
        {
            this.colorCombo.UseColorGrid = !this.colorCombo.UseColorGrid;
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요