5 files changed
+225
-88
No files matched your search
@@ -114,7 +114,8 @@ namespace HandyControl.Controls
|
||||
_isRunning = false;
|
||||
};
|
||||
_isRunning = true;
|
||||
BeginAnimation(CurrentVerticalOffsetProperty, animation);
|
||||
|
||||
BeginAnimation(CurrentVerticalOffsetProperty, animation, HandoffBehavior.Compose);
|
||||
}
|
||||
|
||||
internal void ScrollToHorizontalOffsetInternal(double offset, double milliseconds = 500)
|
||||
@@ -131,7 +132,8 @@ namespace HandyControl.Controls
|
||||
_isRunning = false;
|
||||
};
|
||||
_isRunning = true;
|
||||
BeginAnimation(CurrentHorizontalOffsetProperty, animation);
|
||||
|
||||
BeginAnimation(CurrentHorizontalOffsetProperty, animation, HandoffBehavior.Compose);
|
||||
}
|
||||
|
||||
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) =>
|
||||
@@ -150,7 +152,7 @@ namespace HandyControl.Controls
|
||||
|
||||
public static bool GetIsEnableInertia(DependencyObject element)
|
||||
{
|
||||
return (bool) element.GetValue(IsEnableInertiaProperty);
|
||||
return (bool)element.GetValue(IsEnableInertiaProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -158,7 +160,7 @@ namespace HandyControl.Controls
|
||||
/// </summary>
|
||||
public bool IsEnableInertia
|
||||
{
|
||||
get => (bool)GetValue(IsEnableInertiaProperty);
|
||||
get => (bool) GetValue(IsEnableInertiaProperty);
|
||||
set => SetValue(IsEnableInertiaProperty, value);
|
||||
}
|
||||
|
||||
@@ -173,7 +175,7 @@ namespace HandyControl.Controls
|
||||
/// </summary>
|
||||
public bool IsPenetrating
|
||||
{
|
||||
get => (bool)GetValue(IsPenetratingProperty);
|
||||
get => (bool) GetValue(IsPenetratingProperty);
|
||||
set => SetValue(IsPenetratingProperty, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools.Extension;
|
||||
|
||||
@@ -10,16 +11,31 @@ namespace HandyControl.Controls
|
||||
{
|
||||
[TemplatePart(Name = OverflowButtonKey, Type = typeof(ContextMenuToggleButton))]
|
||||
[TemplatePart(Name = HeaderPanelKey, Type = typeof(TabPanel))]
|
||||
[TemplatePart(Name = OverflowScrollviewer, Type = typeof(ScrollViewer))]
|
||||
[TemplatePart(Name = ScrollButtonLeft, Type = typeof(Button))]
|
||||
[TemplatePart(Name = ScrollButtonRight, Type = typeof(Button))]
|
||||
public class TabControl : System.Windows.Controls.TabControl
|
||||
{
|
||||
private const string OverflowButtonKey = "PART_OverflowButton";
|
||||
|
||||
private const string HeaderPanelKey = "PART_HeaderPanel";
|
||||
|
||||
private const string OverflowScrollviewer = "PART_OverflowScrollviewer";
|
||||
|
||||
private const string ScrollButtonLeft = "PART_ScrollButtonLeft";
|
||||
|
||||
private const string ScrollButtonRight = "PART_ScrollButtonRight";
|
||||
|
||||
private ContextMenuToggleButton _buttonOverflow;
|
||||
|
||||
private TabPanel _headerPanel;
|
||||
|
||||
private ScrollViewer _scrollviewerOverflow;
|
||||
|
||||
private Button _buttonScrollLeft;
|
||||
|
||||
private Button _buttonScrollRight;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为内部操作
|
||||
/// </summary>
|
||||
@@ -115,6 +131,21 @@ namespace HandyControl.Controls
|
||||
set => SetValue(TabItemHeightProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以滚动
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsScrollableProperty = DependencyProperty.Register(
|
||||
"IsScrollable", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以滚动
|
||||
/// </summary>
|
||||
public bool IsScrollable
|
||||
{
|
||||
get => (bool) GetValue(IsScrollableProperty);
|
||||
set => SetValue(IsScrollableProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可见的标签数量
|
||||
/// </summary>
|
||||
@@ -133,7 +164,7 @@ namespace HandyControl.Controls
|
||||
if (!IsEnableTabFill)
|
||||
{
|
||||
_itemShowCount = (int)(ActualWidth / TabItemWidth);
|
||||
_buttonOverflow.Show(Items.Count > 0 && Items.Count >= _itemShowCount);
|
||||
_buttonOverflow.Show(Items.Count > 0 && Items.Count >= _itemShowCount);
|
||||
}
|
||||
|
||||
if (IsInternalAction)
|
||||
@@ -163,12 +194,33 @@ namespace HandyControl.Controls
|
||||
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
if (_buttonOverflow != null)
|
||||
{
|
||||
if (_buttonOverflow.Menu != null)
|
||||
{
|
||||
_buttonOverflow.Menu.Closed -= Menu_Closed;
|
||||
_buttonOverflow.Menu = null;
|
||||
}
|
||||
|
||||
_buttonOverflow.Click -= ButtonOverflow_Click;
|
||||
}
|
||||
|
||||
if (_buttonScrollLeft != null) _buttonScrollLeft.Click -= ButtonScrollLeft_Click;
|
||||
if (_buttonScrollRight != null) _buttonScrollRight.Click -= ButtonScrollRight_Click;
|
||||
|
||||
base.OnApplyTemplate();
|
||||
_headerPanel = Template.FindName(HeaderPanelKey, this) as TabPanel;
|
||||
|
||||
if (IsEnableTabFill) return;
|
||||
|
||||
_buttonOverflow = Template.FindName(OverflowButtonKey, this) as ContextMenuToggleButton;
|
||||
_scrollviewerOverflow = Template.FindName(OverflowScrollviewer, this) as ScrollViewer;
|
||||
_buttonScrollLeft = Template.FindName(ScrollButtonLeft, this) as Button;
|
||||
_buttonScrollRight = Template.FindName(ScrollButtonRight, this) as Button;
|
||||
|
||||
if (_buttonScrollLeft != null) _buttonScrollLeft.Click += ButtonScrollLeft_Click;
|
||||
if (_buttonScrollRight != null) _buttonScrollRight.Click += ButtonScrollRight_Click;
|
||||
|
||||
if (_buttonOverflow != null)
|
||||
{
|
||||
_itemShowCount = (int)(ActualWidth / TabItemWidth);
|
||||
@@ -179,49 +231,68 @@ namespace HandyControl.Controls
|
||||
Placement = PlacementMode.Bottom,
|
||||
PlacementTarget = _buttonOverflow
|
||||
};
|
||||
menu.Closed += (s, e) => _buttonOverflow.IsChecked = false;
|
||||
menu.Closed += Menu_Closed;
|
||||
_buttonOverflow.Menu = menu;
|
||||
_buttonOverflow.Click += (s, e) =>
|
||||
{
|
||||
if (_buttonOverflow.IsChecked == true)
|
||||
{
|
||||
_buttonOverflow.Menu.Items.Clear();
|
||||
foreach (TabItem item in Items)
|
||||
{
|
||||
var menuItem = new MenuItem
|
||||
{
|
||||
Header = item.Header,
|
||||
Width = TabItemWidth,
|
||||
IsChecked = item.IsSelected,
|
||||
IsCheckable = true
|
||||
};
|
||||
menuItem.Click += delegate
|
||||
{
|
||||
_buttonOverflow.IsChecked = false;
|
||||
var index = Items.IndexOf(item);
|
||||
if (index >= _itemShowCount)
|
||||
{
|
||||
Items.Remove(item);
|
||||
Items.Insert(0, item);
|
||||
if (IsEnableAnimation)
|
||||
{
|
||||
_headerPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
|
||||
}
|
||||
_headerPanel.ForceUpdate = true;
|
||||
_headerPanel.Measure(new Size(_headerPanel.DesiredSize.Width, ActualHeight));
|
||||
_headerPanel.ForceUpdate = false;
|
||||
}
|
||||
item.IsSelected = true;
|
||||
};
|
||||
_buttonOverflow.Menu.Items.Add(menuItem);
|
||||
}
|
||||
}
|
||||
};
|
||||
_buttonOverflow.Click += ButtonOverflow_Click;
|
||||
}
|
||||
}
|
||||
|
||||
private void Menu_Closed(object sender, RoutedEventArgs e) => _buttonOverflow.IsChecked = false;
|
||||
|
||||
private void ButtonScrollRight_Click(object sender, RoutedEventArgs e) =>
|
||||
_scrollviewerOverflow.ScrollToHorizontalOffsetInternal(Math.Min(
|
||||
_scrollviewerOverflow.CurrentHorizontalOffset + TabItemWidth, _scrollviewerOverflow.ScrollableWidth));
|
||||
|
||||
private void ButtonScrollLeft_Click(object sender, RoutedEventArgs e) =>
|
||||
_scrollviewerOverflow.ScrollToHorizontalOffsetInternal(Math.Max(
|
||||
_scrollviewerOverflow.CurrentHorizontalOffset - TabItemWidth, 0));
|
||||
|
||||
private void ButtonOverflow_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_buttonOverflow.IsChecked == true)
|
||||
{
|
||||
_buttonOverflow.Menu.Items.Clear();
|
||||
foreach (TabItem item in Items)
|
||||
{
|
||||
var menuItem = new MenuItem
|
||||
{
|
||||
Header = item.Header,
|
||||
Width = TabItemWidth,
|
||||
IsChecked = item.IsSelected,
|
||||
IsCheckable = true
|
||||
};
|
||||
menuItem.Click += delegate
|
||||
{
|
||||
_buttonOverflow.IsChecked = false;
|
||||
var index = Items.IndexOf(item);
|
||||
if (index >= _itemShowCount)
|
||||
{
|
||||
Items.Remove(item);
|
||||
Items.Insert(0, item);
|
||||
if (IsEnableAnimation)
|
||||
{
|
||||
_headerPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
|
||||
}
|
||||
_headerPanel.ForceUpdate = true;
|
||||
_headerPanel.Measure(new Size(_headerPanel.DesiredSize.Width, ActualHeight));
|
||||
_headerPanel.ForceUpdate = false;
|
||||
}
|
||||
item.IsSelected = true;
|
||||
};
|
||||
_buttonOverflow.Menu.Items.Add(menuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal double GetHorizontalOffset() => _scrollviewerOverflow?.CurrentHorizontalOffset ?? 0;
|
||||
|
||||
internal void UpdateScroll() => _scrollviewerOverflow?.RaiseEvent(new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, 0)
|
||||
{
|
||||
RoutedEvent = MouseWheelEvent
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,11 @@ namespace HandyControl.Controls
|
||||
/// </summary>
|
||||
private int _currentIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 标签容器横向滚动距离
|
||||
/// </summary>
|
||||
private double _scrollHorizontalOffset;
|
||||
|
||||
/// <summary>
|
||||
/// 标签容器
|
||||
/// </summary>
|
||||
@@ -178,17 +183,21 @@ namespace HandyControl.Controls
|
||||
base.OnMouseLeftButtonDown(e);
|
||||
if (TabControlParent.IsDraggable && !ItemIsDragging && !_isDragging)
|
||||
{
|
||||
TabControlParent.UpdateScroll();
|
||||
TabPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
|
||||
_mouseDownOffsetX = RenderTransform.Value.OffsetX;
|
||||
var mx = TranslatePoint(new Point(), TabControlParent).X;
|
||||
_scrollHorizontalOffset = TabControlParent.GetHorizontalOffset();
|
||||
var mx = TranslatePoint(new Point(), TabControlParent).X + _scrollHorizontalOffset;
|
||||
_mouseDownIndex = CalLocationIndex(mx);
|
||||
_maxMoveLeft = -_mouseDownIndex * ItemWidth;
|
||||
var subIndex = _mouseDownIndex - CalLocationIndex(_scrollHorizontalOffset);
|
||||
_maxMoveLeft = -subIndex * ItemWidth;
|
||||
_maxMoveRight = TabControlParent.ActualWidth - ActualWidth + _maxMoveLeft;
|
||||
|
||||
_isDragging = true;
|
||||
ItemIsDragging = true;
|
||||
_isWaiting = true;
|
||||
_dragPoint = e.GetPosition(TabControlParent);
|
||||
_dragPoint = new Point(_dragPoint.X + +_scrollHorizontalOffset, _dragPoint.Y);
|
||||
_mouseDownPoint = _dragPoint;
|
||||
CaptureMouse();
|
||||
}
|
||||
@@ -199,9 +208,10 @@ namespace HandyControl.Controls
|
||||
base.OnMouseMove(e);
|
||||
if (ItemIsDragging && _isDragging)
|
||||
{
|
||||
var subX = TranslatePoint(new Point(), TabControlParent).X;
|
||||
var subX = TranslatePoint(new Point(), TabControlParent).X + _scrollHorizontalOffset;
|
||||
CurrentIndex = CalLocationIndex(subX);
|
||||
var p = e.GetPosition(TabControlParent);
|
||||
p = new Point(p.X + _scrollHorizontalOffset, p.Y);
|
||||
var subLeft = p.X - _dragPoint.X;
|
||||
var totalLeft = p.X - _mouseDownPoint.X;
|
||||
|
||||
@@ -229,7 +239,7 @@ namespace HandyControl.Controls
|
||||
ReleaseMouseCapture();
|
||||
if (_isDragged)
|
||||
{
|
||||
var subX = TranslatePoint(new Point(), TabControlParent).X;
|
||||
var subX = TranslatePoint(new Point(), TabControlParent).X + _scrollHorizontalOffset;
|
||||
var index = CalLocationIndex(subX);
|
||||
var left = index * ItemWidth;
|
||||
var offsetX = RenderTransform.Value.OffsetX;
|
||||
@@ -273,7 +283,7 @@ namespace HandyControl.Controls
|
||||
animation.Completed += (s1, e1) => AnimationCompleted();
|
||||
var f = new TranslateTransform(offsetX, 0);
|
||||
RenderTransform = f;
|
||||
f.BeginAnimation(TranslateTransform.XProperty, animation);
|
||||
f.BeginAnimation(TranslateTransform.XProperty, animation, HandoffBehavior.Compose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in new issue
Block a user