更新
This commit is contained in:
534
WPFDark/Controls/BiaTextBox.xaml.cs
Normal file
534
WPFDark/Controls/BiaTextBox.xaml.cs
Normal file
@@ -0,0 +1,534 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using WPFDark.Internals;
|
||||
|
||||
|
||||
namespace WPFDark.Controls
|
||||
{
|
||||
public class BiaTextBox : FrameworkElement
|
||||
{
|
||||
#region Text
|
||||
|
||||
public string? Text
|
||||
{
|
||||
get => _Text;
|
||||
set
|
||||
{
|
||||
if (value != _Text)
|
||||
SetValue(TextProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private string? _Text;
|
||||
|
||||
public static readonly DependencyProperty TextProperty =
|
||||
DependencyProperty.Register(nameof(Text), typeof(string), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
default(string),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._Text = e.NewValue?.ToString() ?? "";
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Watermark
|
||||
|
||||
//public string? Watermark
|
||||
//{
|
||||
// get => _Watermark;
|
||||
// set
|
||||
// {
|
||||
// if (value != _Watermark)
|
||||
// SetValue(WatermarkProperty, value);
|
||||
// }
|
||||
//}
|
||||
//private string? _Watermark;
|
||||
|
||||
public string Watermark
|
||||
{
|
||||
get { return (string)GetValue(WatermarkProperty); }
|
||||
set { SetValue(WatermarkProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty WatermarkProperty =
|
||||
DependencyProperty.Register(nameof(Watermark), typeof(string), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
default(string),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self.Watermark = e.NewValue?.ToString() ?? "";
|
||||
}));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region WatermarkForeground
|
||||
|
||||
public Brush? WatermarkForeground
|
||||
{
|
||||
get => _WatermarkForeground;
|
||||
set
|
||||
{
|
||||
if (value != _WatermarkForeground)
|
||||
SetValue(WatermarkForegroundProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Brush? _WatermarkForeground;
|
||||
|
||||
public static readonly DependencyProperty WatermarkForegroundProperty =
|
||||
DependencyProperty.Register(nameof(WatermarkForeground), typeof(Brush), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
default(Brush),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._WatermarkForeground = (Brush) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsReadOnly
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get => _IsReadOnly;
|
||||
set
|
||||
{
|
||||
if (value != _IsReadOnly)
|
||||
SetValue(IsReadOnlyProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsReadOnly;
|
||||
|
||||
public static readonly DependencyProperty IsReadOnlyProperty =
|
||||
DependencyProperty.Register(nameof(IsReadOnly), typeof(bool), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
false,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._IsReadOnly = (bool) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Background
|
||||
|
||||
public Brush? Background
|
||||
{
|
||||
get => _Background;
|
||||
set
|
||||
{
|
||||
if (value != _Background)
|
||||
SetValue(BackgroundProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Brush? _Background;
|
||||
|
||||
public static readonly DependencyProperty BackgroundProperty =
|
||||
DependencyProperty.Register(nameof(Background), typeof(Brush), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
default(Brush),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._Background = (Brush) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Foreground
|
||||
|
||||
public Brush? Foreground
|
||||
{
|
||||
get => _Foreground;
|
||||
set
|
||||
{
|
||||
if (value != _Foreground)
|
||||
SetValue(ForegroundProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private Brush? _Foreground;
|
||||
|
||||
public static readonly DependencyProperty ForegroundProperty =
|
||||
DependencyProperty.Register(nameof(Foreground), typeof(Brush), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
default(Brush),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._Foreground = (Brush) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region BorderColor
|
||||
|
||||
public ByteColor BorderColor
|
||||
{
|
||||
get => _BorderColor;
|
||||
set
|
||||
{
|
||||
if (value != _BorderColor)
|
||||
SetValue(BorderColorProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private ByteColor _BorderColor = ByteColor.Red;
|
||||
|
||||
public static readonly DependencyProperty BorderColorProperty =
|
||||
DependencyProperty.Register(nameof(BorderColor), typeof(ByteColor), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
Boxes.ByteColorRed,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._BorderColor = (ByteColor) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region CornerRadius
|
||||
|
||||
public double CornerRadius
|
||||
{
|
||||
get => _CornerRadius;
|
||||
set
|
||||
{
|
||||
if (NumberHelper.AreClose(value, _CornerRadius) == false)
|
||||
SetValue(CornerRadiusProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private double _CornerRadius;
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusProperty =
|
||||
DependencyProperty.Register(nameof(CornerRadius), typeof(double), typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
0.0,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._CornerRadius = (double) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
#region TextTrimming
|
||||
|
||||
public BiaTextTrimmingMode TextTrimming
|
||||
{
|
||||
get => _TextTrimming;
|
||||
set
|
||||
{
|
||||
if (value != _TextTrimming)
|
||||
SetValue(TextTrimmingProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
private BiaTextTrimmingMode _TextTrimming = BiaTextTrimmingMode.Standard;
|
||||
|
||||
public static readonly DependencyProperty TextTrimmingProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(TextTrimming),
|
||||
typeof(BiaTextTrimmingMode),
|
||||
typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(
|
||||
BiaTextTrimmingMode.Standard,
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender |
|
||||
FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
(s, e) =>
|
||||
{
|
||||
var self = (BiaTextBox) s;
|
||||
self._TextTrimming = (BiaTextTrimmingMode) e.NewValue;
|
||||
}));
|
||||
|
||||
#endregion
|
||||
|
||||
static BiaTextBox()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(BiaTextBox),
|
||||
new FrameworkPropertyMetadata(typeof(BiaTextBox)));
|
||||
}
|
||||
|
||||
public BiaTextBox()
|
||||
{
|
||||
IsEnabledChanged += (_, __) => InvalidateVisual();
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc)
|
||||
{
|
||||
if (ActualWidth <= 1 ||
|
||||
ActualHeight <= 1)
|
||||
return;
|
||||
|
||||
var rounder = new LayoutRounder(this);
|
||||
|
||||
DrawBackground(rounder, dc);
|
||||
|
||||
var isCornerRadiusZero = NumberHelper.AreCloseZero(CornerRadius);
|
||||
|
||||
if (isCornerRadiusZero == false)
|
||||
dc.PushClip(Caches.GetClipGeom(rounder, ActualWidth, ActualHeight, CornerRadius, true));
|
||||
{
|
||||
if (_isEditing == false &&
|
||||
string.IsNullOrEmpty(TargetText) &&
|
||||
string.IsNullOrEmpty(Watermark) == false &&
|
||||
WatermarkForeground != null)
|
||||
TextRenderer.Italic.Draw(
|
||||
this,
|
||||
Watermark,
|
||||
5d, 4d,
|
||||
WatermarkForeground,
|
||||
dc,
|
||||
(1d, ActualWidth - 9d).Max(),
|
||||
TextAlignment.Left,
|
||||
TextTrimming,
|
||||
true);
|
||||
|
||||
if (string.IsNullOrEmpty(TargetText) == false &&
|
||||
Foreground != null)
|
||||
DefaultTextRenderer.Instance.Draw(
|
||||
this,
|
||||
TargetText,
|
||||
5d, 4d,
|
||||
Foreground,
|
||||
dc,
|
||||
(1d, ActualWidth - 9d).Max(),
|
||||
TextAlignment.Left,
|
||||
TextTrimming,
|
||||
true);
|
||||
}
|
||||
|
||||
if (isCornerRadiusZero == false)
|
||||
dc.Pop();
|
||||
}
|
||||
|
||||
private string? TargetText => _isEditing ? _textBox?.Text : Text;
|
||||
|
||||
private void DrawBackground(in LayoutRounder rounder, DrawingContext dc)
|
||||
{
|
||||
var brush = Background;
|
||||
|
||||
if (NumberHelper.AreCloseZero(CornerRadius))
|
||||
dc.DrawRectangle(
|
||||
brush,
|
||||
rounder.GetBorderPen(BorderColor),
|
||||
rounder.RoundRenderRectangle(true));
|
||||
else
|
||||
dc.DrawRoundedRectangle(
|
||||
brush,
|
||||
rounder.GetBorderPen(BorderColor),
|
||||
rounder.RoundRenderRectangle(true),
|
||||
CornerRadius,
|
||||
CornerRadius);
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonUp(e);
|
||||
|
||||
if (IsEnabled == false)
|
||||
return;
|
||||
|
||||
if (_isEditing)
|
||||
{
|
||||
if (this.IsInActualSize(e.GetPosition(this)) == false)
|
||||
{
|
||||
FinishEditing(true);
|
||||
ReleaseMouseCapture();
|
||||
|
||||
Win32Helper.mouse_event(Win32Helper.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Focus();
|
||||
ShowEditBox();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
|
||||
if (IsEnabled == false)
|
||||
return;
|
||||
|
||||
if (e.Key == Key.Return)
|
||||
{
|
||||
ShowEditBox();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private TextBox? _textBox;
|
||||
private bool _isEditing;
|
||||
|
||||
private void ShowEditBox()
|
||||
{
|
||||
if (_textBox is null)
|
||||
{
|
||||
_textBox = new TextBox
|
||||
{
|
||||
IsTabStop = false,
|
||||
IsUndoEnabled = false,
|
||||
FocusVisualStyle = null,
|
||||
SelectionLength = 0,
|
||||
};
|
||||
|
||||
_textBox.TextChanged += TextBox_OnTextChanged;
|
||||
_textBox.PreviewKeyDown += TextBox_OnPreviewKeyDown;
|
||||
_textBox.PreviewMouseDown += TextBox_OnPreviewMouseDown;
|
||||
}
|
||||
|
||||
_textBox.Width = ActualWidth;
|
||||
_textBox.Height = ActualHeight;
|
||||
_textBox.IsReadOnly = IsReadOnly;
|
||||
_textBox.Text = Text;
|
||||
|
||||
AddVisualChild(_textBox);
|
||||
InvalidateMeasure();
|
||||
|
||||
_textBox.SelectAll();
|
||||
_textBox.Focus();
|
||||
_textBox.CaptureMouse();
|
||||
|
||||
_isEditing = true;
|
||||
}
|
||||
|
||||
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private void TextBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Tab:
|
||||
{
|
||||
FinishEditing(true);
|
||||
|
||||
var t = Keyboard.Modifiers == ModifierKeys.Shift
|
||||
? Caches.PreviousTraversalRequest
|
||||
: Caches.NextTraversalRequest;
|
||||
MoveFocus(t);
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Key.Return:
|
||||
{
|
||||
FinishEditing(true);
|
||||
|
||||
Dispatcher?.BeginInvoke(DispatcherPriority.Input, (Action) (() => Focus()));
|
||||
|
||||
e.Handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case Key.Escape:
|
||||
{
|
||||
FinishEditing(false);
|
||||
|
||||
Dispatcher?.BeginInvoke(DispatcherPriority.Input, (Action) (() => Focus()));
|
||||
|
||||
e.Handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TextBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var rounder = new LayoutRounder(this);
|
||||
|
||||
// 自コントロール上であれば、終了させない
|
||||
var pos = e.GetPosition(this);
|
||||
var rect = rounder.RoundRenderRectangle(false);
|
||||
if (rect.Contains(pos))
|
||||
return;
|
||||
|
||||
if (_isEditing)
|
||||
FinishEditing(true);
|
||||
}
|
||||
|
||||
private void FinishEditing(bool isEdit)
|
||||
{
|
||||
if (isEdit)
|
||||
{
|
||||
Text = _textBox?.Text;
|
||||
GetBindingExpression(TextProperty)?.UpdateSource();
|
||||
}
|
||||
|
||||
RemoveVisualChild(_textBox);
|
||||
_isEditing = false;
|
||||
InvalidateVisual();
|
||||
|
||||
if (IsMouseCaptured)
|
||||
ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount
|
||||
=> _isEditing ? 1 : 0;
|
||||
|
||||
protected override Visual? GetVisualChild(int index)
|
||||
=> _textBox;
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (_isEditing)
|
||||
_textBox?.Arrange(new Rect(new Point(0, 0), _textBox.DesiredSize));
|
||||
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (_isEditing)
|
||||
_textBox?.Measure(new Size(ActualWidth, ActualHeight));
|
||||
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user