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); } } }