重构代码并添加新功能

在 `App.xaml.cs` 中,调整了命名空间的格式,并重新排列了代码块,使其更具可读性。

在 `MainWindow.xaml.cs` 中,添加了 `WeakReferenceMessenger.Default.Register<ErrorDialogMessage>` 的注册。

在 `HostExtension.cs` 中,调整了命名空间的格式,并将 `HostExtension` 类从内部类改为静态类,同时优化了 `AddViewAndViewModel` 方法的实现。

在 `ImportExcelPage.xaml` 中,添加了新的命名空间引用 `converter`,并添加了 `BooleanToVisibilityConverter` 和 `ReBooleanToVisibilityConverter` 转换器。同时,添加了新的 `LoadingMask` DataTemplate 和相关的 `ContentPresenter`,并对现有的控件属性进行了调整和优化。

在 `ImportExcelPage.xaml.cs` 中,删除了一些注释代码,并调整了命名空间的格式。

在 `ImportViewModel.cs` 中,添加了新的命名空间引用,并重构了 `ImportViewModel` 类,添加了异步方法 `ReadForExcel` 和 `GetSheets`,以及新的命令 `OnReLoadExcel` 和 `OnReadSheets`。同时,调整了属性的定义和初始化。

新增了 `BooleanConverter.cs` 文件,定义了一个通用的布尔值转换器类 `BooleanConverter<T>`。

新增了 `ReBooleanToVisibilityConverter.cs` 文件,定义了一个反转的布尔值到可见性转换器类 `ReBooleanToVisibilityConverter`。
This commit is contained in:
lihanbo 2024-10-21 15:21:17 +08:00
parent 9de71dcef1
commit 05b41a5685
8 changed files with 353 additions and 189 deletions

View File

@ -5,42 +5,39 @@ using ExcelHelper.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ExcelHelper
namespace ExcelHelper;
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
public static App AppHost;
private readonly IHost _host = Host.CreateDefaultBuilder()
.AddViewAndViewModel()
.ConfigureServices((context, services) =>
{
services.AddHostedService<ExcelHelperHostedService>();
services.AddSingleton<NavigationService>();
})
.Build();
protected override void OnStartup(StartupEventArgs e)
{
AppHost = this;
public static App AppHost;
private readonly IHost _host = Host.CreateDefaultBuilder()
.AddViewAndViewModel()
.ConfigureServices((context, services) =>
{
services.AddHostedService<ExcelHelperHostedService>();
services.AddSingleton<NavigationService>();
base.OnStartup(e);
// 初始化AppHost
_host.Start();
}
})
.Build();
protected override void OnStartup(StartupEventArgs e)
{
AppHost = this;
base.OnStartup(e);
// 初始化AppHost
_host.Start();
}
public T Get<T>()
{
return _host.Services.GetService<T>();
}
public object Get(Type type)
{
return _host.Services.GetService(type);
}
public T Get<T>()
{
return _host.Services.GetService<T>();
}
public object Get(Type type)
{
return _host.Services.GetService(type);
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;
namespace ExcelHelper.Converter;
public class BooleanConverter<T> : IValueConverter
{
protected BooleanConverter(T tValue, T fValue)
{
True = tValue;
False = fValue;
}
public T True
{
get; set;
}
public T False
{
get; set;
}
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool flag && flag ? True : False;
}
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is T flag && EqualityComparer<T>.Default.Equals(flag, True);
}
}

View File

@ -0,0 +1,12 @@
using System.Windows;
using System.Windows.Data;
namespace ExcelHelper.Converter;
/// <summary>
/// BooleanToVisibilityConverter 反转
/// </summary>
[ValueConversion(typeof(bool), typeof(Visibility))]
public class ReBooleanToVisibilityConverter : BooleanConverter<Visibility>
{
public ReBooleanToVisibilityConverter() : base(Visibility.Visible, Visibility.Collapsed) { }
}

View File

