support for .NET framework 3.5
Still have bugs
This commit is contained in:
1 parent
5878039e21
commit
7d6b491f25
704 files changed
+48273
-33
No files matched your search
@@ -0,0 +1,227 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools.Extension;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
[TemplatePart(Name = OverflowButtonKey, Type = typeof(ContextMenuToggleButton))]
|
||||
[TemplatePart(Name = HeaderPanelKey, Type = typeof(TabPanel))]
|
||||
public class TabControl : System.Windows.Controls.TabControl
|
||||
{
|
||||
private const string OverflowButtonKey = "PART_OverflowButton";
|
||||
|
||||
private const string HeaderPanelKey = "PART_HeaderPanel";
|
||||
|
||||
private ContextMenuToggleButton _buttonOverflow;
|
||||
|
||||
private TabPanel _headerPanel;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为内部操作
|
||||
/// </summary>
|
||||
internal bool IsInternalAction;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用动画
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsEnableAnimationProperty = DependencyProperty.Register(
|
||||
"IsEnableAnimation", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用动画
|
||||
/// </summary>
|
||||
public bool IsEnableAnimation
|
||||
{
|
||||
get => (bool)GetValue(IsEnableAnimationProperty);
|
||||
set => SetValue(IsEnableAnimationProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以拖动
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsDraggableProperty = DependencyProperty.Register(
|
||||
"IsDraggable", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以拖动
|
||||
/// </summary>
|
||||
public bool IsDraggable
|
||||
{
|
||||
get => (bool)GetValue(IsDraggableProperty);
|
||||
set => SetValue(IsDraggableProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示关闭按钮
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(
|
||||
"ShowCloseButton", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示关闭按钮
|
||||
/// </summary>
|
||||
public bool ShowCloseButton
|
||||
{
|
||||
get => (bool)GetValue(ShowCloseButtonProperty);
|
||||
set => SetValue(ShowCloseButtonProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否将标签填充
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsEnableTabFillProperty = DependencyProperty.Register(
|
||||
"IsEnableTabFill", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 是否将标签填充
|
||||
/// </summary>
|
||||
public bool IsEnableTabFill
|
||||
{
|
||||
get => (bool)GetValue(IsEnableTabFillProperty);
|
||||
set => SetValue(IsEnableTabFillProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签宽度
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TabItemWidthProperty = DependencyProperty.Register(
|
||||
"TabItemWidth", typeof(double), typeof(TabControl), new PropertyMetadata(200.0));
|
||||
|
||||
/// <summary>
|
||||
/// 标签宽度
|
||||
/// </summary>
|
||||
public double TabItemWidth
|
||||
{
|
||||
get => (double)GetValue(TabItemWidthProperty);
|
||||
set => SetValue(TabItemWidthProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签高度
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TabItemHeightProperty = DependencyProperty.Register(
|
||||
"TabItemHeight", typeof(double), typeof(TabControl), new PropertyMetadata(30.0));
|
||||
|
||||
/// <summary>
|
||||
/// 标签高度
|
||||
/// </summary>
|
||||
public double TabItemHeight
|
||||
{
|
||||
get => (double)GetValue(TabItemHeightProperty);
|
||||
set => SetValue(TabItemHeightProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可见的标签数量
|
||||
/// </summary>
|
||||
private int _itemShowCount;
|
||||
|
||||
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
base.OnItemsChanged(e);
|
||||
|
||||
if (_headerPanel == null)
|
||||
{
|
||||
IsInternalAction = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsEnableTabFill)
|
||||
{
|
||||
_itemShowCount = (int)(ActualWidth / TabItemWidth);
|
||||
_buttonOverflow.Show(Items.Count > 0 && Items.Count >= _itemShowCount);
|
||||
}
|
||||
|
||||
if (IsInternalAction)
|
||||
{
|
||||
IsInternalAction = false;
|
||||
return;
|
||||
}
|
||||
if (IsEnableAnimation)
|
||||
{
|
||||
_headerPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
|
||||
}
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
foreach (TabItem item in e.NewItems)
|
||||
{
|
||||
item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
item.IsSelected = true;
|
||||
item.TabPanel = _headerPanel;
|
||||
}
|
||||
}
|
||||
IsInternalAction = false;
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
_headerPanel = Template.FindName(HeaderPanelKey, this) as TabPanel;
|
||||
|
||||
if (IsEnableTabFill) return;
|
||||
|
||||
_buttonOverflow = Template.FindName(OverflowButtonKey, this) as ContextMenuToggleButton;
|
||||
if (_buttonOverflow != null)
|
||||
{
|
||||
_itemShowCount = (int)(ActualWidth / TabItemWidth);
|
||||
_buttonOverflow.Show(Items.Count > 0 && Items.Count >= _itemShowCount);
|
||||
|
||||
var menu = new ContextMenu
|
||||
{
|
||||
Placement = PlacementMode.Bottom,
|
||||
PlacementTarget = _buttonOverflow
|
||||
};
|
||||
menu.Closed += (s, e) => _buttonOverflow.IsChecked = false;
|
||||
_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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using HandyControl.Interactivity;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
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>
|
||||
internal TabPanel TabPanel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前编号
|
||||
/// </summary>
|
||||
internal int CurrentIndex
|
||||
{
|
||||
get => _currentIndex;
|
||||
set
|
||||
{
|
||||
if (_currentIndex == value || value < 0) return;
|
||||
var oldIndex = _currentIndex;
|
||||
_currentIndex = value;
|
||||
UpdateItemOffsetX(oldIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新选项卡横向偏移
|
||||
/// </summary>
|
||||
/// <param name="oldIndex"></param>
|
||||
private void UpdateItemOffsetX(int oldIndex)
|
||||
{
|
||||
if (!_isDragging) 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, Close));
|
||||
CommandBindings.Add(new CommandBinding(ControlCommands.CloseAll, (s, e) =>
|
||||
{
|
||||
TabControlParent.IsInternalAction = true;
|
||||
TabControlParent.Items.Clear();
|
||||
}));
|
||||
CommandBindings.Add(new CommandBinding(ControlCommands.CloseOther, (s, e) =>
|
||||
{
|
||||
TabControlParent.IsInternalAction = true;
|
||||
var enumerator = ((IEnumerable)TabControlParent.Items).GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var item = enumerator.Current;
|
||||
if (!Equals(item) && item != null)
|
||||
{
|
||||
TabControlParent.Items.Remove(item);
|
||||
enumerator = ((IEnumerable)TabControlParent.Items).GetEnumerator();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private TabControl _tabControlParent;
|
||||
|
||||
private TabControl TabControlParent => _tabControlParent ?? (_tabControlParent = ItemsControl.ItemsControlFromItemContainer(this) as TabControl);
|
||||
|
||||
protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseRightButtonDown(e);
|
||||
IsSelected = true;
|
||||
Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭
|
||||
/// </summary>
|
||||
private void Close(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (TabControlParent.IsEnableAnimation)
|
||||
{
|
||||
TabPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
|
||||
}
|
||||
TabControlParent.IsInternalAction = true;
|
||||
TabControlParent.Items.Remove(this);
|
||||
}
|
||||
|
||||
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
||||
{
|
||||
base.OnMouseLeftButtonDown(e);
|
||||
if (TabControlParent.IsDraggable && !ItemIsDragging && !_isDragging)
|
||||
{
|
||||
TabPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
|
||||
_mouseDownOffsetX = RenderTransform.Value.OffsetX;
|
||||
var mx = TranslatePoint(new Point(), TabControlParent).X;
|
||||
_mouseDownIndex = CalLocationIndex(mx);
|
||||
_maxMoveLeft = -_mouseDownIndex * ItemWidth;
|
||||
_maxMoveRight = TabControlParent.ActualWidth - ActualWidth + _maxMoveLeft;
|
||||
|
||||
_isDragging = true;
|
||||
ItemIsDragging = true;
|
||||
_isWaiting = true;
|
||||
_dragPoint = e.GetPosition(TabControlParent);
|
||||
_mouseDownPoint = _dragPoint;
|
||||
CaptureMouse();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
if (ItemIsDragging && _isDragging)
|
||||
{
|
||||
var subX = TranslatePoint(new Point(), TabControlParent).X;
|
||||
CurrentIndex = CalLocationIndex(subX);
|
||||
var p = e.GetPosition(TabControlParent);
|
||||
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 subX = TranslatePoint(new Point(), TabControlParent).X;
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建动画
|
||||
/// </summary>
|
||||
internal void CreateAnimation(double offsetX, double resultX, int index = -1)
|
||||
{
|
||||
void AnimationCompleted()
|
||||
{
|
||||
RenderTransform = new TranslateTransform(resultX, 0);
|
||||
if (index == -1) return;
|
||||
var parent = TabControlParent;
|
||||
TabPanel.CanUpdate = false;
|
||||
parent.IsInternalAction = true;
|
||||
parent.Items.Remove(this);
|
||||
parent.IsInternalAction = true;
|
||||
parent.Items.Insert(index, this);
|
||||
TabPanel.CanUpdate = true;
|
||||
TabPanel.ForceUpdate = true;
|
||||
TabPanel.Measure(new Size(TabPanel.DesiredSize.Width, ActualHeight));
|
||||
TabPanel.ForceUpdate = false;
|
||||
Focus();
|
||||
IsSelected = true;
|
||||
}
|
||||
TargetOffsetX = resultX;
|
||||
if (!TabControlParent.IsEnableAnimation)
|
||||
{
|
||||
AnimationCompleted();
|
||||
return;
|
||||
}
|
||||
var animation = AnimationHelper.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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算选项卡当前合适的位置编号
|
||||
/// </summary>
|
||||
/// <param name="left"></param>
|
||||
/// <returns></returns>
|
||||
private int CalLocationIndex(double left)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
public class TabPanel : Panel
|
||||
{
|
||||
private int _itemCount;
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以更新
|
||||
/// </summary>
|
||||
internal bool CanUpdate = true;
|
||||
|
||||
/// <summary>
|
||||
/// 选项卡字典
|
||||
/// </summary>
|
||||
internal Dictionary<int, TabItem> ItemDic = new Dictionary<int, TabItem>();
|
||||
|
||||
/// <summary>
|
||||
/// 流式行为持续时间
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FluidMoveDurationProperty = DependencyProperty.Register(
|
||||
"FluidMoveDuration", typeof(Duration), typeof(TabPanel), new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(200))));
|
||||
|
||||
/// <summary>
|
||||
/// 流式行为持续时间
|
||||
/// </summary>
|
||||
public Duration FluidMoveDuration
|
||||
{
|
||||
get => (Duration)GetValue(FluidMoveDurationProperty);
|
||||
set => SetValue(FluidMoveDurationProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否将标签填充
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsEnableTabFillProperty = DependencyProperty.Register(
|
||||
"IsEnableTabFill", typeof(bool), typeof(TabPanel), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 是否将标签填充
|
||||
/// </summary>
|
||||
public bool IsEnableTabFill
|
||||
{
|
||||
get => (bool)GetValue(IsEnableTabFillProperty);
|
||||
set => SetValue(IsEnableTabFillProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签宽度
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TabItemWidthProperty = DependencyProperty.Register(
|
||||
"TabItemWidth", typeof(double), typeof(TabPanel), new PropertyMetadata(200.0));
|
||||
|
||||
/// <summary>
|
||||
/// 标签宽度
|
||||
/// </summary>
|
||||
public double TabItemWidth
|
||||
{
|
||||
get => (double)GetValue(TabItemWidthProperty);
|
||||
set => SetValue(TabItemWidthProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签高度
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TabItemHeightProperty = DependencyProperty.Register(
|
||||
"TabItemHeight", typeof(double), typeof(TabPanel), new PropertyMetadata(30.0));
|
||||
|
||||
/// <summary>
|
||||
/// 标签高度
|
||||
/// </summary>
|
||||
public double TabItemHeight
|
||||
{
|
||||
get => (double)GetValue(TabItemHeightProperty);
|
||||
set => SetValue(TabItemHeightProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以强制更新
|
||||
/// </summary>
|
||||
internal bool ForceUpdate;
|
||||
|
||||
private Size _oldSize;
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经加载
|
||||
/// </summary>
|
||||
private bool _isLoaded;
|
||||
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
if ((_itemCount == InternalChildren.Count || !CanUpdate) && !ForceUpdate) return _oldSize;
|
||||
constraint.Height = TabItemHeight;
|
||||
_itemCount = InternalChildren.Count;
|
||||
|
||||
var size = new Size();
|
||||
|
||||
ItemDic.Clear();
|
||||
|
||||
var count = InternalChildren.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
_oldSize = new Size();
|
||||
return _oldSize;
|
||||
}
|
||||
constraint.Width += InternalChildren.Count;
|
||||
|
||||
var itemWidth = .0;
|
||||
var arr = new int[count];
|
||||
|
||||
if (!IsEnableTabFill)
|
||||
{
|
||||
itemWidth = TabItemWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TemplatedParent is TabControl tabControl)
|
||||
{
|
||||
arr = ArithmeticHelper.DivideInt2Arr((int)tabControl.ActualWidth + InternalChildren.Count, count);
|
||||
}
|
||||
}
|
||||
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
if (IsEnableTabFill)
|
||||
{
|
||||
itemWidth = arr[index];
|
||||
}
|
||||
if (InternalChildren[index] is TabItem tabItem)
|
||||
{
|
||||
tabItem.RenderTransform = new TranslateTransform();
|
||||
tabItem.MaxWidth = itemWidth;
|
||||
var rect = new Rect
|
||||
{
|
||||
X = size.Width - tabItem.BorderThickness.Left,
|
||||
Width = itemWidth,
|
||||
Height = TabItemHeight
|
||||
};
|
||||
tabItem.Arrange(rect);
|
||||
tabItem.ItemWidth = itemWidth - tabItem.BorderThickness.Left;
|
||||
tabItem.CurrentIndex = index;
|
||||
tabItem.TargetOffsetX = 0;
|
||||
ItemDic[index] = tabItem;
|
||||
size.Width += tabItem.ItemWidth;
|
||||
}
|
||||
}
|
||||
size.Height = constraint.Height;
|
||||
_oldSize = size;
|
||||
return _oldSize;
|
||||
}
|
||||
|
||||
public TabPanel()
|
||||
{
|
||||
Loaded += (s, e) =>
|
||||
{
|
||||
if (_isLoaded) return;
|
||||
ForceUpdate = true;
|
||||
Measure(new Size(DesiredSize.Width, ActualHeight));
|
||||
ForceUpdate = false;
|
||||
foreach (var item in ItemDic.Values)
|
||||
{
|
||||
item.TabPanel = this;
|
||||
}
|
||||
_isLoaded = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user