FieldWorkClient/Provider/ExplorerMinIOBehavior.cs

1051 lines
38 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using HeBianGu.Base.WpfBase;
using HeBianGu.Control.Explorer;
using Hopetry.Services;
using Minio.DataModel.Args;
using Minio;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Extensions.Configuration;
using Minio.ApiEndpoints;
using System.Windows.Threading;
using System.Drawing;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using HeBianGu.App.Disk;
using System.Windows.Controls.Primitives;
using System.Diagnostics;
using System.Windows.Input;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.Reflection;
using Minio.DataModel.Select;
namespace Hopetry.Provider.Behaviors
{
public class ExplorerMinIOBehavior : Behavior<Explorer>
{
#region 依赖属性
public bool UseMinIO
{
get { return (bool)GetValue(UseMinIOProperty); }
set { SetValue(UseMinIOProperty, value); }
}
public static readonly DependencyProperty UseMinIOProperty =
DependencyProperty.Register("UseMinIO", typeof(bool), typeof(ExplorerMinIOBehavior),
new PropertyMetadata(false, OnUseMinIOChanged));
public static readonly DependencyProperty StatusMessageProperty =
DependencyProperty.Register(
"StatusMessage",
typeof(string),
typeof(ExplorerMinIOBehavior),
new PropertyMetadata(null));
public string StatusMessage
{
get => (string)GetValue(StatusMessageProperty);
set => SetValue(StatusMessageProperty, value);
}
//获取当前选中的文件
public IEnumerable<SystemInfoModel> GetSelectedItems()
{
var dataGrid = FindVisualChild<DataGrid>(AssociatedObject);
if (dataGrid != null)
{
return dataGrid.SelectedItems.Cast<SystemInfoModel>().ToList();
}
return Enumerable.Empty<SystemInfoModel>();
}
#endregion
#region 字段和初始化
private IMinioClient _minioClient;
private bool _isLocalHistoryRefresh;
private bool _isNavigationCommand = false;
protected override void OnAttached()
{
base.OnAttached();
InitializeMinIOClient();
// 监听路径变化
var pathDescriptor = DependencyPropertyDescriptor.FromProperty(
Explorer.CurrentPathProperty, typeof(Explorer));
pathDescriptor.AddValueChanged(AssociatedObject, OnCurrentPathChanged);
// 监听历史记录变化
var historyDescriptor = DependencyPropertyDescriptor.FromProperty(
Explorer.HistoryProperty, typeof(Explorer));
historyDescriptor.AddValueChanged(AssociatedObject, OnHistoryChanged);
// 初始加载MinIO内容
if (UseMinIO)
{
Dispatcher.BeginInvoke((Action)(() =>
{
RefreshMinIOPath(AssociatedObject.CurrentPath);
}), DispatcherPriority.Loaded);
}
// 延迟执行以确保UI元素已加载
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 更安全的方式查找DataGrid
var dataGrid = FindVisualChild<DataGrid>(AssociatedObject);
if (dataGrid != null)
{
dataGrid.CellEditEnding += DataGrid_CellEditEnding;
}
else
{
// 如果首次查找失败,可以尝试延迟再次查找
Dispatcher.BeginInvoke(new Action(() =>
{
dataGrid = FindVisualChild<DataGrid>(AssociatedObject);
if (dataGrid != null)
{
dataGrid.CellEditEnding += DataGrid_CellEditEnding;
}
}), DispatcherPriority.Loaded);
}
}
catch (Exception ex)
{
// 记录错误但不中断程序
System.Diagnostics.Debug.WriteLine($"附加CellEditEnding事件失败: {ex.Message}");
}
}), DispatcherPriority.Loaded);
var dataGrid = FindVisualChild<DataGrid>(AssociatedObject);
if (dataGrid != null)
{
dataGrid.BeginningEdit += DataGrid_BeginningEdit;
}
AssociatedObject.Loaded += OnExplorerLoaded;
// 监听导航命令
CommandManager.AddPreviewExecutedHandler(AssociatedObject, OnPreviewCommandExecuted);
}
protected override void OnDetaching()
{
// Clean up event handlers
var pathDescriptor = DependencyPropertyDescriptor.FromProperty(
Explorer.CurrentPathProperty, typeof(Explorer));
pathDescriptor.RemoveValueChanged(AssociatedObject, OnCurrentPathChanged);
var historyDescriptor = DependencyPropertyDescriptor.FromProperty(
Explorer.HistoryProperty, typeof(Explorer));
historyDescriptor.RemoveValueChanged(AssociatedObject, OnHistoryChanged);
var dataGrid = FindVisualChild<DataGrid>(AssociatedObject);
if (dataGrid != null)
{
dataGrid.CellEditEnding -= DataGrid_CellEditEnding;
}
CommandManager.RemovePreviewExecutedHandler(AssociatedObject, OnPreviewCommandExecuted);
base.OnDetaching();
}
#endregion
#region 事件处理
private static void OnUseMinIOChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (ExplorerMinIOBehavior)d;
if ((bool)e.NewValue && behavior.AssociatedObject != null)
{
behavior.Dispatcher.BeginInvoke((Action)(() =>
{
behavior.RefreshMinIOPath(behavior.AssociatedObject.CurrentPath);
}), DispatcherPriority.ContextIdle);
}
}
private void OnPreviewCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command == Explorer.Previous || e.Command == Explorer.Next)
{
_isNavigationCommand = true;
Dispatcher.BeginInvoke(new Action(() => _isNavigationCommand = false),
DispatcherPriority.ApplicationIdle);
}
}
private void OnCurrentPathChanged(object sender, EventArgs e)
{
if (UseMinIO)
{
RefreshMinIOPath(AssociatedObject.CurrentPath);
}
}
private void OnHistoryChanged(object sender, EventArgs e)
{
// 只有当历史记录变更不是由导航触发时才标记为本地刷新
_isLocalHistoryRefresh = true;
}
//定义状态更新事件
public event EventHandler<string> StatusMessageChanged;
#endregion
#region MinIO 操作
private void InitializeMinIOClient()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
// 构建配置
var config = builder.Build();
_minioClient = new MinioClient()
.WithEndpoint(config["Minio:Endpoint"])
.WithCredentials(config["Minio:AccessKey"], config["Minio:SecretKey"])
.Build();
}
public async void RefreshMinIOPath(string path, string searchText = "")
{
if (AssociatedObject == null || _minioClient == null) return;
try
{
// 获取绑定的 ViewModel
if (AssociatedObject.DataContext is LoyoutViewModel vm)
{
// 更新 ViewModel 状态自动通过属性变更通知更新UI
vm.SelectedItems.Clear();
}
AssociatedObject.IsEnabled = false;
AssociatedObject.Cursor = System.Windows.Input.Cursors.Wait;
var items = await GetMinIOItemsAsync(path, searchText);
var filecount=items.Count();
StatusMessage = $"已全部加载,共 {filecount} 个";
AssociatedObject.ItemsSource = items.ToObservable();
//var items = await GetMinIOItemsAsync(path);
//AssociatedObject.ItemsSource = items.ToObservable();
// 2. 刷新导航栏
var navigationBar = FindVisualChild<NavigationBar>(AssociatedObject);
if (navigationBar != null)
{
RefreshNavigationBar(navigationBar, path);
}
// Update history if this wasn't triggered by a history navigation
if (!_isNavigationCommand && !string.IsNullOrEmpty(path))
{
UpdateHistory(path);
}
_isLocalHistoryRefresh = false;
}
catch (Exception ex)
{
MessageBox.Show($"Error accessing MinIO: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
AssociatedObject.IsEnabled = true;
AssociatedObject.Cursor = null;
}
}
private async Task<IEnumerable<SystemInfoModel>> GetMinIOItemsAsync(string path, string searchText)
{
var items = new List<SystemInfoModel>();
if (string.IsNullOrEmpty(path) || path == "全部文件")
{
// List all buckets
var buckets = await _minioClient.ListBucketsAsync();
items.AddRange(buckets.Buckets.Select(b =>
new MinIODirectoryModel(new MinIODirectoryInfo(b.Name, null, true, b.CreationDateDateTime))));
}
else
{
// List objects in a bucket/path
var parts = path.Split(new[] { '/' }, 2);
var bucketName = parts[0];
var prefix = parts.Length > 1 ? parts[1] : null;
var args = new ListObjectsArgs()
.WithBucket(bucketName)
.WithPrefix(prefix ?? "")
.WithRecursive(false);
var observable = _minioClient.ListObjectsEnumAsync(args);
await foreach (var item in observable)
{
// 解码为正确的字符串
var decodedKey = Uri.UnescapeDataString(item.Key);
if (item.IsDir)
{
items.Add(new MinIODirectoryModel(
new MinIODirectoryInfo(bucketName, decodedKey, false, DateTime.Now)));
}
else
{
if (!string.IsNullOrEmpty(Path.GetFileName(decodedKey)))
{
string size = item.Size < 1024 * 1024 ?
$"{Math.Ceiling((decimal)item.Size / 1024)}KB" :
$"{Math.Ceiling((decimal)item.Size / (1024 * 1024))}MB";
items.Add(new MinIOFileModel(new MinIOFileInfo(bucketName, decodedKey, size, item.LastModifiedDateTime)));
}
}
}
}
//// 按修改时间降序排序(最新修改的排前面)
//return items.Where(x => x.DisplayName.Contains(searchText)).OrderByDescending(x =>
//{
// if (x is MinIOFileModel fileModel)
// {
// return ((MinIOFileInfo)fileModel.Model).LastModified;
// }
// return DateTime.Now; // 文件夹使用当前时间(或根据需求调整)
//}).ToList();
//按照名称排序
return items.Where(x => x.DisplayName.Contains(searchText)).OrderBy(x =>x.DisplayName)
.OrderBy(x =>
{
int lastDashIndex = x.DisplayName.LastIndexOf('-');
int index = x.DisplayName.LastIndexOf('.');
if (lastDashIndex == -1 || lastDashIndex == x.DisplayName.Length - 1||index==-1)
return int.MaxValue; // 无效格式排到最后
if(index - lastDashIndex - 1 < 0)
{
return int.MaxValue;
}
string numPart = x.DisplayName.Substring(lastDashIndex + 1,index- lastDashIndex-1);
return int.TryParse(numPart, out int num) ? num : int.MaxValue;
})
.ToList();
//{
// if (x is MinIOFileModel fileModel)
// {
// return ((MinIOFileInfo)fileModel.Model).LastModified;
// }
// return DateTime.Now; // 文件夹使用当前时间(或根据需求调整)
//}).ToList();
}
//private void UpdateHistory(string path)
//{
// if (string.IsNullOrEmpty(path))
// {
// // 根路径时创建一个特殊的目录模型
// var rootItem = new DirectoryModel(new MinIODirectoryInfo("", "", true, DateTime.Now))
// {
// DisplayName = RootDisplayName
// };
// AssociatedObject.History.Insert(0, rootItem);
// }
// else
// {
// var parts = path.Split(new[] { '/' }, 2);
// var bucketName = parts[0];
// var prefix = parts.Length > 1 ? parts[1] : null;
// var dirInfo = new MinIODirectoryInfo(bucketName, prefix, false, DateTime.Now);
// var historyItem = new DirectoryModel(dirInfo);
// if (AssociatedObject.History.FirstOrDefault()?.Model?.FullName != path)
// {
// AssociatedObject.History.Insert(0, historyItem);
// AssociatedObject.History = AssociatedObject.History
// .Take(ExplorerSetting.Instance.HistCapacity)
// .ToObservable();
// }
// }
// AssociatedObject.HistorySelectedItem = AssociatedObject.History.FirstOrDefault();
//}
private void UpdateHistory(string path)
{
if (AssociatedObject == null || _isNavigationCommand)
return;
try
{
// 检查是否已经是历史记录中的第一个项目
var firstHistoryItem = AssociatedObject.History.FirstOrDefault();
if (firstHistoryItem?.Model?.FullName == path)
return;
// 创建适当的目录模型
DirectoryModel historyItem;
if (string.IsNullOrEmpty(path) || path == RootDisplayName)
{
// 根路径时创建一个特殊的目录模型
historyItem = new DirectoryModel(new MinIODirectoryInfo("", "", true, DateTime.Now))
{
DisplayName = RootDisplayName
};
}
else
{
var parts = path.Split(new[] { '/' }, 2);
var bucketName = parts[0];
var prefix = parts.Length > 1 ? parts[1] : null;
var dirInfo = new MinIODirectoryInfo(bucketName, prefix, false, DateTime.Now);
historyItem = new DirectoryModel(dirInfo);
}
// 更新历史记录
_isLocalHistoryRefresh = true; // 防止递归触发
var newHistory = AssociatedObject.History.ToList();
newHistory.Insert(0, historyItem);
// 限制历史记录数量
AssociatedObject.History = newHistory
.Take(ExplorerSetting.Instance.HistCapacity)
.ToObservable();
AssociatedObject.HistorySelectedItem = AssociatedObject.History.FirstOrDefault();
}
catch (Exception ex)
{
Debug.WriteLine($"UpdateHistory error: {ex.Message}");
}
finally
{
_isLocalHistoryRefresh = false;
}
}
//// 更新文件数量 $"已加载 {fileCount} 个文件"
//private void UpdateFileCount(string message)
//{
// _viewModel.UpdateStatus(message);
//}
#endregion
#region 图标处理
private static readonly Dictionary<string, System.Drawing.Icon> _iconCache = new Dictionary<string, System.Drawing.Icon>();
private const string DefaultFontFamily = "Segoe MDL2 Assets";
private const double DefaultIconSize = 16;
public static System.Drawing.Icon GetIconForMinIOItem(string name, bool isDirectory, bool isBucket = false)
{
var cacheKey = $"{(isBucket ? "bucket" : (isDirectory ? "folder" : Path.GetExtension(name).ToLower()))}";
if (!_iconCache.TryGetValue(cacheKey, out var icon))
{
icon = LoadMinIOIcon(name, isDirectory, isBucket);
_iconCache[cacheKey] = icon;
}
return icon;
}
private static System.Drawing.Icon LoadMinIOIcon(string name, bool isDirectory, bool isBucket)
{
// 获取字体图标字符
string iconChar = GetFontIconChar(name, isDirectory, isBucket);
// 创建字体图标ImageSource
var imageSource = CreateFontIconImageSource(iconChar);
// 转换为Icon
return ConvertRenderTargetBitmapToIcon(imageSource as RenderTargetBitmap);
}
private static System.Drawing.Icon ConvertRenderTargetBitmapToIcon(RenderTargetBitmap renderTargetBitmap)
{
// 将 RenderTargetBitmap 转为 Bitmap
var bitmap = new System.Drawing.Bitmap(
renderTargetBitmap.PixelWidth,
renderTargetBitmap.PixelHeight,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
var data = bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
renderTargetBitmap.CopyPixels(
System.Windows.Int32Rect.Empty,
data.Scan0,
data.Height * data.Stride,
data.Stride);
bitmap.UnlockBits(data);
// 从 Bitmap 创建 Icon
var iconHandle = bitmap.GetHicon();
return System.Drawing.Icon.FromHandle(iconHandle);
}
private static string GetFontIconChar(string name, bool isDirectory, bool isBucket)
{
// 使用Segoe MDL2 Assets字体中的字符代码
if (isBucket)
{
return "\ue7c3"; // Cloud icon
}
if (isDirectory)
{
return "\ue8b7"; // Folder icon
}
// 按文件类型返回不同图标
string extension = Path.GetExtension(name)?.ToLower();
switch (extension)
{
case ".pdf": return "\uea90";
case ".jpg":
case ".png":
case ".jpeg":
case ".gif":
case ".bmp":
case ".svg":
return "\ueb9f"; // Picture icon
case ".doc":
case ".docx":
return "\ue8a5"; // Word document icon
case ".xls":
case ".xlsx":
return "\ue8a6"; // Excel document icon
case ".ppt":
case ".pptx":
return "\ue8a7"; // PowerPoint icon
case ".zip":
case ".rar":
case ".7z":
return "\ue7b8"; // Zip folder icon
case ".txt":
case ".log":
return "\ue8ab"; // Text document icon
case ".mp3":
case ".wav":
case ".flac":
return "\ue8d6"; // Audio icon
case ".mp4":
case ".avi":
case ".mov":
case ".wmv":
return "\ue8b2"; // Video icon
case ".exe":
case ".msi":
return "\ue756"; // Application icon
case ".html":
case ".htm":
return "\ue842"; // HTML icon
case ".cs":
case ".cpp":
case ".js":
case ".java":
return "\ue9a2"; // Code icon
default:
return "\ue7c4"; // Default document icon
}
}
private static ImageSource CreateFontIconImageSource(
string iconChar,
string fontFamily = DefaultFontFamily,
double fontSize = DefaultIconSize,
System.Windows.Media.Brush foreground = null)
{
try
{
var textBlock = new System.Windows.Controls.TextBlock
{
Text = iconChar,
FontFamily = new System.Windows.Media.FontFamily(fontFamily),
FontSize = fontSize,
Foreground = foreground ?? System.Windows.Media.Brushes.Black,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = System.Windows.VerticalAlignment.Center
};
// 计算实际需要的尺寸
textBlock.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
textBlock.Arrange(new System.Windows.Rect(textBlock.DesiredSize));
var renderTargetBitmap = new RenderTargetBitmap(
(int)Math.Ceiling(textBlock.ActualWidth),
(int)Math.Ceiling(textBlock.ActualHeight),
96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(textBlock);
renderTargetBitmap.Freeze();
return renderTargetBitmap;
}
catch
{
// 回退到默认图标
return CreateFontIconImageSource("\ue7c4", fontFamily, fontSize, foreground);
}
}
#endregion
#region 新建文件夹
private SystemInfoModel _newFolderItem;
public void BeginEditNewFolder(SystemInfoModel newFolder)
{
if (AssociatedObject == null)
{
// 记录错误或尝试重新获取AssociatedObject
Debug.WriteLine("Error: AssociatedObject is null in BeginEditNewFolder");
return;
}
// 获取当前绑定的集合
var itemsSource = AssociatedObject.ItemsSource as ObservableCollection<SystemInfoModel>;
if (itemsSource == null) return;
// 添加到集合最前面
itemsSource.Insert(0, newFolder);
_newFolderItem = newFolder;
// 使用Dispatcher确保在UI线程执行
AssociatedObject.Dispatcher.InvokeAsync(() =>
{
// 查找DataGrid
var dataGrid = FindVisualChild<DataGrid>(AssociatedObject);
if (dataGrid == null)
{
Debug.WriteLine("Error: Could not find DataGrid in Explorer");
return;
}
// 选中新创建的文件夹
dataGrid.SelectedItem = newFolder;
// 开始编辑
dataGrid.Dispatcher.InvokeAsync(() =>
{
// 强制更新布局
dataGrid.UpdateLayout();
// 滚动到视图并获取行
dataGrid.ScrollIntoView(newFolder);
dataGrid.UpdateLayout(); // 再次更新布局确保行生成
var row = dataGrid.ItemContainerGenerator.ContainerFromItem(newFolder) as DataGridRow;
if (row == null)
{
// 如果仍然为空,尝试遍历所有行
foreach (var item in dataGrid.Items)
{
dataGrid.ScrollIntoView(item);
var tempRow = dataGrid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
if (tempRow != null && tempRow.DataContext == newFolder)
{
row = tempRow;
break;
}
}
}
if (row != null)
{
var cell = GetCell(dataGrid, row, 1);
if (cell != null)
{
cell.Focus();
dataGrid.BeginEdit();
// 获取文本框并全选文本
var textBox = FindVisualChild<TextBox>(cell);
if (textBox != null)
{
textBox.SelectAll();
}
}
}
}, DispatcherPriority.Input);
}, DispatcherPriority.Background);
}
private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (_newFolderItem == null || e.Row.DataContext != _newFolderItem)
return;
if (e.EditAction == DataGridEditAction.Commit)
{
//var textBox = e.EditingElement as TextBox;
// 使用递归查找TextBox
var textBox = FindVisualChild<TextBox>(e.EditingElement);
if (textBox != null)
{
var newName = textBox.Text.Trim();
if (!string.IsNullOrEmpty(newName))
{
// 更新模型名称
_newFolderItem.DisplayName = newName;
// 通知ViewModel在MinIO中创建文件夹
if (AssociatedObject.DataContext is LoyoutViewModel vm)
{
vm.CreateMinIOFolder(newName).ConfigureAwait(false);
}
}
else
{
// 如果名称为空,移除该项
if (AssociatedObject.ItemsSource is ObservableCollection<SystemInfoModel> items)
{
items.Remove(_newFolderItem);
}
}
}
}
else if (e.EditAction == DataGridEditAction.Cancel)
{
// 如果取消编辑,移除该项
if (AssociatedObject.ItemsSource is ObservableCollection<SystemInfoModel> items)
{
items.Remove(_newFolderItem);
}
}
if (_newFolderItem is MinIODirectoryModel directoryModel)
{
directoryModel.IsRenaming = true; // 编辑完成后设为不可编辑
}
_newFolderItem = null;
}
private DataGridCell GetCell(DataGrid dataGrid, DataGridRow row, int column)
{
if (row == null) return null;
// 获取行的视觉子元素 - DataGridCellsPresenter
var presenter = FindVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
// 如果行是虚拟化的,可能需要先滚动到视图
dataGrid.ScrollIntoView(row, dataGrid.Columns[column]);
presenter = FindVisualChild<DataGridCellsPresenter>(row);
if (presenter == null) return null;
}
// 获取指定列的单元格
var cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
if (cell == null)
{
// 如果单元格尚未生成,再次尝试滚动到视图
dataGrid.ScrollIntoView(row, dataGrid.Columns[column]);
cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
}
return cell;
}
private static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
if (parent == null) return null;
// 先检查parent本身是否是目标类型
if (parent is T result)
{
return result;
}
try
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
result = FindVisualChild<T>(child);
if (result != null)
{
return result;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"FindVisualChild error: {ex.Message}");
}
return null;
}
private void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
if (e.Row.DataContext is MinIODirectoryModel directoryModel)
{
// 只有 IsRenaming 为 true 的文件夹可以编辑
e.Cancel = directoryModel.IsRenaming;
}
else
{
// 其他类型项目不可编辑
e.Cancel = true;
}
}
#endregion
#region 导航栏属性
// 添加导航栏控制属性
public string RootDisplayName
{
get { return (string)GetValue(RootDisplayNameProperty); }
set { SetValue(RootDisplayNameProperty, value); }
}
public static readonly DependencyProperty RootDisplayNameProperty =
DependencyProperty.Register("RootDisplayName", typeof(string), typeof(ExplorerMinIOBehavior),
new PropertyMetadata("全部文件"));
private void OnExplorerLoaded(object sender, RoutedEventArgs e)
{
var navigationBar = FindVisualChild<NavigationBar>(AssociatedObject);
if (navigationBar != null)
{
// 替换导航栏的RefreshData方法
ReplaceNavigationBarRefreshMethod(navigationBar);
// 强制刷新导航栏
RefreshNavigationBar(navigationBar, AssociatedObject.CurrentPath);
}
}
private void ReplaceNavigationBarRefreshMethod(NavigationBar navigationBar)
{
// 获取原始RefreshData方法
var originalMethod = typeof(NavigationBar).GetMethod("RefreshData",
BindingFlags.NonPublic | BindingFlags.Instance);
if (originalMethod == null) return;
// 动态创建委托替换原始方法
var methodDelegate = (Action<string>)delegate (string path)
{
RefreshNavigationBar(navigationBar, path);
};
// 使用反射替换方法(简化版,实际生产环境可能需要更安全的方式)
var field = typeof(NavigationBar).GetField("_refreshData",
BindingFlags.NonPublic | BindingFlags.Instance);
field?.SetValue(navigationBar, methodDelegate);
}
private void RefreshNavigationBar(NavigationBar navigationBar, string path)
{
if (!UseMinIO)
{
// 本地文件系统模式,使用原始逻辑
var originalMethod = typeof(NavigationBar).GetMethod("OriginalRefreshData",
BindingFlags.NonPublic | BindingFlags.Instance);
originalMethod?.Invoke(navigationBar, new object[] { path });
return;
}
// MinIO模式下的自定义路径处理
List<IDirectoryInfo> dirs = new List<IDirectoryInfo>();
if (string.IsNullOrEmpty(path) || path == "全部文件")
{
// 根节点显示自定义名称
var root = ExplorerProxy.Instance.CreateDirectoryInfo(RootDisplayName);
dirs.Add(root);
}
else
{
// 处理MinIO路径格式 bucketname/path/to/item
var parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
string currentPath = "";
// 添加根节点
dirs.Add(ExplorerProxy.Instance.CreateDirectoryInfo(RootDisplayName));
// 添加各级路径
for (int i = 0; i < parts.Length; i++)
{
currentPath += (i > 0 ? "/" : "") + parts[i];
var dir = ExplorerProxy.Instance.CreateDirectoryInfo(currentPath);
dirs.Add(dir);
}
}
// 更新导航栏ItemsSource
navigationBar.ItemsSource = dirs.Select(l => new DirectoryModel(l));
}
#endregion
#region 搜索行为实现
#endregion
}
#region MinIO 模型类
public interface IMinIOFileInfo : ISystemFileInfo
{
string BucketName { get; }
bool IsDirectory { get; }
bool IsBucket { get; }
}
public class MinIOFileInfo : IMinIOFileInfo, IFileInfo
{
public MinIOFileInfo(string bucketName, string key, string size, DateTime? lastModified)
{
BucketName = bucketName;
FullName = $"{bucketName}/{key}";
Name = Path.GetFileName(key);
Size = size;
LastModified = lastModified;
}
public string BucketName { get; }
public bool IsDirectory => false;
public bool IsBucket => false;
public string Name { get; }
public string FullName { get; }
public string Size { get; }
public DateTime? LastModified { get; }
public DateTime? LastWriteTime => LastModified;
public FileAttributes Attributes => FileAttributes.Normal;
}
public class MinIODirectoryInfo : IMinIOFileInfo, IDirectoryInfo
{
public MinIODirectoryInfo(string bucketName, string key, bool isBucket, DateTime creationDate)
{
BucketName = bucketName;
FullName = string.IsNullOrEmpty(key) ? bucketName : $"{bucketName}/{key}";
Name = string.IsNullOrEmpty(key) ? bucketName : key.TrimEnd('/').Split('/').Last();
IsBucket = isBucket;
LastWriteTime = creationDate;
}
public string BucketName { get; }
public bool IsDirectory => true;
public bool IsBucket { get; }
public string Name { get; }
public string FullName { get; }
public DateTime LastWriteTime { get; }
public FileAttributes Attributes => FileAttributes.Directory;
}
public class MinIOFileModel : FileModel
{
public MinIOFileModel(IFileInfo model) : base(model)
{
this.Icon = "\ue7c4"; // 默认文件图标
}
public new Icon Logo => ExplorerMinIOBehavior.GetIconForMinIOItem(
this.Model.Name,
isDirectory: false);
private bool _isRenaming = true; // 默认不可编辑
public bool IsRenaming
{
get => _isRenaming;
set
{
if (_isRenaming != value)
{
_isRenaming = value;
RaisePropertyChanged(nameof(IsRenaming));
}
}
}
private bool _isChecked = false; // 默认不选中
public new bool IsChecked
{
get => _isChecked;
set
{
if (_isChecked != value)
{
_isChecked = value;
RaisePropertyChanged(nameof(IsChecked));
}
}
}
}
public class MinIODirectoryModel : DirectoryModel
{
public MinIODirectoryModel(IDirectoryInfo model) : base(model)
{
this.Icon = "\ue8b7"; // 默认文件夹图标
}
public new Icon Logo => ExplorerMinIOBehavior.GetIconForMinIOItem(
this.Model.Name,
isDirectory: true,
isBucket: (this.Model as IMinIOFileInfo)?.IsBucket ?? false);
private bool _isRenaming = true; // 默认不可编辑
public bool IsRenaming
{
get => _isRenaming;
set
{
if (_isRenaming != value)
{
_isRenaming = value;
RaisePropertyChanged(nameof(IsRenaming));
}
}
}
private bool _isChecked = false; // 默认不选中
public new bool IsChecked
{
get => _isChecked;
set
{
if (_isChecked != value)
{
_isChecked = value;
RaisePropertyChanged(nameof(IsChecked));
}
}
}
}
#endregion
}