@ -18,6 +18,7 @@ namespace ExcelHelper
InitializeComponent();
DataContext = ViewModel = viewModel;
navigationService.InitForFrame(MainFrame);
WeakReferenceMessenger.Default.Register<ErrorDialogMessage>(this, (r, message) =>
{
if (message.Value != null)

View File

@ -3,30 +3,29 @@ using ExcelHelper.Views;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ExcelHelper.Utils
namespace ExcelHelper.Utils;
internal static class HostExtension
{
internal static class HostExtension
public static IHostBuilder AddViewAndViewModel(this IHostBuilder hostBuilder)
{
public static IHostBuilder AddViewAndViewModel(this IHostBuilder hostBuilder)
// Scan all assemblies to find all types that implement IViewModel
var assem = Assembly.GetExecutingAssembly();
var types = assem.GetTypes();
foreach (var type in types)
{
// Scan all assemblies to find all types that implement IViewModel
var assem = Assembly.GetExecutingAssembly();
var types = assem.GetTypes();
foreach (var type in types)
if (type.IsInterface || type.IsAbstract)
{
if (type.IsInterface || type.IsAbstract)
{
continue;
}
if (typeof(IViewModel).IsAssignableFrom(type) || typeof(IView).IsAssignableFrom(type))
{
hostBuilder.ConfigureServices((context, services) =>
{
services.AddSingleton(type);
});
}
continue;
}
if (typeof(IViewModel).IsAssignableFrom(type) || typeof(IView).IsAssignableFrom(type))
{
hostBuilder.ConfigureServices((context, services) =>
{
services.AddSingleton(type);
});
}
return hostBuilder;
}
return hostBuilder;
}
}

View File

@ -2,11 +2,12 @@
x:Class="ExcelHelper.Views.Pages.ImportExcelPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:ExcelHelper.Converter"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:local="clr-namespace:ExcelHelper.Views.Pages"
xmlns:utils="clr-namespace:ExcelHelper.Utils"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:utils="clr-namespace:ExcelHelper.Utils"
xmlns:viewmodels="clr-namespace:ExcelHelper.Views.ViewModels"
Title="ImportExcelPage"
d:DataContext="{d:DesignInstance Type=viewmodels:ImportViewModel}"
@ -14,6 +15,8 @@
d:DesignWidth="1200"
mc:Ignorable="d">
<Page.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converter:ReBooleanToVisibilityConverter x:Key="ReBooleanToVisibilityConverter" />
<DataTemplate x:Key="Mask">
<Grid Background="#99424242">
<TextBlock
@ -22,6 +25,19 @@
FontSize="20"
Foreground="White"
Text="拖拽到此处放开" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="LoadingMask">
<Grid Background="#99424242">
<hc:CircleProgressBar
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Foreground="White"
IsIndeterminate="True"
Text="加载中" />
</Grid>
</DataTemplate>
</Page.Resources>
@ -39,16 +55,39 @@
Grid.Column="0"
Grid.ColumnSpan="2"
Panel.ZIndex="10"
Content="{Binding}"
ContentTemplate="{StaticResource Mask}"
Visibility="Collapsed" />
<ContentPresenter
x:Name="LoadingMask"
Grid.Column="0"
Grid.ColumnSpan="2"
Panel.ZIndex="10"
Content="{Binding}"
ContentTemplate="{StaticResource LoadingMask}"
Visibility="{Binding IsLoading, Converter={StaticResource Boolean2VisibilityConverter}}" />
<hc:TabControl Grid.Column="0" Margin="5,5,7,5">
<hc:TabItem Header="预览">
<DataGrid IsReadOnly="True" x:Name="ExcelDataPreviewGrid" AutoGenerateColumns="False" ColumnWidth="Auto" ItemsSource="{Binding ExcelData}" />
<DataGrid
x:Name="ExcelDataPreviewGrid"
hc:DataGridAttach.ShowRowNumber="True"
AutoGenerateColumns="False"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
IsReadOnly="True"
ItemsSource="{Binding ExcelData, IsAsync=True}"
ScrollViewer.CanContentScroll="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling" />
</hc:TabItem>
<hc:TabItem Header="列信息">
<DataGrid>
<DataGrid.Columns >
<DataGridTextColumn Header="列" />
<DataGrid
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding ExcelColumns, IsAsync=True}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Header}" Header="列" />
</DataGrid.Columns>
</DataGrid>
</hc:TabItem>
@ -67,7 +106,10 @@
Margin="5,0"
VerticalAlignment="Center"
Text="参数配置" />
<Button VerticalAlignment="Center" Content="重新读取Excel" />
<Button
VerticalAlignment="Center"
Command="{Binding ReLoadExcelCommand}"
Content="重新读取Excel" />
</StackPanel>
</GroupBox.Header>
<hc:SimpleStackPanel SnapsToDevicePixels="True">
@ -79,33 +121,44 @@
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="指定的Sheet" />
<hc:ComboBox MinWidth="60" Margin="0" ItemsSource="{Binding Sheets}" SelectedValue="{Binding SelectedSheetName}" />
<hc:ComboBox
MinWidth="60"
Margin="0"
ItemsSource="{Binding Sheets}"
SelectedValue="{Binding SelectedSheetName}" />
<Button
Margin="0"
VerticalAlignment="Center"
Content="读取Excel" />
Command=""
Content="读取" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Height="Auto" Orientation="Horizontal">
<Label Content="起始位置:" />
<hc:TextBox MinWidth="40" Text="{Binding StartCell}" />
<CheckBox Content="包含列头" IsChecked="{Binding UseHeaderRow}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<hc:SimpleStackPanel IsEnabled="False" Orientation="Horizontal">
<Label Content="结束位置:" />
<hc:TextBox MinWidth="40" Text="{Binding EndCell}" />
<hc:TextBox MinWidth="40" Text="{Binding EndCell}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="最大读取行数:" />
<hc:NumericUpDown MinWidth="40" Minimum="1" Value="{Binding MaxRow}" />
<hc:NumericUpDown
MinWidth="40"
Minimum="0"
Value="{Binding MaxRow}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Width="Auto" Orientation="Horizontal">
<Label Content="当前文件路径:" />
<!--<hc:TextBox MinWidth="200" />-->
<hc:ComboBox ItemsSource="{Binding ExcelFiles}" SelectedValue="{Binding CurrentFilePath}" MaxWidth="200" />
<hc:ComboBox
MaxWidth="200"
ItemsSource="{Binding ExcelFiles}"
SelectedValue="{Binding CurrentFilePath}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="Excel密码" />
<PasswordBox utils:PasswordBoxHelper.Password="{Binding ExcelPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="60" />
<PasswordBox MinWidth="60" utils:PasswordBoxHelper.Password="{Binding ExcelPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="?" ToolTip="未加密留空多个Excel如果不同密码请分批次导入" />
</hc:SimpleStackPanel>
</hc:SimpleStackPanel>

