You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
3.2 KiB
C#
93 lines
3.2 KiB
C#
|
1 month ago
|
using Avalonia.Controls;
|
||
|
|
using Avalonia.Interactivity;
|
||
|
|
using Avalonia.Platform.Storage;
|
||
|
|
using Avalonia.Threading;
|
||
|
|
using AI.ViewModels;
|
||
|
|
|
||
|
|
namespace AI.Views
|
||
|
|
{
|
||
|
|
public partial class MainWindow : Window
|
||
|
|
{
|
||
|
|
private bool _isUserAtBottom = true; // 跟踪用户是否在底部
|
||
|
|
private const double ScrollThreshold = 50.0; // 距离底部的阈值(像素)
|
||
|
|
|
||
|
|
public MainWindow()
|
||
|
|
{
|
||
|
|
InitializeComponent();
|
||
|
|
DataContextChanged += MainWindow_DataContextChanged;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void MainWindow_DataContextChanged(object? sender, EventArgs e)
|
||
|
|
{
|
||
|
|
if (DataContext is MainWindowViewModel vm)
|
||
|
|
{
|
||
|
|
vm.RequestScrollToBottom += ViewModel_RequestScrollToBottom;
|
||
|
|
|
||
|
|
// 订阅文件选择请求事件
|
||
|
|
vm.RequestFileSelection += async () =>
|
||
|
|
{
|
||
|
|
var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||
|
|
{
|
||
|
|
Title = "选择文件",
|
||
|
|
AllowMultiple = false,
|
||
|
|
FileTypeFilter = new[] { FilePickerFileTypes.All }
|
||
|
|
});
|
||
|
|
return files;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 初始化滚动视图的滚动事件监听
|
||
|
|
if (ChatMessagesScrollViewer != null)
|
||
|
|
{
|
||
|
|
ChatMessagesScrollViewer.ScrollChanged += ChatMessagesScrollViewer_ScrollChanged;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ChatMessagesScrollViewer_ScrollChanged(object? sender, ScrollChangedEventArgs e)
|
||
|
|
{
|
||
|
|
if (sender is ScrollViewer scrollViewer)
|
||
|
|
{
|
||
|
|
// 检查用户是否在底部(考虑一个小的阈值,避免浮点数精度问题)
|
||
|
|
var offset = scrollViewer.Offset.Y;
|
||
|
|
var extent = scrollViewer.Extent.Height;
|
||
|
|
var viewport = scrollViewer.Viewport.Height;
|
||
|
|
|
||
|
|
// 处理边界情况:如果内容高度小于视口高度,认为在底部
|
||
|
|
if (extent <= viewport)
|
||
|
|
{
|
||
|
|
_isUserAtBottom = true;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var distanceFromBottom = extent - (offset + viewport);
|
||
|
|
|
||
|
|
// 如果距离底部小于阈值,认为用户在底部
|
||
|
|
_isUserAtBottom = distanceFromBottom <= ScrollThreshold;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ViewModel_RequestScrollToBottom()
|
||
|
|
{
|
||
|
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||
|
|
{
|
||
|
|
// 只在用户已经在底部时才自动滚动
|
||
|
|
// 这样如果用户向上滚动查看历史消息,就不会被强制拉回底部
|
||
|
|
if (_isUserAtBottom)
|
||
|
|
{
|
||
|
|
ChatMessagesScrollViewer.ScrollToEnd();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnDeleteSessionClick(object? sender, RoutedEventArgs e)
|
||
|
|
{
|
||
|
|
if (sender is Button button && button.Tag is string sessionId)
|
||
|
|
{
|
||
|
|
if (DataContext is MainWindowViewModel vm)
|
||
|
|
{
|
||
|
|
vm.DeleteSessionCommand.Execute(sessionId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|