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

TestSolution.zip
0.02MB

 

[TestLibrary 프로젝트]

 

▶ ExpandableListBoxStyle.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestLibrary">
    <Style x:Key="BorderStyleKey" TargetType="{x:Type Border}">
        <Setter Property="BorderThickness" Value="0 1"       />
        <Setter Property="BorderBrush"     Value="#ff484848" />
        <Setter Property="Background"      Value="#ff525252" />
    </Style>
    <Style x:Key="ExpanderToggleButtonStyleKey" TargetType="{x:Type ToggleButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Border Name="border"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        Background="{TemplateBinding Background}">
                        <TextBlock Name="iconTextBlock"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Text="▽" />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter
                                TargetName="border"
                                Property="BorderBrush"
                                Value="#ff363636" />
                            <Setter
                                TargetName="border"
                                Property="Background"
                                Value="#ff424343" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter
                                TargetName="border"
                                Property="BorderBrush"
                                Value="#ff2d2d2d" />
                            <Setter
                                TargetName="border"
                                Property="Background"
                                Value="#FF373838" />
                        </Trigger>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter
                                TargetName="iconTextBlock"
                                Property="Text"
                                Value="△" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="ExpanderStyleKey" TargetType="{x:Type Expander}">
        <Setter
            Property="IsExpanded"
            Value="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Expander}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*"    />
                        </Grid.RowDefinitions>
                        <Grid Grid.Row="0"
                            Height="35">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"    />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <Border Grid.Column="0"
                                Style="{StaticResource BorderStyleKey}">
                                <TextBlock
                                    VerticalAlignment="Center"
                                    Margin="22 0 0 0"
                                    Foreground="#FFDDDDDD"
                                    Text="{TemplateBinding Header}" />
                            </Border>
                            <ToggleButton Grid.Column="1"
                                Style="{DynamicResource ExpanderToggleButtonStyleKey}"
                                Width="28"
                                Height="35"
                                BorderThickness="1 1 0 1"
                                BorderBrush="#FF484848"
                                Background="#ff4d4e4e"
                                IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
                        </Grid>
                        <ContentPresenter Name="ExpandSite" Grid.Row="1"
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            Margin="{TemplateBinding Padding}"
                            Focusable="false"
                            Visibility="Collapsed" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsExpanded" Value="true">
                            <Setter
                                TargetName="ExpandSite"
                                Property="Visibility"
                                Value="Visible" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="ListBoxItemStyleKey" TargetType="{x:Type ListBoxItem}">
        <Setter
            Property="HorizontalContentAlignment"
            Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
        <Setter
            Property="VerticalContentAlignment"
            Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
        <Setter Property="Focusable"  Value="False"       />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Padding"    Value="0"           />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Expander
                        Style="{StaticResource ExpanderStyleKey}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        Header="{Binding Header}"
                        Background="{TemplateBinding Background}"
                        Padding="{TemplateBinding Padding}"
                        SnapsToDevicePixels="true">
                        <ContentPresenter
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </Expander>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="{x:Type local:ExpandableListBox}">
        <Setter Property="HorizontalContentAlignment"                 Value="Stretch"  />
        <Setter Property="VerticalContentAlignment"                   Value="Stretch"  />
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility"   Value="Disabled" />
        <Setter Property="ScrollViewer.CanContentScroll"              Value="true"     />
        <Setter Property="ScrollViewer.PanningMode"                   Value="Both"     />
        <Setter Property="Stylus.IsFlicksEnabled"                     Value="False"    />
        <Setter
            Property="ItemContainerStyle"
            Value="{StaticResource ListBoxItemStyleKey}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBox}">
                    <ScrollViewer
                        Background="{TemplateBinding Background}"
                        Padding="{TemplateBinding Padding}"
                        Focusable="false">
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

 

728x90

 