View File

@ -15,8 +15,6 @@ namespace ExcelHelper.Views.Pages
{
InitializeComponent();
this.DataContext = ViewModel = viewModel;
//GenerateDataGridColumns();
//Messenger
ViewModel.IsActive = true;
// Register a message in some module
WeakReferenceMessenger.Default.Register<UpdateDataGridColumnsMessage>(this, (r, message) =>
@ -52,7 +50,6 @@ namespace ExcelHelper.Views.Pages
var files = (string[])e.Data.GetData(System.Windows.DataFormats.FileDrop);
if (files.Length > 0)
{
//ViewModel.FileDropCommand.CanExecute(files);
ViewModel.FileDrop(files);
}
}
@ -74,6 +71,5 @@ namespace ExcelHelper.Views.Pages
}
}
}
}
}

View File

@ -1,134 +1,205 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using ExcelHelper.Message;
using MiniExcelLibs;
namespace ExcelHelper.Views.ViewModels
namespace ExcelHelper.Views.ViewModels;
public partial class ImportViewModel : ObservableRecipient, IViewModel
{
public partial class ImportViewModel : ObservableRecipient, IViewModel
public void FileDrop(string[] files)
{
public void FileDrop(string[] files)
IsLoading = true;
ExcelFiles.Clear();
SelectedSheetName = null;
Sheets.Clear();
MaxRow = 0;
foreach (var item in files)
{
ExcelFiles.Clear();
foreach (var item in files)
{
ExcelFiles.Add(item);
}
//ReadForExcel(ExcelFiles.First());
CurrentFilePath = ExcelFiles.First();
ExcelFiles.Add(item);
}
public void ReadForExcel(string path)
{
ExcelData = MiniExcel.Query(path, useHeaderRow: UseHeaderRow);
var columns = MiniExcel.GetColumns(path, useHeaderRow: UseHeaderRow);
GenColumns(columns);
}
private void GenColumns(IEnumerable<string> columns)
{
Columns.Clear();
if (UseHeaderRow)
{
foreach (var columnName in columns)
{
var column = new DataGridTextColumn
{
Header = columnName,
Binding = new System.Windows.Data.Binding($"{columnName}"),
Width = DataGridLength.Auto
};
Columns.Add(column);
}
}
else
{
for (var i = 'A'; i < 'Z'; i++)
{
var column = new DataGridTextColumn
{
Header = $"{i}",
Binding = new System.Windows.Data.Binding($"{i}"),
Width = DataGridLength.Auto
};
Columns.Add(column);
}
}
if (Columns.Count > 0)
WeakReferenceMessenger.Default.Send(new UpdateDataGridColumnsMessage([.. Columns]));
else
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage("未读取到列信息!"));
}
}
#region Props
[ObservableProperty]
private IEnumerable<dynamic> _excelData;
[ObservableProperty]
private ObservableCollection<string> _excelFiles = [];
[ObservableProperty]
private ObservableCollection<DataGridColumn> _columns = [];
/// <summary>
/// 使用首行作为表头
/// </summary>
[ObservableProperty]
private bool _useHeaderRow;
/// <summary>
/// 选中的Sheet
/// </summary>
[ObservableProperty]
private string _selectedSheetName;
/// <summary>
/// Sheets列表
/// </summary>
[ObservableProperty]
private ObservableCollection<string> _sheets = [];
/// <summary>
/// 起始位置
/// </summary>
[ObservableProperty]
private string _startCell;
/// <summary>
/// 结束位置
/// </summary>
[ObservableProperty]
private string _endCell;
/// <summary>
/// 最大行数
/// </summary>
[ObservableProperty]
private string _maxRow;
/// <summary>
/// 当前文件路径
/// </summary>
private string _currentFilePath;
public string CurrentFilePath
{
get => _currentFilePath;
set
{
SetProperty(ref _currentFilePath, value);
ReadForExcel(value);
}
}
/// <summary>
/// Excel文件密码
/// </summary>
[ObservableProperty]
private string _excelPassword;
#endregion
//ReadForExcel(ExcelFiles.First());
CurrentFilePath = ExcelFiles.FirstOrDefault();
IsLoading = false;
}
public async void ReadForExcel(string path)
{
IsLoading = true;
//await Task.Factory.StartNew(async () =>
//{
try
{
if (string.IsNullOrEmpty(path)) return;
var excelData = await MiniExcel.QueryAsync(path, sheetName: SelectedSheetName, useHeaderRow: UseHeaderRow, startCell: StartCell);
if (MaxRow != 0)
{
ExcelData = excelData.Take(Math.Min(MaxRow, 300));
}
else
{
ExcelData = excelData.Take(300);
}
var columns = MiniExcel.GetColumns(path, useHeaderRow: UseHeaderRow, startCell: StartCell, sheetName: SelectedSheetName);
GenColumns(columns);
GetSheets(path);
MaxRow = ExcelData.Count();
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage($"出现了异常!{ex}"));
}
IsLoading = false;
//}).ContinueWith(x =>
// {
// });
}
private void GenColumns(IEnumerable<string> columns)
{
ExcelColumns.Clear();
if (UseHeaderRow)
{
foreach (var columnName in columns)
{
var column = new DataGridTextColumn
{
Header = columnName,
Binding = new System.Windows.Data.Binding($"{columnName}"),
Width = DataGridLength.Auto
};
ExcelColumns.Add(column);
}
}
else
{
for (var i = 'A'; i < 'Z'; i++)
{
var column = new DataGridTextColumn
{
Header = $"{i}",
Binding = new System.Windows.Data.Binding($"{i}"),
Width = DataGridLength.Auto
};
ExcelColumns.Add(column);
}
}
if (ExcelColumns.Count > 0)
WeakReferenceMessenger.Default.Send(new UpdateDataGridColumnsMessage([.. ExcelColumns]));
else
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage("未读取到列信息!"));
}
}
[RelayCommand]
private void OnReLoadExcel()
{
ReadForExcel(CurrentFilePath);
}
[RelayCommand]
private void OnReadSheets()
{
if (string.IsNullOrWhiteSpace(CurrentFilePath))
{
GetSheets(CurrentFilePath);
}
}
private void GetSheets(string excelPath)
{
if (Sheets == null)
{
Sheets = new ObservableCollection<string>();
}
else
{
Sheets.Clear();
}
MiniExcel.GetSheetNames(excelPath)?.ForEach(sheetName =>
{
Sheets.Add(sheetName);
});
SelectedSheetName = Sheets.FirstOrDefault();
}
#region Props
[ObservableProperty]
private IEnumerable<dynamic> _excelData;
[ObservableProperty]
private ObservableCollection<string> _excelFiles = [];
[ObservableProperty]
private ObservableCollection<DataGridColumn> _excelColumns = [];
/// <summary>
/// 是否正在加载
/// </summary>
[ObservableProperty]
private bool _isLoading = false;
/// <summary>
/// 使用首行作为表头
/// </summary>
[ObservableProperty]
private bool _useHeaderRow = false;
/// <summary>
/// 选中的Sheet
/// </summary>
[ObservableProperty]
private string _selectedSheetName;
/// <summary>
/// Sheets列表
/// </summary>
[ObservableProperty]
private ObservableCollection<string> _sheets = [];
/// <summary>
/// 起始位置
/// </summary>
[ObservableProperty]
private string _startCell = "A1";
/// <summary>
/// 结束位置
/// </summary>
[ObservableProperty]
private string _endCell;
/// <summary>
/// 最大行数
/// </summary>
[ObservableProperty]
private int _maxRow = 0;
/// <summary>
/// 当前文件路径
/// </summary>
private string _currentFilePath;
public string CurrentFilePath
{
get => _currentFilePath;
set
{
SetProperty(ref _currentFilePath, value);
ReadForExcel(value);
}
}
/// <summary>
/// Excel文件密码
/// </summary>
[ObservableProperty]
private string _excelPassword;
#endregion
}