using System; using System.ComponentModel; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; namespace UILib { public enum EditBoxType { String = 0, Bool = 1 } public class RelayCommand : ICommand { private readonly Action _execute; private readonly Func _canExecute; public RelayCommand(Action execute, Func canExecute = null) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } public event EventHandler CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true; public void Execute(object parameter) => _execute(); } public class EditBoxConverter : IMultiValueConverter { public static EditBoxConverter Instance = new EditBoxConverter(); public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values[0] is EditBox eb) { if (parameter?.ToString() == "StringFormat") { return eb.EditType == EditBoxType.String && eb.FormatString ? $"\"{values[1]}\"" : values[1]?.ToString(); } if (parameter?.ToString() == "BoolIndex") { if (values[1] == null) return null; return string.Equals("true", values[1]?.ToString(), StringComparison.OrdinalIgnoreCase) ? 0 : 1; } } return null; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { return null; } } public class EditBox : Control { static EditBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(EditBox), new FrameworkPropertyMetadata(typeof(EditBox))); } public static readonly DependencyProperty EditTypeProperty = DependencyProperty.Register( "EditType", typeof(EditBoxType), typeof(EditBox), new PropertyMetadata(EditBoxType.String)); public static readonly DependencyProperty FormatStringProperty = DependencyProperty.Register( "FormatString", typeof(bool), typeof(EditBox), new PropertyMetadata(false)); public static readonly DependencyProperty InitEditProperty = DependencyProperty.Register( "InitEdit", typeof(bool), typeof(EditBox)); public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(object), typeof(EditBox), new FrameworkPropertyMetadata()); public static readonly DependencyProperty CanEditProperty = DependencyProperty.Register( "CanEdit", typeof(bool), typeof(EditBox), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnCanEditPropertyChanged))); public static DependencyProperty IsEditingProperty = DependencyProperty.Register( "IsEditing", typeof(bool), typeof(EditBox), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsEditingPropertyChanged))); public static DependencyProperty PreEditingProperty = DependencyProperty.Register( "PreEditing", typeof(Func), typeof(EditBox), new FrameworkPropertyMetadata()); public static DependencyProperty PreSubmitProperty = DependencyProperty.Register( "PreSubmit", typeof(Func), typeof(EditBox), new FrameworkPropertyMetadata()); public static DependencyProperty SubmitedProperty = DependencyProperty.Register( "Submited", typeof(Action), typeof(EditBox), new FrameworkPropertyMetadata()); public static DependencyProperty ShowEmptyProperty = DependencyProperty.Register( "ShowEmpty", typeof(bool), typeof(EditBox), new FrameworkPropertyMetadata(false)); private static void OnCanEditPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (obj is EditBox eb && e.Property == CanEditProperty) { bool value = (bool)e.NewValue; //if (value) //{ // eb.PreviewMouseDoubleClick += eb.EditBox_MouseDoubleClick; //} //else //{ // eb.PreviewMouseDoubleClick -= eb.EditBox_MouseDoubleClick; //} } } private static void OnIsEditingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (obj is EditBox eb && e.Property == IsEditingProperty) { bool value = (bool)e.NewValue; if (eb.EditType == EditBoxType.String) { eb.editing.Visibility = value ? Visibility.Visible : Visibility.Collapsed; if (value) { eb.editing.Focus(); eb.editing.SelectAll(); eb.editing.KeyDown += eb.EditBox_KeyDown; } else { eb.editing.KeyDown -= eb.EditBox_KeyDown; } } else if (eb.EditType == EditBoxType.Bool) { eb.editingComboBox.Visibility = value ? Visibility.Visible : Visibility.Collapsed; if (value) { eb.editingComboBox.Focus(); eb.editingComboBox.IsDropDownOpen = true; eb.editingComboBox.KeyDown += eb.EditBox_KeyDown; } else { eb.editingComboBox.KeyDown -= eb.EditBox_KeyDown; } } } } private static void OnInitEditPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (obj is EditBox eb && e.Property == InitEditProperty) { bool value = (bool)e.NewValue; if (eb.editing != null || !value) { return; } if (value) { } } } public bool FormatString { get => (bool)GetValue(FormatStringProperty); set { SetValue(FormatStringProperty, value); } } public EditBoxType EditType { get => (EditBoxType)GetValue(EditTypeProperty); set { SetValue(EditTypeProperty, value); } } public TextBox editing { get; private set; } private ComboBox editingComboBox; public EditBox() { this.MouseDoubleClick += EditBox_MouseDoubleClick; //if (CanEdit) //{ //} this.InputBindings.Add(new KeyBinding( new RelayCommand(() => TryEdit(null)), Key.F2, ModifierKeys.None )); this.Loaded += EditBox_Loaded; } private void EditBox_Loaded(object sender, RoutedEventArgs e) { //var control = this.Template.FindName("PART_TextBoxPart", this) as ContentControl; //editing = control.Content as FrameworkElement; if (EditType == EditBoxType.String) { editing = this.Template.FindName("PART_TextBoxPart", this) as TextBox; } else { editingComboBox = this.Template.FindName("PART_ComboBoxPart", this) as ComboBox; } //editing = control.Content as FrameworkElement; } private void EditBox_MouseDoubleClick(object sender, MouseButtonEventArgs e) { TryEdit(e); } public void TryEdit(MouseButtonEventArgs e) { if (!CanEdit) { return; } if (IsEditing) { return; } Func pre = GetValue(PreEditingProperty) as Func; if (pre != null && pre.Invoke(this) != true) { return; } var win = Window.GetWindow(this); win.MouseDown += Win_MouseDown; if (EditType == EditBoxType.String) { editing.Focus(); //editing.LostFocus += TextBox_LostFocus; this.LostFocus += EditBox_LostFocus; } else { editingComboBox.DropDownClosed += EditingComboBox_DropDownClosed; } IsEditing = true; if (e != null) { e.Handled = true; } } private void EditingComboBox_DropDownClosed(object sender, EventArgs e) { CancelEditing(sender); } public static bool GetElementUnderMouse(UIElement relativeTo, UIElement target) { Point mousePosition = Mouse.GetPosition(relativeTo); bool res = false; HitTestResultCallback resultCallback = (result) => { // 获取相交的UI元素 var element = result.VisualHit as FrameworkElement; if (element != null && element == target) { res = true; return HitTestResultBehavior.Stop; // 找到后停止遍历 } return HitTestResultBehavior.Continue; }; VisualTreeHelper.HitTest(relativeTo, null, resultCallback, new PointHitTestParameters(mousePosition)); return res; } private void EditBox_LostFocus(object sender, RoutedEventArgs e) { CancelEditing(sender); } private void TextBox_LostFocus(object sender, RoutedEventArgs e) { CancelEditing(sender); } private void Win_MouseDown(object sender, RoutedEventArgs e) { CancelEditing(sender); } private void EditBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { CancelEditing(sender); } } private void CancelEditing(object sender) { if (EditType == EditBoxType.String) { if (PreSubmit != null && !PreSubmit.Invoke(this, editing.Text, DataContext)) { editing.Text = Value?.ToString(); Dispatcher.BeginInvoke(new Action(() => { editing.Focus(); }), System.Windows.Threading.DispatcherPriority.Input); //editing.LostFocus += TextBox_LostFocus; return; } } if (EditType == EditBoxType.String) { IsEditing = false; this.LostFocus -= EditBox_LostFocus; editing.LostFocus -= TextBox_LostFocus; Window.GetWindow(this).MouseDown -= Win_MouseDown; } else if (EditType == EditBoxType.Bool) { IsEditing = false; editingComboBox.DropDownClosed -= EditingComboBox_DropDownClosed; Window.GetWindow(this).MouseDown -= Win_MouseDown; } else { var win = sender as Window; if (!GetElementUnderMouse(win, this)) { //editing.Focu IsEditing = false; win.MouseDown -= Win_MouseDown; Window.GetWindow(this).MouseDown -= Win_MouseDown; } } Submited?.Invoke(this, null, null); } [Bindable(true)] [Category("Appearance")] public object Value { get { return GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public bool CanEdit { get => (bool)GetValue(CanEditProperty); set { SetValue(CanEditProperty, value); } } public bool IsEditing { get => (bool)GetValue(IsEditingProperty); set { SetValue(IsEditingProperty, value); } } public Func PreEditing { get => (Func)GetValue(PreEditingProperty); set { SetValue(PreEditingProperty, value); } } public Func PreSubmit { get => (Func)GetValue(PreSubmitProperty); set { SetValue(PreSubmitProperty, value); } } public Action Submited { get => (Action)GetValue(SubmitedProperty); set { SetValue(SubmitedProperty, value); } } public bool ShowEmpty { get => (bool)GetValue(ShowEmptyProperty); set { SetValue(ShowEmptyProperty, value); } } public bool InitEdit { get => (bool)GetValue(InitEditProperty); set { SetValue(InitEditProperty, value); } } } }