[C#/WPF] ItemsPanelTemplate 엘리먼트 : ListBox 엘리먼트의 ItemPanel 속성에 커스텀 패널 설정하기
C#/WPF 2020. 8. 26. 21:11728x90
반응형
728x170
▶ Employees.xml
<Employees xmlns="">
<Employee Name="Betty">
<BirthDate>1970/08/31</BirthDate>
<Face>IMAGE/Betty.png</Face>
<LeftHanded>False</LeftHanded>
</Employee>
<Employee Name="Edgar">
<BirthDate>1965/02/02</BirthDate>
<Face>IMAGE/Edgar.png</Face>
<LeftHanded>True</LeftHanded>
</Employee>
<Employee Name="Sally">
<BirthDate>1980/07/12</BirthDate>
<Face>IMAGE/Sally.png</Face>
<LeftHanded>True</LeftHanded>
</Employee>
<Employee Name="Jim">
<BirthDate>1975/06/15</BirthDate>
<Face>IMAGE/Jim.png</Face>
<LeftHanded>False</LeftHanded>
</Employee>
<Employee Name="Anne">
<BirthDate>1975/04/07</BirthDate>
<Face>IMAGE/Anne.png</Face>
<LeftHanded>True</LeftHanded>
</Employee>
<Employee Name="John">
<BirthDate>1955/12/02</BirthDate>
<Face>IMAGE/John.png</Face>
<LeftHanded>False</LeftHanded>
</Employee>
</Employees>
728x90
▶ RadialPanelOrientation.cs
namespace TestProject
{
/// <summary>
/// 방사형 패널 방향
/// </summary>
public enum RadialPanelOrientation
{
/// <summary>
/// 너비
/// </summary>
ByWidth,
/// <summary>
/// 높이
/// </summary>
ByHeight
}
}
300x250
▶ RadialPanel.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace TestProject
{
/// <summary>
/// 방사형 패널
/// </summary>
public class RadialPanel : Panel
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 방향 속성 - OrientationProperty
/// <summary>
/// 방향 속성
/// </summary>
public static readonly DependencyProperty OrientationProperty;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 파이 선 보여주기 여부
/// </summary>
private bool showPieLines;
/// <summary>
/// 가장 큰 자식 크기
/// </summary>
private Size largestChildSize;
/// <summary>
/// 개별 각도
/// </summary>
private double eachAngle;
/// <summary>
/// 반지름
/// </summary>
private double radius;
/// <summary>
/// 중심점에서 외부 가장자리
/// </summary>
private double outerEdgeFromCenter;
/// <summary>
/// 중심점에서 내부 가장자리
/// </summary>
private double innerEdgeFromCenter;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 방향 - Orientation
/// <summary>
/// 방향
/// </summary>
public RadialPanelOrientation Orientation
{
set
{
SetValue(OrientationProperty, value);
}
get
{
return (RadialPanelOrientation)GetValue(OrientationProperty);
}
}
#endregion
#region 파이 선 보여주기 여부 - ShowPieLines
/// <summary>
/// 파이 선 보여주기 여부
/// </summary>
public bool ShowPieLines
{
set
{
if(value != this.showPieLines)
{
InvalidateVisual();
}
this.showPieLines = value;
}
get
{
return this.showPieLines;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Static
#region 생성자 - RadialPanel()
/// <summary>
/// 생성자
/// </summary>
static RadialPanel()
{
OrientationProperty = DependencyProperty.Register
(
"Orientation",
typeof(RadialPanelOrientation),
typeof(RadialPanel),
new FrameworkPropertyMetadata
(
RadialPanelOrientation.ByWidth,
FrameworkPropertyMetadataOptions.AffectsMeasure
)
);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 측정하기 (오버라이딩) - MeasureOverride(availableSize)
/// <summary>
/// 측정하기 (오버라이딩)
/// </summary>
/// <param name="availableSize">이용 가능한 크기</param>
/// <returns>희망 크기</returns>
protected override Size MeasureOverride(Size availableSize)
{
if(InternalChildren.Count == 0)
{
return new Size(0, 0);
}
this.eachAngle = 360d / InternalChildren.Count;
this.largestChildSize = new Size(0, 0);
foreach(UIElement child in InternalChildren)
{
child.Measure
(
new Size
(
Double.PositiveInfinity,
Double.PositiveInfinity
)
);
this.largestChildSize.Width = Math.Max(this.largestChildSize.Width , child.DesiredSize.Width );
this.largestChildSize.Height = Math.Max(this.largestChildSize.Height, child.DesiredSize.Height);
}
if(Orientation == RadialPanelOrientation.ByWidth)
{
this.innerEdgeFromCenter = this.largestChildSize.Width / 2d / Math.Tan(Math.PI * this.eachAngle / 360d);
this.outerEdgeFromCenter = this.innerEdgeFromCenter + this.largestChildSize.Height;
this.radius = Math.Sqrt
(
Math.Pow(this.largestChildSize.Width / 2d, 2d) + Math.Pow(this.outerEdgeFromCenter, 2d)
);
}
else
{
this.innerEdgeFromCenter = this.largestChildSize.Height / 2d / Math.Tan(Math.PI * this.eachAngle / 360d);
this.outerEdgeFromCenter = this.innerEdgeFromCenter + this.largestChildSize.Width;
this.radius = Math.Sqrt
(
Math.Pow(this.largestChildSize.Height / 2d, 2d) + Math.Pow(this.outerEdgeFromCenter, 2d)
);
}
return new Size(2 * this.radius, 2 * this.radius);
}
#endregion
#region 정렬하기 (오버라이딩) - ArrangeOverride(finalSize)
/// <summary>
/// 정렬하기 (오버라이딩)
/// </summary>
/// <param name="finalSize">최종 크기</param>
/// <returns>최종 크기</returns>
protected override Size ArrangeOverride(Size finalSize)
{
double childAngle = 0;
Point centerPoint = new Point(finalSize.Width / 2d, finalSize.Height / 2d);
double multiplier = Math.Min
(
finalSize.Width / (2d * this.radius),
finalSize.Height / (2d * this.radius)
);
foreach(UIElement child in InternalChildren)
{
child.RenderTransform = Transform.Identity;
if(Orientation == RadialPanelOrientation.ByWidth)
{
child.Arrange
(
new Rect
(
centerPoint.X - multiplier * this.largestChildSize.Width / 2,
centerPoint.Y - multiplier * this.outerEdgeFromCenter,
multiplier * this.largestChildSize.Width,
multiplier * this.largestChildSize.Height
)
);
}
else
{
child.Arrange
(
new Rect
(
centerPoint.X + multiplier * this.innerEdgeFromCenter,
centerPoint.Y - multiplier * this.largestChildSize.Height / 2,
multiplier * this.largestChildSize.Width,
multiplier * this.largestChildSize.Height
)
);
}
Point childRelativeCenterPoint = TranslatePoint(centerPoint, child);
child.RenderTransform = new RotateTransform
(
childAngle,
childRelativeCenterPoint.X,
childRelativeCenterPoint.Y
);
childAngle += this.eachAngle;
}
return finalSize;
}
#endregion
#region 렌더링시 처리하기 - OnRender(drawingContext)
/// <summary>
/// 렌더링시 처리하기
/// </summary>
/// <param name="drawingContext">드로잉 컨텍스트</param>
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if(ShowPieLines)
{
Point centerPoint = new Point(RenderSize.Width / 2d, RenderSize.Height / 2d);
double multiplier = Math.Min
(
RenderSize.Width / (2d * this.radius),
RenderSize.Height / (2d * this.radius)
);
Pen pen = new Pen(SystemColors.WindowTextBrush, 1d);
pen.DashStyle = DashStyles.Dash;
drawingContext.DrawEllipse
(
null,
pen,
centerPoint,
multiplier * this.radius,
multiplier * this.radius
);
double childAngle = -this.eachAngle / 2;
if(Orientation == RadialPanelOrientation.ByWidth)
{
childAngle += 90;
}
foreach(UIElement child in InternalChildren)
{
drawingContext.DrawLine
(
pen, centerPoint,
new Point
(
centerPoint.X + multiplier * this.radius * Math.Cos(2 * Math.PI * childAngle / 360),
centerPoint.Y + multiplier * this.radius * Math.Sin(2 * Math.PI * childAngle / 360)
)
);
childAngle += this.eachAngle;
}
}
}
#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="ItemsPanelTemplate 엘리먼트 : ListBox 엘리먼트의 ItemPanel 속성에 커스텀 패널 설정하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Window.Resources>
<XmlDataProvider x:Key="EmployeeXmlDataProviderKey"
Source="Employees.xml"
XPath="Employees" />
</Window.Resources>
<Grid>
<ListBox Name="listBox"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ItemsSource="{Binding Source={StaticResource EmployeeXmlDataProviderKey}, XPath=Employee}"
SelectedValuePath="Face">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<local:RadialPanel Orientation="ByHeight" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel Margin="3">
<Image DockPanel.Dock="Right"
Stretch="None"
Source="{Binding XPath=Face}" />
<UniformGrid Rows="3"
VerticalAlignment="Center"
Margin="10">
<TextBlock
TextAlignment="Center"
Text="{Binding XPath=@Name}" />
<TextBlock
TextAlignment="Center"
FontSize="12pt"
Text="{Binding XPath=BirthDate}" />
<TextBlock Name="leftHandedTextBlock"
TextAlignment="Center"
Text="Right-Handed" />
</UniformGrid>
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding XPath=LeftHanded}" Value="True">
<Setter
TargetName="leftHandedTextBlock"
Property="Text"
Value="Left-Handed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Image
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="None"
Source="{Binding ElementName=listBox, Path=SelectedValue}" />
</Grid>
</Window>
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] TextBox 클래스 : Select 메소드를 사용해 커서를 맨 앞으로 이동시키기 (0) | 2020.08.27 |
---|---|
[C#/WPF] FormattedText 클래스 : SetForegroundBrush/SetFontWeight/SetFontStyle/SetFontSize 메소드 사용하기 (0) | 2020.08.27 |
[C#/WPF] 지오메트리 디자이너 사용하기 (0) | 2020.08.27 |
[C#/WPF] xml:space 속성 : XAML에서 문자열 공백 유지하기 (0) | 2020.08.27 |
[C#/WPF] XmlDataProvider 엘리먼트 : x:XData 엘리먼트를 사용해 XML 데이터 내장하기 (0) | 2020.08.26 |
[C#/WPF] DataTemplate 엘리먼트 : ListBox 엘리먼트의 ItemTemplate 속성 설정하기 (0) | 2020.08.26 |
[C#/WPF] DataTemplate 엘리먼트 : Button 엘리먼트의 ContentTemplate 속성 설정하기 (0) | 2020.08.26 |
[C#/WPF] ControlTemplate 엘리먼트 : CheckBox 엘리먼트 정의하기 (0) | 2020.08.26 |
[C#/WPF] ControlTemplate 엘리먼트 : RadioButton 엘리먼트 정의하기 (0) | 2020.08.26 |
[C#/WPF] 지오메트리 사용하기 (0) | 2020.08.26 |
댓글을 달아 주세요