|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- using System;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Controls.Primitives;
- using System.Windows.Data;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using BPASmartClient.MilkWithTea.Control;
- using BPASmartClient.MilkWithTea.Data;
- using TabControl = BPASmartClient.MilkWithTea.Control.TabControl;
-
- namespace BPASmartClient.MilkWithTea.Control;
-
- public class TabItem : System.Windows.Controls.TabItem
- {
- /// <summary>
- /// 动画速度
- /// </summary>
- private const int AnimationSpeed = 150;
-
- /// <summary>
- /// 选项卡是否处于拖动状态
- /// </summary>
- private static bool ItemIsDragging;
-
- /// <summary>
- /// 选项卡是否等待被拖动
- /// </summary>
- private bool _isWaiting;
-
- /// <summary>
- /// 拖动中的选项卡坐标
- /// </summary>
- private Point _dragPoint;
-
- /// <summary>
- /// 鼠标按下时选项卡位置
- /// </summary>
- private int _mouseDownIndex;
-
- /// <summary>
- /// 鼠标按下时选项卡横向偏移
- /// </summary>
- private double _mouseDownOffsetX;
-
- /// <summary>
- /// 鼠标按下时的坐标
- /// </summary>
- private Point _mouseDownPoint;
-
- /// <summary>
- /// 右侧可移动的最大值
- /// </summary>
- private double _maxMoveRight;
-
- /// <summary>
- /// 左侧可移动的最大值
- /// </summary>
- private double _maxMoveLeft;
-
- /// <summary>
- /// 选项卡宽度
- /// </summary>
- public double ItemWidth { get; internal set; }
-
- /// <summary>
- /// 选项卡拖动等待距离(在鼠标移动了超过20个像素无关单位后,选项卡才开始被拖动)
- /// </summary>
- private const double WaitLength = 20;
-
- /// <summary>
- /// 选项卡是否处于拖动状态
- /// </summary>
- private bool _isDragging;
-
- /// <summary>
- /// 选项卡是否已经被拖动
- /// </summary>
- private bool _isDragged;
-
- /// <summary>
- /// 目标横向位移
- /// </summary>
- internal double TargetOffsetX { get; set; }
-
- /// <summary>
- /// 当前编号
- /// </summary>
- private int _currentIndex;
-
- /// <summary>
- /// 标签容器横向滚动距离
- /// </summary>
- private double _scrollHorizontalOffset;
-
-
- private TabPanel _tabPanel;
-
-
- /// <summary>
- /// 标签容器
- /// </summary>
- internal TabPanel TabPanel
- {
- get
- {
- if (_tabPanel == null && TabControlParent != null)
- {
- _tabPanel = TabControlParent.HeaderPanel;
- }
-
- return _tabPanel;
- }
- set => _tabPanel = value;
- }
-
- private TabControl TabControlParent => ItemsControl.ItemsControlFromItemContainer(this) as TabControl;
-
-
- /// <summary>
- /// 当前编号
- /// </summary>
- internal int CurrentIndex
- {
- get => _currentIndex;
- set
- {
- if (_currentIndex == value || value < 0) return;
- var oldIndex = _currentIndex;
- _currentIndex = value;
- UpdateItemOffsetX(oldIndex);
- }
- }
-
- /// <summary>
- /// 是否显示关闭按钮
- /// </summary>
- public static readonly DependencyProperty ShowCloseButtonProperty =
- TabControl.ShowCloseButtonProperty.AddOwner(typeof(TabItem));
-
- /// <summary>
- /// 是否显示关闭按钮
- /// </summary>
- public bool ShowCloseButton
- {
- get => (bool) GetValue(ShowCloseButtonProperty);
- set => SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
- }
-
- public static void SetShowCloseButton(DependencyObject element, bool value)
- => element.SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
-
- public static bool GetShowCloseButton(DependencyObject element)
- => (bool) element.GetValue(ShowCloseButtonProperty);
-
- /// <summary>
- /// 是否显示上下文菜单
- /// </summary>
- public static readonly DependencyProperty ShowContextMenuProperty =
- TabControl.ShowContextMenuProperty.AddOwner(typeof(TabItem), new FrameworkPropertyMetadata(OnShowContextMenuChanged));
-
- private static void OnShowContextMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var ctl = (TabItem) d;
- if (ctl.Menu != null)
- {
- var show = (bool) e.NewValue;
- ctl.Menu.IsEnabled = show;
- //ctl.Menu.Show(show);
- }
- }
-
- /// <summary>
- /// 是否显示上下文菜单
- /// </summary>
- public bool ShowContextMenu
- {
- get => (bool) GetValue(ShowContextMenuProperty);
- set => SetValue(ShowContextMenuProperty, ValueBoxes.BooleanBox(value));
- }
-
- public static void SetShowContextMenu(DependencyObject element, bool value)
- => element.SetValue(ShowContextMenuProperty, ValueBoxes.BooleanBox(value));
-
- public static bool GetShowContextMenu(DependencyObject element)
- => (bool) element.GetValue(ShowContextMenuProperty);
-
- public static readonly DependencyProperty MenuProperty = DependencyProperty.Register(
- "Menu", typeof(ContextMenu), typeof(TabItem), new PropertyMetadata(default(ContextMenu), OnMenuChanged));
-
- private static void OnMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var ctl = (TabItem) d;
- //ctl.OnMenuChanged(e.NewValue as ContextMenu);
- }
-
- //private void OnMenuChanged(ContextMenu menu)
- //{
- // if (IsLoaded && menu != null)
- // {
- // var parent = TabControlParent;
- // if (parent == null) return;
-
- // var item = parent.ItemContainerGenerator.ItemFromContainer(this);
-
- // menu.DataContext = item;
- // menu.SetBinding(IsEnabledProperty, new Binding(ShowContextMenuProperty.Name)
- // {
- // Source = this
- // });
- // menu.SetBinding(VisibilityProperty, new Binding(ShowContextMenuProperty.Name)
- // {
- // Source = this,
- // Converter = GetResourceInternal<IValueConverter>(ResourceToken.Boolean2VisibilityConverter)
- // });
- // }
- //}
-
-
-
- public ContextMenu Menu
- {
- get => (ContextMenu) GetValue(MenuProperty);
- set => SetValue(MenuProperty, value);
- }
-
- /// <summary>
- /// 更新选项卡横向偏移
- /// </summary>
- /// <param name="oldIndex"></param>
- private void UpdateItemOffsetX(int oldIndex)
- {
- if (!_isDragging || CurrentIndex >= TabPanel.ItemDic.Count)
- {
- return;
- }
-
- var moveItem = TabPanel.ItemDic[CurrentIndex];
- moveItem.CurrentIndex -= CurrentIndex - oldIndex;
- var offsetX = moveItem.TargetOffsetX;
- var resultX = offsetX + (oldIndex - CurrentIndex) * ItemWidth;
- TabPanel.ItemDic[CurrentIndex] = this;
- TabPanel.ItemDic[moveItem.CurrentIndex] = moveItem;
- moveItem.CreateAnimation(offsetX, resultX);
- }
-
- public TabItem()
- {
- //CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close()));
- //CommandBindings.Add(new CommandBinding(ControlCommands.CloseAll,
- // (s, e) => { TabControlParent.CloseAllItems(); }));
- //CommandBindings.Add(new CommandBinding(ControlCommands.CloseOther,
- // (s, e) => { TabControlParent.CloseOtherItems(this); }));
-
- //Loaded += (s, e) => OnMenuChanged(Menu);
- }
-
-
- protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
- {
- base.OnMouseRightButtonDown(e);
-
- if (VisualTreeHelper.HitTest(this, e.GetPosition(this)) == null) return;
- IsSelected = true;
- Focus();
- }
-
- protected override void OnHeaderChanged(object oldHeader, object newHeader)
- {
- base.OnHeaderChanged(oldHeader, newHeader);
-
- if (TabPanel != null)
- {
- TabPanel.ForceUpdate = true;
- InvalidateMeasure();
- TabPanel.ForceUpdate = true;
- }
- }
-
- internal void Close()
- {
- var parent = TabControlParent;
- if (parent == null) return;
-
- var item = parent.ItemContainerGenerator.ItemFromContainer(this);
-
- var argsClosing = new CancelRoutedEventArgs(ClosingEvent, item);
- RaiseEvent(argsClosing);
- if (argsClosing.Cancel) return;
-
- TabPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey, parent.IsAnimationEnabled
- ? new Duration(TimeSpan.FromMilliseconds(200))
- : new Duration(TimeSpan.FromMilliseconds(1)));
-
- parent.IsInternalAction = true;
- RaiseEvent(new RoutedEventArgs(ClosedEvent, item));
-
- var list = parent.GetActualList();
- list?.Remove(item);
- }
-
- protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
- {
- base.OnMouseLeftButtonDown(e);
-
- if (VisualTreeHelper.HitTest(this, e.GetPosition(this)) == null) return;
- var parent = TabControlParent;
- if (parent == null) return;
-
- if (parent.IsDraggable && !ItemIsDragging && !_isDragging)
- {
- parent.UpdateScroll();
- TabPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey, new Duration(TimeSpan.FromSeconds(0)));
- _mouseDownOffsetX = RenderTransform.Value.OffsetX;
- _scrollHorizontalOffset = parent.GetHorizontalOffset();
- var mx = TranslatePoint(new Point(), parent).X + _scrollHorizontalOffset;
- _mouseDownIndex = CalLocationIndex(mx);
- var subIndex = _mouseDownIndex - CalLocationIndex(_scrollHorizontalOffset);
- _maxMoveLeft = -subIndex * ItemWidth;
- _maxMoveRight = parent.ActualWidth - ActualWidth + _maxMoveLeft;
-
- _isDragging = true;
- ItemIsDragging = true;
- _isWaiting = true;
- _dragPoint = e.GetPosition(parent);
- _dragPoint = new Point(_dragPoint.X + _scrollHorizontalOffset, _dragPoint.Y);
- _mouseDownPoint = _dragPoint;
- CaptureMouse();
- }
- }
-
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
-
- if (ItemIsDragging && _isDragging)
- {
- var parent = TabControlParent;
- if (parent == null) return;
-
- var subX = TranslatePoint(new Point(), parent).X + _scrollHorizontalOffset;
- CurrentIndex = CalLocationIndex(subX);
-
- var p = e.GetPosition(parent);
- p = new Point(p.X + _scrollHorizontalOffset, p.Y);
-
- var subLeft = p.X - _dragPoint.X;
- var totalLeft = p.X - _mouseDownPoint.X;
-
- if (Math.Abs(subLeft) <= WaitLength && _isWaiting) return;
-
- _isWaiting = false;
- _isDragged = true;
-
- var left = subLeft + RenderTransform.Value.OffsetX;
- if (totalLeft < _maxMoveLeft)
- {
- left = _maxMoveLeft + _mouseDownOffsetX;
- }
- else if (totalLeft > _maxMoveRight)
- {
- left = _maxMoveRight + _mouseDownOffsetX;
- }
-
- var t = new TranslateTransform(left, 0);
- RenderTransform = t;
- _dragPoint = p;
- }
- }
-
- protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
- {
- base.OnMouseLeftButtonUp(e);
-
- ReleaseMouseCapture();
-
- if (_isDragged)
- {
- var parent = TabControlParent;
- if (parent == null) return;
-
- var subX = TranslatePoint(new Point(), parent).X + _scrollHorizontalOffset;
- var index = CalLocationIndex(subX);
- var left = index * ItemWidth;
- var offsetX = RenderTransform.Value.OffsetX;
- CreateAnimation(offsetX, offsetX - subX + left, index);
- }
-
- _isDragging = false;
- ItemIsDragging = false;
- _isDragged = false;
- }
-
- protected override void OnMouseDown(MouseButtonEventArgs e)
- {
- if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed)
- {
- if (ShowCloseButton || ShowContextMenu)
- {
- Close();
- }
- }
- }
-
- /// <summary>
- /// 创建动画
- /// </summary>
- internal void CreateAnimation(double offsetX, double resultX, int index = -1)
- {
- var parent = TabControlParent;
-
- void AnimationCompleted()
- {
- RenderTransform = new TranslateTransform(resultX, 0);
- if (index == -1) return;
-
- var list = parent.GetActualList();
- if (list == null) return;
-
- var item = parent.ItemContainerGenerator.ItemFromContainer(this);
- if (item == null) return;
-
- TabPanel.CanUpdate = false;
- parent.IsInternalAction = true;
-
- list.Remove(item);
- parent.IsInternalAction = true;
- list.Insert(index, item);
- _tabPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey, new Duration(TimeSpan.FromMilliseconds(0)));
- TabPanel.CanUpdate = true;
- TabPanel.ForceUpdate = true;
- TabPanel.Measure(new Size(TabPanel.DesiredSize.Width, ActualHeight));
- TabPanel.ForceUpdate = false;
-
- Focus();
- IsSelected = true;
-
- if (!IsMouseCaptured)
- {
- parent.SetCurrentValue(Selector.SelectedIndexProperty, _currentIndex);
- }
- }
-
- TargetOffsetX = resultX;
- if (!parent.IsAnimationEnabled)
- {
- AnimationCompleted();
- return;
- }
-
- var animation = CreateAnimation(resultX, AnimationSpeed);
- animation.FillBehavior = FillBehavior.Stop;
- animation.Completed += (s1, e1) => AnimationCompleted();
- var f = new TranslateTransform(offsetX, 0);
- RenderTransform = f;
- f.BeginAnimation(TranslateTransform.XProperty, animation, HandoffBehavior.Compose);
- }
- /// <summary>
- /// 创建一个Double动画
- /// </summary>
- /// <param name="toValue"></param>
- /// <param name="milliseconds"></param>
- /// <returns></returns>
- public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
- {
- return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
- {
- EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
- };
- }
-
-
- /// <summary>
- /// 计算选项卡当前合适的位置编号
- /// </summary>
- /// <param name="left"></param>
- /// <returns></returns>
- private int CalLocationIndex(double left)
- {
- if (_isWaiting)
- {
- return CurrentIndex;
- }
-
- var maxIndex = TabControlParent.Items.Count - 1;
- var div = (int) (left / ItemWidth);
- var rest = left % ItemWidth;
- var result = rest / ItemWidth > .5 ? div + 1 : div;
-
- return result > maxIndex ? maxIndex : result;
- }
-
- public static readonly RoutedEvent ClosingEvent = EventManager.RegisterRoutedEvent("Closing", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TabItem));
-
- public event EventHandler Closing
- {
- add => AddHandler(ClosingEvent, value);
- remove => RemoveHandler(ClosingEvent, value);
- }
-
- public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TabItem));
-
- public event EventHandler Closed
- {
- add => AddHandler(ClosedEvent, value);
- remove => RemoveHandler(ClosedEvent, value);
- }
- }
|