Initial commit: FluidExplorer:从零实现的 WinUI 3 文件资源管理器替代品(Mica、命令栏、面包屑)
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using System.ComponentModel;
|
||||
using FluidExplorer.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
|
||||
namespace FluidExplorer.Views;
|
||||
|
||||
/// <summary>一个标签页的宿主:负责单/双窗格布局与中间分隔条。</summary>
|
||||
public sealed partial class ExplorerTabView : UserControl
|
||||
{
|
||||
private bool _dragging;
|
||||
private double _startX;
|
||||
private double _startFirstWidth;
|
||||
private double _startSecondWidth;
|
||||
|
||||
public ExplorerTabView()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += (_, _) => UpdatePaneLayout();
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TabViewModelProperty = DependencyProperty.Register(
|
||||
nameof(TabViewModel),
|
||||
typeof(ExplorerTabViewModel),
|
||||
typeof(ExplorerTabView),
|
||||
new PropertyMetadata(null, OnTabChanged));
|
||||
|
||||
public ExplorerTabViewModel? TabViewModel
|
||||
{
|
||||
get => (ExplorerTabViewModel?)GetValue(TabViewModelProperty);
|
||||
set => SetValue(TabViewModelProperty, value);
|
||||
}
|
||||
|
||||
private static void OnTabChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var view = (ExplorerTabView)d;
|
||||
if (e.OldValue is ExplorerTabViewModel old) old.PropertyChanged -= view.OnTabPropertyChanged;
|
||||
if (e.NewValue is ExplorerTabViewModel tab) tab.PropertyChanged += view.OnTabPropertyChanged;
|
||||
view.UpdatePaneLayout();
|
||||
}
|
||||
|
||||
private void OnTabPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(ExplorerTabViewModel.IsDualPane)) UpdatePaneLayout();
|
||||
}
|
||||
|
||||
private void UpdatePaneLayout()
|
||||
{
|
||||
var dual = TabViewModel?.IsDualPane == true;
|
||||
|
||||
if (dual)
|
||||
{
|
||||
Splitter.Visibility = Visibility.Visible;
|
||||
SecondaryPaneView.Visibility = Visibility.Visible;
|
||||
SplitterColumn.Width = new GridLength(4);
|
||||
if (FirstColumn.Width.IsAbsolute && SecondColumn.Width.IsAbsolute) return;
|
||||
FirstColumn.Width = new GridLength(1, GridUnitType.Star);
|
||||
SecondColumn.Width = new GridLength(1, GridUnitType.Star);
|
||||
}
|
||||
else
|
||||
{
|
||||
Splitter.Visibility = Visibility.Collapsed;
|
||||
SecondaryPaneView.Visibility = Visibility.Collapsed;
|
||||
SplitterColumn.Width = new GridLength(0);
|
||||
FirstColumn.Width = new GridLength(1, GridUnitType.Star);
|
||||
SecondColumn.Width = new GridLength(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSplitterPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
_dragging = true;
|
||||
_startX = e.GetCurrentPoint(LayoutRoot).Position.X;
|
||||
_startFirstWidth = FirstColumn.ActualWidth;
|
||||
_startSecondWidth = SecondColumn.ActualWidth;
|
||||
Splitter.CapturePointer(e.Pointer);
|
||||
}
|
||||
|
||||
private void OnSplitterPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (!_dragging) return;
|
||||
var delta = e.GetCurrentPoint(LayoutRoot).Position.X - _startX;
|
||||
var total = _startFirstWidth + _startSecondWidth;
|
||||
const double min = 240;
|
||||
|
||||
var first = Math.Clamp(_startFirstWidth + delta, min, total - min);
|
||||
FirstColumn.Width = new GridLength(first, GridUnitType.Pixel);
|
||||
SecondColumn.Width = new GridLength(total - first, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
private void OnSplitterPointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (!_dragging) return;
|
||||
_dragging = false;
|
||||
Splitter.ReleasePointerCapture(e.Pointer);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user