You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
9.1 KiB
259 lines
9.1 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Media;
|
|
|
|
namespace AIK.Behaviors
|
|
{
|
|
public static class PlaceholderBehavior
|
|
{
|
|
#region 依赖属性定义
|
|
private static readonly DependencyPropertyKey IsPlaceholderActivePropertyKey =
|
|
DependencyProperty.RegisterAttachedReadOnly(
|
|
"IsPlaceholderActive",
|
|
typeof(bool),
|
|
typeof(PlaceholderBehavior),
|
|
new PropertyMetadata(false));
|
|
|
|
public static readonly DependencyProperty IsPlaceholderActiveProperty = IsPlaceholderActivePropertyKey.DependencyProperty;
|
|
|
|
public static readonly DependencyProperty PlaceholderTextProperty =
|
|
DependencyProperty.RegisterAttached(
|
|
"PlaceholderText",
|
|
typeof(string),
|
|
typeof(PlaceholderBehavior),
|
|
new PropertyMetadata(string.Empty, OnPlaceholderTextChanged));
|
|
|
|
// 添加一个属性来存储原始绑定
|
|
private static readonly DependencyProperty OriginalBindingProperty =
|
|
DependencyProperty.RegisterAttached(
|
|
"OriginalBinding",
|
|
typeof(Binding),
|
|
typeof(PlaceholderBehavior));
|
|
|
|
// 添加一个属性来标记是否正在设置Placeholder
|
|
private static readonly DependencyProperty IsSettingPlaceholderProperty =
|
|
DependencyProperty.RegisterAttached(
|
|
"IsSettingPlaceholder",
|
|
typeof(bool),
|
|
typeof(PlaceholderBehavior),
|
|
new PropertyMetadata(false));
|
|
#endregion
|
|
|
|
#region 公共方法
|
|
public static bool GetIsPlaceholderActive(DependencyObject obj)
|
|
{
|
|
return (bool)obj.GetValue(IsPlaceholderActiveProperty);
|
|
}
|
|
|
|
public static string GetPlaceholderText(DependencyObject obj)
|
|
{
|
|
return (string)obj.GetValue(PlaceholderTextProperty);
|
|
}
|
|
|
|
public static void SetPlaceholderText(DependencyObject obj, string value)
|
|
{
|
|
obj.SetValue(PlaceholderTextProperty, value);
|
|
}
|
|
#endregion
|
|
|
|
#region 私有方法
|
|
private static bool GetIsSettingPlaceholder(DependencyObject obj)
|
|
{
|
|
return (bool)obj.GetValue(IsSettingPlaceholderProperty);
|
|
}
|
|
|
|
private static void SetIsSettingPlaceholder(DependencyObject obj, bool value)
|
|
{
|
|
obj.SetValue(IsSettingPlaceholderProperty, value);
|
|
}
|
|
|
|
private static Binding GetOriginalBinding(DependencyObject obj)
|
|
{
|
|
return (Binding)obj.GetValue(OriginalBindingProperty);
|
|
}
|
|
|
|
private static void SetOriginalBinding(DependencyObject obj, Binding value)
|
|
{
|
|
obj.SetValue(OriginalBindingProperty, value);
|
|
}
|
|
#endregion
|
|
|
|
#region 事件处理
|
|
private static void OnPlaceholderTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
if (d is TextBox textBox)
|
|
{
|
|
// 确保只注册一次事件
|
|
textBox.Loaded -= OnTextBoxLoaded;
|
|
textBox.GotFocus -= OnTextBoxGotFocus;
|
|
textBox.LostFocus -= OnTextBoxLostFocus;
|
|
textBox.TextChanged -= OnTextBoxTextChanged;
|
|
|
|
textBox.Loaded += OnTextBoxLoaded;
|
|
textBox.GotFocus += OnTextBoxGotFocus;
|
|
textBox.LostFocus += OnTextBoxLostFocus;
|
|
textBox.TextChanged += OnTextBoxTextChanged;
|
|
|
|
// 保存原始绑定
|
|
var binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
|
|
if (binding != null)
|
|
{
|
|
SetOriginalBinding(textBox, binding);
|
|
}
|
|
|
|
// 如果已经加载,立即初始化
|
|
if (textBox.IsLoaded)
|
|
{
|
|
InitializePlaceholder(textBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void OnTextBoxLoaded(object sender, RoutedEventArgs e)
|
|
{
|
|
InitializePlaceholder((TextBox)sender);
|
|
}
|
|
|
|
private static void OnTextBoxGotFocus(object sender, RoutedEventArgs e)
|
|
{
|
|
var textBox = (TextBox)sender;
|
|
if (GetIsPlaceholderActive(textBox))
|
|
{
|
|
// 标记开始设置Placeholder,避免触发数据源更新
|
|
SetIsSettingPlaceholder(textBox, true);
|
|
|
|
// 移除placeholder
|
|
textBox.Text = string.Empty;
|
|
textBox.SetValue(IsPlaceholderActivePropertyKey, false);
|
|
textBox.Foreground = Brushes.Black;
|
|
|
|
// 恢复数据绑定
|
|
RestoreBinding(textBox);
|
|
|
|
SetIsSettingPlaceholder(textBox, false);
|
|
}
|
|
}
|
|
|
|
private static void OnTextBoxLostFocus(object sender, RoutedEventArgs e)
|
|
{
|
|
var textBox = (TextBox)sender;
|
|
if (string.IsNullOrEmpty(textBox.Text))
|
|
{
|
|
ShowPlaceholder(textBox);
|
|
}
|
|
}
|
|
|
|
private static void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
|
{
|
|
var textBox = (TextBox)sender;
|
|
|
|
// 如果正在设置Placeholder,不处理TextChanged事件
|
|
if (GetIsSettingPlaceholder(textBox))
|
|
return;
|
|
|
|
// 只有在不是placeholder状态时才处理
|
|
if (!GetIsPlaceholderActive(textBox))
|
|
{
|
|
// 如果文本变为空,显示placeholder
|
|
if (string.IsNullOrEmpty(textBox.Text) && !textBox.IsFocused)
|
|
{
|
|
ShowPlaceholder(textBox);
|
|
}
|
|
else
|
|
{
|
|
// 正常文本变化,确保数据绑定是激活的
|
|
EnsureBindingActive(textBox);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 核心方法
|
|
private static void InitializePlaceholder(TextBox textBox)
|
|
{
|
|
// 检查当前文本是否为空或者就是Placeholder文本
|
|
var currentText = textBox.Text;
|
|
var placeholderText = GetPlaceholderText(textBox);
|
|
|
|
if (string.IsNullOrEmpty(currentText) || currentText == placeholderText)
|
|
{
|
|
ShowPlaceholder(textBox);
|
|
}
|
|
}
|
|
|
|
private static void ShowPlaceholder(TextBox textBox)
|
|
{
|
|
var placeholderText = GetPlaceholderText(textBox);
|
|
if (!string.IsNullOrEmpty(placeholderText))
|
|
{
|
|
// 标记开始设置Placeholder
|
|
SetIsSettingPlaceholder(textBox, true);
|
|
|
|
// 临时解除数据绑定,避免触发数据源更新
|
|
SuspendBinding(textBox);
|
|
|
|
// 设置Placeholder文本
|
|
textBox.Text = placeholderText;
|
|
textBox.SetValue(IsPlaceholderActivePropertyKey, true);
|
|
textBox.Foreground = new SolidColorBrush(Color.FromArgb(0xFF, 0xA0, 0xAE, 0xC0));
|
|
|
|
SetIsSettingPlaceholder(textBox, false);
|
|
}
|
|
}
|
|
|
|
// 临时解除数据绑定
|
|
private static void SuspendBinding(TextBox textBox)
|
|
{
|
|
// 清除绑定,但保留绑定表达式
|
|
var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
|
|
if (bindingExpression != null)
|
|
{
|
|
// 保存原始绑定(如果还没有保存的话)
|
|
if (GetOriginalBinding(textBox) == null)
|
|
{
|
|
var binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
|
|
SetOriginalBinding(textBox, binding);
|
|
}
|
|
|
|
// 清除绑定,这样设置文本就不会更新数据源
|
|
BindingOperations.ClearBinding(textBox, TextBox.TextProperty);
|
|
}
|
|
}
|
|
|
|
// 恢复数据绑定
|
|
private static void RestoreBinding(TextBox textBox)
|
|
{
|
|
var originalBinding = GetOriginalBinding(textBox);
|
|
if (originalBinding != null)
|
|
{
|
|
// 恢复原始绑定
|
|
textBox.SetBinding(TextBox.TextProperty, originalBinding);
|
|
|
|
// 立即更新数据源为空值
|
|
var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
|
|
bindingExpression?.UpdateSource();
|
|
}
|
|
}
|
|
|
|
// 确保数据绑定是激活的
|
|
private static void EnsureBindingActive(TextBox textBox)
|
|
{
|
|
var currentBinding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
|
|
var originalBinding = GetOriginalBinding(textBox);
|
|
|
|
// 如果没有当前绑定但有原始绑定,恢复绑定
|
|
if (currentBinding == null && originalBinding != null)
|
|
{
|
|
textBox.SetBinding(TextBox.TextProperty, originalBinding);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
|