月更
This commit is contained in:
179
AntDesignWPF/Behaviors/PasswordBoxBehavior.cs
Normal file
179
AntDesignWPF/Behaviors/PasswordBoxBehavior.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using Microsoft.Xaml.Behaviors;
|
||||
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Documents;
|
||||
using AntDesign.WPF.Helpers;
|
||||
|
||||
namespace AntDesign.WPF.Behaviors
|
||||
{
|
||||
|
||||
[TemplatePart(Name = PART_TextBox, Type = typeof(TextBox))]
|
||||
[TemplatePart(Name = PART_Eye, Type = typeof(ToggleButton))]
|
||||
public class PasswordBoxBehavior : Behavior<PasswordBox>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private const string PART_TextBox = "PART_TextBox";
|
||||
|
||||
private const string PART_Eye = "PART_Eye";
|
||||
|
||||
private static MethodInfo select;
|
||||
|
||||
private static PropertyInfo selection;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Attached Properties
|
||||
|
||||
private static readonly DependencyProperty TextBoxProperty =
|
||||
DependencyProperty.RegisterAttached("TextBox", typeof(TextBox), typeof(PasswordBoxBehavior), new PropertyMetadata(null));
|
||||
|
||||
private static TextBox GetTextBox(DependencyObject obj)
|
||||
{
|
||||
return (TextBox)obj.GetValue(TextBoxProperty);
|
||||
}
|
||||
|
||||
private static void SetTextBox(DependencyObject obj, TextBox value)
|
||||
{
|
||||
obj.SetValue(TextBoxProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
static PasswordBoxBehavior()
|
||||
{
|
||||
select = typeof(PasswordBox).GetMethod("Select", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
selection = typeof(PasswordBox).GetProperty("Selection", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
AssociatedObject.Loaded += OnLoaded;
|
||||
AssociatedObject.GotFocus += OnGotFocus;
|
||||
AssociatedObject.PasswordChanged += OnPasswordChanged;
|
||||
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
AssociatedObject.Loaded -= OnLoaded;
|
||||
AssociatedObject.GotFocus -= OnGotFocus;
|
||||
AssociatedObject.PasswordChanged -= OnPasswordChanged;
|
||||
|
||||
if (Input.GetEyeable(AssociatedObject))
|
||||
{
|
||||
AssociatedObject.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnEyeClick));
|
||||
}
|
||||
|
||||
base.OnDetaching();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get the insertion position index of the caret.
|
||||
/// </summary>
|
||||
private static int GetCaretIndex(TextSelection selection)
|
||||
{
|
||||
if (selection == null) return 0;
|
||||
|
||||
var tTextRange = selection.GetType().GetInterfaces().FirstOrDefault(i => i.Name == "ITextRange");
|
||||
var oStart = tTextRange?.GetProperty("Start")?.GetGetMethod()?.Invoke(selection, null);
|
||||
var value = oStart?.GetType().GetProperty("Offset", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(oStart, null) as int?;
|
||||
|
||||
return value.GetValueOrDefault(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the insertion position index of the caret.
|
||||
/// </summary>
|
||||
private static void SetCaretIndex(object obj, int index)
|
||||
{
|
||||
select.Invoke(obj, new object[] { index, 0 });
|
||||
}
|
||||
|
||||
private static void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var passwordBox = (PasswordBox)sender;
|
||||
var password = passwordBox.Password;
|
||||
|
||||
if (!string.IsNullOrEmpty(password))
|
||||
{
|
||||
Input.SetPassword(passwordBox, password);
|
||||
SetCaretIndex(passwordBox, password.Length);
|
||||
}
|
||||
|
||||
if (Input.GetEyeable(passwordBox))
|
||||
{
|
||||
SetTextBox(passwordBox, passwordBox.FindChild<TextBox>(PART_TextBox));
|
||||
passwordBox.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(OnEyeClick));
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnEyeClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var passwordBox = (PasswordBox)sender;
|
||||
TextBox textBox;
|
||||
|
||||
if (!(e.OriginalSource is ToggleButton button) || button.Name != PART_Eye ||
|
||||
(textBox = GetTextBox(passwordBox)) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
if (button.IsChecked.GetValueOrDefault())
|
||||
{
|
||||
textBox.CaretIndex = GetCaretIndex(selection?.GetValue(passwordBox, null) as TextSelection);
|
||||
textBox.Focus();
|
||||
} else
|
||||
{
|
||||
SetCaretIndex(passwordBox, textBox.CaretIndex);
|
||||
passwordBox.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnGotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is PasswordBox passwordBox)
|
||||
{
|
||||
TextBox textBox;
|
||||
|
||||
if (Input.GetEyeable(passwordBox) &&
|
||||
(textBox = GetTextBox(passwordBox)) != null &&
|
||||
textBox.Visibility == Visibility.Visible)
|
||||
{
|
||||
textBox.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnPasswordChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var passwordBox = (PasswordBox)sender;
|
||||
var password = passwordBox.Password;
|
||||
|
||||
// Sync password
|
||||
if (password != Input.GetPassword(passwordBox))
|
||||
{
|
||||
Input.SetPassword(passwordBox, password);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
19
AntDesignWPF/Behaviors/StylizedBehaviorCollection.cs
Normal file
19
AntDesignWPF/Behaviors/StylizedBehaviorCollection.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Windows;
|
||||
|
||||
using Microsoft.Xaml.Behaviors;
|
||||
|
||||
namespace AntDesign.WPF.Behaviors
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// From https://github.com/MahApps/MahApps.Metro/tree/1.6.1/src/MahApps.Metro/Behaviours/StylizedBehaviorCollection.cs
|
||||
/// </summary>
|
||||
public class StylizedBehaviorCollection : FreezableCollection<Behavior>
|
||||
{
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new StylizedBehaviorCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
151
AntDesignWPF/Behaviors/StylizedBehaviors.cs
Normal file
151
AntDesignWPF/Behaviors/StylizedBehaviors.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
namespace AntDesign.WPF.Behaviors
|
||||
{
|
||||
using System.Windows;
|
||||
|
||||
using global::Microsoft.Xaml.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// From https://github.com/MahApps/MahApps.Metro/tree/1.6.1/src/MahApps.Metro/Behaviours/StylizedBehaviors.cs
|
||||
/// </summary>
|
||||
public class StylizedBehaviors
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached(
|
||||
"Behaviors",
|
||||
typeof(StylizedBehaviorCollection),
|
||||
typeof(StylizedBehaviors),
|
||||
new PropertyMetadata(null, OnBehaviorsChanged));
|
||||
|
||||
public static StylizedBehaviorCollection GetBehaviors(DependencyObject obj)
|
||||
{
|
||||
return (StylizedBehaviorCollection)obj.GetValue(BehaviorsProperty);
|
||||
}
|
||||
|
||||
public static void SetBehaviors(DependencyObject obj, StylizedBehaviorCollection value)
|
||||
{
|
||||
obj.SetValue(BehaviorsProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty OriginalBehaviorProperty = DependencyProperty.RegisterAttached(
|
||||
"OriginalBehavior",
|
||||
typeof(Behavior),
|
||||
typeof(StylizedBehaviors),
|
||||
new UIPropertyMetadata(null));
|
||||
|
||||
public static Behavior GetOriginalBehavior(DependencyObject obj)
|
||||
{
|
||||
return (Behavior)obj.GetValue(OriginalBehaviorProperty);
|
||||
}
|
||||
|
||||
public static void SetOriginalBehavior(DependencyObject obj, Behavior value)
|
||||
{
|
||||
obj.SetValue(OriginalBehaviorProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void OnBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var fe = d as FrameworkElement;
|
||||
if (fe == null) return;
|
||||
|
||||
var newVal = e.NewValue as StylizedBehaviorCollection;
|
||||
var oldVal = e.OldValue as StylizedBehaviorCollection;
|
||||
|
||||
if (newVal == oldVal) return;
|
||||
|
||||
var behaviors = Interaction.GetBehaviors(fe);
|
||||
fe.Unloaded -= OnUnloaded;
|
||||
|
||||
|
||||
if (oldVal != null)
|
||||
{
|
||||
foreach (var behavior in oldVal)
|
||||
{
|
||||
var index = GetIndexOf(behaviors, behavior);
|
||||
if (index >= 0)
|
||||
{
|
||||
behaviors.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newVal != null)
|
||||
{
|
||||
foreach (var behavior in newVal)
|
||||
{
|
||||
var index = GetIndexOf(behaviors, behavior);
|
||||
if (index < 0)
|
||||
{
|
||||
var clone = (Behavior)behavior.Clone();
|
||||
SetOriginalBehavior(clone, behavior);
|
||||
behaviors.Add(clone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (behaviors.Count > 0)
|
||||
{
|
||||
fe.Unloaded += OnUnloaded;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var fe = sender as FrameworkElement;
|
||||
if (fe == null) return;
|
||||
|
||||
fe.Loaded -= OnLoaded;
|
||||
var behaviors = Interaction.GetBehaviors(fe);
|
||||
|
||||
foreach (var behavior in behaviors)
|
||||
{
|
||||
behavior.Attach(fe);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var fe = sender as FrameworkElement;
|
||||
if (fe == null) return;
|
||||
|
||||
var behaviors = Interaction.GetBehaviors(fe);
|
||||
foreach (var behavior in behaviors)
|
||||
{
|
||||
behavior.Detach();
|
||||
}
|
||||
|
||||
fe.Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private static int GetIndexOf(BehaviorCollection items, Behavior behavior)
|
||||
{
|
||||
int index = -1;
|
||||
var orignalBehavior = GetOriginalBehavior(behavior);
|
||||
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
var currentBehavior = items[i];
|
||||
if (currentBehavior == behavior || currentBehavior == orignalBehavior)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
var currentOrignalBehavior = GetOriginalBehavior(currentBehavior);
|
||||
if (currentOrignalBehavior == behavior || currentOrignalBehavior == orignalBehavior)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
101
AntDesignWPF/Behaviors/VisibilityBehavior.cs
Normal file
101
AntDesignWPF/Behaviors/VisibilityBehavior.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
using Microsoft.Xaml.Behaviors;
|
||||
namespace AntDesign.WPF.Behaviors
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the ability to animate when changing the Visibility property for an element.
|
||||
/// </summary>
|
||||
public class VisibilityBehavior : Behavior<FrameworkElement>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private Visibility visibility;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public static readonly DependencyProperty AppearProperty =
|
||||
DependencyProperty.Register("Appear", typeof(Storyboard), typeof(VisibilityBehavior), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the storyboard when the element is visible.
|
||||
/// </summary>
|
||||
public Storyboard Appear
|
||||
{
|
||||
get { return (Storyboard)GetValue(AppearProperty); }
|
||||
set { SetValue(AppearProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LeaveProperty =
|
||||
DependencyProperty.Register("Leave", typeof(Storyboard), typeof(VisibilityBehavior), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the storyboard when the element is hidden.
|
||||
/// </summary>
|
||||
public Storyboard Leave
|
||||
{
|
||||
get { return (Storyboard)GetValue(LeaveProperty); }
|
||||
set { SetValue(LeaveProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
visibility = AssociatedObject.Visibility;
|
||||
AssociatedObject.IsVisibleChanged += OnVisibleChanged;
|
||||
|
||||
base.OnAttached();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AssociatedObject.IsVisibleChanged += OnVisibleChanged;
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
AssociatedObject.IsVisibleChanged -= OnVisibleChanged;
|
||||
|
||||
base.OnDetaching();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (visibility == AssociatedObject.Visibility) return;
|
||||
|
||||
Storyboard storyboard;
|
||||
|
||||
if (AssociatedObject.Visibility == Visibility.Visible)
|
||||
{
|
||||
if (Appear == null) return;
|
||||
|
||||
storyboard = Appear;
|
||||
visibility = AssociatedObject.Visibility;
|
||||
|
||||
} else
|
||||
{
|
||||
if (Leave == null) return;
|
||||
|
||||
var cache = AssociatedObject.Visibility;
|
||||
AssociatedObject.SetCurrentValue(UIElement.VisibilityProperty, visibility = Visibility.Visible);
|
||||
|
||||
storyboard = Leave.Clone();
|
||||
storyboard.Completed += (s, a) => AssociatedObject.SetCurrentValue(UIElement.VisibilityProperty, visibility = cache);
|
||||
}
|
||||
|
||||
AssociatedObject.BeginStoryboard(storyboard);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user