兼容win7版应用
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

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