using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace TestProject
{
/// <summary>
/// 노크 버튼
/// </summary>
public class KnockButton : Control
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Event
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 노크 이벤트 - Knock
/// <summary>
/// 노크 이벤트
/// </summary>
public event RoutedEventHandler Knock
{
add
{
AddHandler(KnockEvent, value);
}
remove
{
RemoveHandler(KnockEvent, value);
}
}
#endregion
#region 노크 사전 이벤트 - PreviewKnock
/// <summary>
/// 노크 사전 이벤트
/// </summary>
public event RoutedEventHandler PreviewKnock
{
add
{
AddHandler(PreviewKnockEvent, value);
}
remove
{
RemoveHandler(PreviewKnockEvent, value);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 텍스트 속성
/// </summary>
public static readonly DependencyProperty TextProperty;
/// <summary>
/// 노크 이벤트
/// </summary>
public static readonly RoutedEvent KnockEvent;
/// <summary>
/// 노크 사전 이벤트
/// </summary>
public static readonly RoutedEvent PreviewKnockEvent;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 포맷 텍스트
/// </summary>
private FormattedText formattedText;
/// <summary>
/// 마우스 상위 여부
/// </summary>
private bool isMouseReallyOver;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 텍스트 - Text
/// <summary>
/// 텍스트
/// </summary>
public string Text
{
set
{
SetValue(TextProperty, value == null ? " " : value);
}
get
{
return (string)GetValue(TextProperty);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Static
#region 생성자 - KnockButton()
/// <summary>
/// 생성자
/// </summary>
static KnockButton()
{
TextProperty = DependencyProperty.Register
(
"Text",
typeof(string),
typeof(KnockButton),
new FrameworkPropertyMetadata
(
" ",
FrameworkPropertyMetadataOptions.AffectsMeasure
)
);
KnockEvent = EventManager.RegisterRoutedEvent
(
"Knock",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(KnockButton)
);
PreviewKnockEvent = EventManager.RegisterRoutedEvent
(
"PreviewKnock",
RoutingStrategy.Tunnel,
typeof(RoutedEventHandler),
typeof(KnockButton)
);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 노크시 처리하기 - OnKnock()
/// <summary>
/// 노크시 처리하기
/// </summary>
protected virtual void OnKnock()
{
RoutedEventArgs e = new RoutedEventArgs();
e.RoutedEvent = KnockButton.KnockEvent;
e.Source = this;
RaiseEvent(e);
}
#endregion
#region 프리뷰 노크시 처리하기 - OnPreviewKnock()
/// <summary>
/// 프리뷰 노크시 처리하기
/// </summary>
protected virtual void OnPreviewKnock()
{
RoutedEventArgs e = new RoutedEventArgs();
e.RoutedEvent = KnockButton.PreviewKnockEvent;
e.Source = this;
RaiseEvent(e);
}
#endregion
#region 마우스 진입시 처리하기 - OnMouseEnter(e)
/// <summary>
/// 마우스 진입시 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
InvalidateVisual();
}
#endregion
#region 마우스 이탈시 처리하기 - OnMouseLeave(e)
/// <summary>
/// 마우스 이탈시 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
InvalidateVisual();
}
#endregion
#region 마우스 이동시 처리하기 - OnMouseMove(e)
/// <summary>
/// 마우스 이동시 처리하기
/// </summary>
/// <param name="e">이벤트 발생자</param>
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
Point mousePoint = e.GetPosition(this);
bool isReallyOverNow =
(
mousePoint.X >= 0 && mousePoint.X < ActualWidth &&
mousePoint.Y >= 0 && mousePoint.Y < ActualHeight
);
if(isReallyOverNow != this.isMouseReallyOver)
{
this.isMouseReallyOver = isReallyOverNow;
InvalidateVisual();
}
}
#endregion
#region 마우스 왼쪽 버튼 DOWN 처리하기 - OnMouseLeftButtonDown(e)
/// <summary>
/// 마우스 왼쪽 버튼 DOWN 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
CaptureMouse();
InvalidateVisual();
e.Handled = true;
}
#endregion
#region 마우스 왼쪽 버튼 UP 처리하기 - OnMouseLeftButtonUp(e)
/// <summary>
/// 마우스 왼쪽 버튼 UP 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if(IsMouseCaptured)
{
if(this.isMouseReallyOver)
{
OnPreviewKnock();
OnKnock();
}
e.Handled = true;
Mouse.Capture(null);
}
}
#endregion
#region 마우스 캡처 해제시 처리하기 - OnLostMouseCapture(e)
/// <summary>
/// 마우스 캡처 해제시 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnLostMouseCapture(MouseEventArgs e)
{
base.OnLostMouseCapture(e);
InvalidateVisual();
}
#endregion
#region 키 DOWN 처리하기 - OnKeyDown(e)
/// <summary>
/// 키 DOWN 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if(e.Key == Key.Space || e.Key == Key.Enter)
{
e.Handled = true;
}
}
#endregion
#region 키 UP 처리하기 - OnKeyUp(e)
/// <summary>
/// 키 UP 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if(e.Key == Key.Space || e.Key == Key.Enter)
{
OnPreviewKnock();
OnKnock();
e.Handled = true;
}
}
#endregion
#region 측정하기 (오버라이딩) - MeasureOverride(availableSize)
/// <summary>
/// 측정하기 (오버라이딩)
/// </summary>
/// <param name="availableSize">이용 가능한 크기</param>
/// <returns>측정 크기</returns>
protected override Size MeasureOverride(Size availableSize)
{
this.formattedText = new FormattedText
(
Text,
CultureInfo.CurrentCulture,
FlowDirection,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
FontSize,
Foreground
);
Size desiredSize = new Size(Math.Max(48, this.formattedText.Width) + 4, this.formattedText.Height + 4);
desiredSize.Width += Padding.Left + Padding.Right;
desiredSize.Height += Padding.Top + Padding.Bottom;
desiredSize.Width = Math.Min(desiredSize.Width , availableSize.Width );
desiredSize.Height = Math.Min(desiredSize.Height, availableSize.Height);
return desiredSize;
}
#endregion
#region 렌더링 처리하기 - OnRender(drawingContext)
/// <summary>
/// 렌더링 처리하기
/// </summary>
/// <param name="drawingContext">DrawingContext</param>
protected override void OnRender(DrawingContext drawingContext)
{
Brush backgroundBrush = SystemColors.ControlBrush;
if(this.isMouseReallyOver && IsMouseCaptured)
{
backgroundBrush = SystemColors.ControlDarkBrush;
}
Pen pen = new Pen(Foreground, IsMouseOver ? 2 : 1);
drawingContext.DrawRoundedRectangle
(
backgroundBrush,
pen,
new Rect(new Point(0, 0), RenderSize),
4,
4
);
this.formattedText.SetForegroundBrush
(
IsEnabled ? Foreground : SystemColors.ControlDarkBrush
);
Point textPoint = new Point(2, 2);
switch(HorizontalContentAlignment)
{
case HorizontalAlignment.Left :
textPoint.X += Padding.Left;
break;
case HorizontalAlignment.Right :
textPoint.X += RenderSize.Width - this.formattedText.Width - Padding.Right;
break;
case HorizontalAlignment.Center :
case HorizontalAlignment.Stretch :
textPoint.X += (RenderSize.Width - this.formattedText.Width - Padding.Left - Padding.Right) / 2;
break;
}
switch(VerticalContentAlignment)
{
case VerticalAlignment.Top :
textPoint.Y += Padding.Top;
break;
case VerticalAlignment.Bottom :
textPoint.Y += RenderSize.Height - this.formattedText.Height - Padding.Bottom;
break;
case VerticalAlignment.Center :
case VerticalAlignment.Stretch :
textPoint.Y += (RenderSize.Height - this.formattedText.Height - Padding.Top - Padding.Bottom) / 2;
break;
}
drawingContext.DrawText(this.formattedText, textPoint);
}
#endregion
}
}