▶ ExpandableListBox.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace TestLibrary
{
    /// <summary>
    /// 확장 가능한 리스트 박스
    /// </summary>
    public class ExpandableListBox : ListBox
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - ExpandableListBox()

        /// <summary>
        /// 생성자
        /// </summary>
        static ExpandableListBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata
            (
                typeof(ExpandableListBox),
                new FrameworkPropertyMetadata(typeof(ExpandableListBox))
            );
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 초기화시 처리하기 - OnInitialized(e)

        /// <summary>
        /// 초기화시 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
        }

        #endregion

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

        #region 항목 컨테이너 제너레이터 상태 변경시 처리하기 - ItemContainerGenerator_StatusChanged(sender, e)

        /// <summary>
        /// 항목 컨테이너 제너레이터 상태 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            try
            {
                double actualHeight = ActualHeight;

                if(ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                {
                    IEnumerable<object> itemEnumerable = Items.OfType<object>();

                    ListBoxItem firstOfvalidListBoxItems =
                    (
                        from   item in itemEnumerable
                        let    listBoxItem = ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem
                        where  listBoxItem.IsSelected == false && listBoxItem.ActualHeight != 0
                        select listBoxItem
                    ).FirstOrDefault();

                    if(firstOfvalidListBoxItems == null)
                    {
                        return;
                    }

                    double collapsedHeight = firstOfvalidListBoxItems.ActualHeight * Items.Count;

                    foreach(object item in itemEnumerable)
                    {
                        ListBoxItem listBoxItem = ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;

                        Expander expander = VisualTreeHelper.GetChild(listBoxItem, 0) as Expander;

                        if(expander == null)
                        {
                            break;
                        }

                        FrameworkElement element = expander.Content as FrameworkElement;

                        if(element == null)
                        {
                            break;
                        }

                        double elementHeight = (actualHeight - collapsedHeight);

                        if(elementHeight >= 0)
                        {
                            element.Height = elementHeight;
                        }
                    }
                }
            }
            catch(Exception exception)
            {
                Debug.WriteLine(exception);
            }
        }

        #endregion
    }
}

 

300x250

 

[TestProject 프로젝트]

 

▶ MenuItem.cs

using System.Windows;

namespace TestProject
{
    /// <summary>
    /// 메뉴 항목
    /// </summary>
    public class MenuItem : DependencyObject
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 헤더 속성 - HeaderProperty

        /// <summary>
        /// 헤더 속성
        /// </summary>
        public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register
        (
            "Header",
            typeof(object),
            typeof(MenuItem),
            new PropertyMetadata(null)
        );

        #endregion
        #region 컨텐트 속성 - ContentProperty

        /// <summary>
        /// 컨텐트 속성
        /// </summary>
        public static readonly DependencyProperty ContentProperty = DependencyProperty.Register
        (
            "Content",
            typeof(object),
            typeof(MenuItem),
            new PropertyMetadata(null)
        );

        #endregion
        #region 확장 여부 속성 - IsExpandedProperty

        /// <summary>
        /// 확장 여부 속성
        /// </summary>
        public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register
        (
            "IsExpanded",
            typeof(bool),
            typeof(MenuItem),
            new PropertyMetadata(false)
        );

        #endregion

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

        #region 헤더 - Header

        /// <summary>
        /// 헤더
        /// </summary>
        public object Header
        {
            get
            {
                return (object)GetValue(HeaderProperty);
            }
            set
            {
                SetValue(HeaderProperty, value);
            }
        }

        #endregion
        #region 컨텐트 - Content

        /// <summary>
        /// 컨텐트
        /// </summary>
        public object Content
        {
            get
            {
                return (object)GetValue(ContentProperty);
            }
            set
            {
                SetValue(ContentProperty, value);
            }
        }

        #endregion
        #region 확장 여부 - IsExpanded

        /// <summary>
        /// 확장 여부
        /// </summary>
        public bool IsExpanded
        {
            get
            {
                return (bool)GetValue(IsExpandedProperty);
            }
            set
            {
                SetValue(IsExpandedProperty, value);
            }
        }

        #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:TestLibrary;assembly=TestLibrary"
    Width="800"
    Height="600"
    Title="ListBox 클래스 : 확장 가능한 리스트 박스 사용하기"
    FontFamily="나눔고딕코딩"
    FontSize="16">
    <DockPanel LastChildFill="False">
        <local:ExpandableListBox x:Name="expandableListBox" DockPanel.Dock="Left"
            Width="300"
            Background="Gray">
            <local:ExpandableListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="10">
                        <TextBlock Text="{Binding Content}" />
                    </StackPanel>
                </DataTemplate>
            </local:ExpandableListBox.ItemTemplate>
        </local:ExpandableListBox>
    </DockPanel>
</Window>

 

▶ MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.Windows;

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

        #region 생성자 - MainWindow()

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

            ObservableCollection<MenuItem> collection = new ObservableCollection<MenuItem>();

            collection.Add(new MenuItem { Header = "메일"  , Content = "메일 컨텐트"   });
            collection.Add(new MenuItem { Header = "달력"  , Content = "달력 컨텐트"   });
            collection.Add(new MenuItem { Header = "연락처", Content = "연락처 컨텐트" });
            collection.Add(new MenuItem { Header = "일정표", Content = "일정표 컨텐트" });

            this.expandableListBox.ItemsSource = collection;
        }

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

댓글을 달아 주세요