以下是基于MAUI Blazor整合SQLite数据库与Star打印机的详细步骤,包含必要的NuGet包引入及核心代码实现:
零、目錄結構
一、整合SQLite数据库
1. 安装NuGet包
# SQLite核心库
Install-Package sqlite-net-pcl
# SQLite平台适配库(解决跨平台兼容性问题)
Install-Package sqlitepclraw.bundle_green
2. 数据库配置
-
创建数据模型(如
Order.cs
):using SQLite; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StarMauiPrinter.Models { public class Order { [PrimaryKey, AutoIncrement] public int Id { get; set; } // 注文番号 public string OrderNumber { get; set; } // 注文日時 public string OrderTime { get; set; } // 商品コード public string ProductCode { get; set; } // 商品名 public string ProductName { get; set; } // 単価 public decimal UnitPrice { get; set; } // 数量 public int Quantity { get; set; } // 担当者 public string ResponsiblePerson { get; set; } // 顧客名 public string CustomerName { get; set; } // 支払方法 public string PaymentMethod { get; set; } // 合計金額 public decimal TotalAmount { get; set; } public override string ToString() => $"[{OrderNumber}] {ProductName} x{Quantity} 総額:{TotalAmount:C}"; } }
-
实现数据库服务类(
DatabaseHelper.cs
):using SQLite; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StarMauiPrinter.Services { public class DatabaseHelper { private readonly SQLiteAsyncConnection _connection; public DatabaseHelper(string dbName = "app.db3") { var dbPath = Path.Combine(FileSystem.AppDataDirectory, dbName); Console.WriteLine($"Database Path: {dbPath}"); _connection = new SQLiteAsyncConnection(dbPath); } /// <summary> /// テーブルを作成する(ジェネリック) /// </summary> public Task CreateTableAsync<T>() where T : new() { return _connection.CreateTableAsync<T>(); } /// <summary> /// 全てのレコードを取得する /// </summary> public Task<List<T>> GetAllAsync<T>() where T : new() { return _connection.Table<T>().ToListAsync(); } /// <summary> /// 主キーIDでレコードを取得する /// </summary> public Task<T> GetByIdAsync<T>(int id) where T : new() { return _connection.FindAsync<T>(id); } /// <summary> /// レコードを挿入または更新する /// </summary> public async Task<int> SaveAsync<T>(T entity) where T : class { var type = typeof(T); var idProp = type.GetProperty("Id"); var id = (int?)idProp?.GetValue(entity); if (id == 0) { return await _connection.InsertAsync(entity); } var updated = await _connection.UpdateAsync(entity); var result = updated > 0 ? id.Value : 0; return result; } /// <summary> /// レコードを挿入する /// </summary> public Task<int> InsertAsync<T>(T entity) where T : new() { return _connection.InsertAsync(entity); } /// <summary> /// レコードを更新する /// </summary> public Task<int> UpdateAsync<T>(T entity) where T : new() { return _connection.UpdateAsync(entity); } /// <summary> /// レコードを削除する /// </summary> public Task<int> DeleteAsync<T>(T entity) where T : new() { return _connection.DeleteAsync(entity); } /// <summary> /// SQL文を実行してデータを取得する(クエリ) /// </summary> public Task<List<T>> QueryAsync<T>(string sql, params object[] args) where T : new() { return _connection.QueryAsync<T>(sql, args); } /// <summary> /// SQL文を実行する(非クエリ) /// </summary> public Task<int> ExecuteAsync(string sql, params object[] args) { return _connection.ExecuteAsync(sql, args); } / <summary> / トランザクションを実行する / </summary> //public Task RunInTransactionAsync(Action<SQLiteConnection> action) //{ // return Task.Run(() => // { // var syncConn = _connection.GetConnection(); // syncConn.RunInTransaction(action); // }); //} } }
-
定义類service類(OrderService.cs)
using StarMauiPrinter.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StarMauiPrinter.Services { public class OrderService { private readonly DatabaseHelper _db; public OrderService(DatabaseHelper dbHelper) { try { _db = dbHelper; _db.CreateTableAsync<Order>().Wait(); } catch (Exception ex) { throw; } } public Task<List<Order>> GetAllAsync() { try { return _db.GetAllAsync<Order>(); } catch (Exception ex) { throw; } } public Task<Order> GetByIdAsync(int id) { try { return _db.GetByIdAsync<Order>(id); } catch (Exception ex) { throw; } } public Task<int> SaveAsync(Order order) { try { if (string.IsNullOrWhiteSpace(order.OrderNumber)) { order.OrderNumber = $"ORD-{DateTime.Now:yyyyMMddHHmmss}-{Guid.NewGuid().ToString("N")[..4]}"; } return _db.SaveAsync(order); } catch (Exception ex) { throw; } } public Task<int> DeleteAsync(Order order) { try { return _db.DeleteAsync(order); } catch (Exception ex) { throw; } } public Task<List<Order>> QueryByCustomer(string customerName) { try { return _db.QueryAsync<Order>("SELECT * FROM Order WHERE CustomerName = ?", customerName); } catch (Exception ex) { throw; } } } }
3. 注册服务
在MauiProgram.cs
中注入数据库服务:
// sqlite
builder.Services.AddSingleton<DatabaseHelper>();
builder.Services.AddSingleton<OrderService>();
builder.Services.AddSingleton<Order>();
二、整合Star打印机
1. 安装Star打印机SDK
通过NuGet安装官方或第三方SDK(需根据具体型号选择):
using StarMicronics.StarIO;
using StarMicronics.StarIOExtension;
using StarMicronics.StarIODeviceSetting;
# 示例包(需确认具体型号)
Install-Package StarIO_Extension
2. 实现跨平台打印服务
-
定义打印幫助類(
PrintService.cs
):using System; using System.Text; using System.Threading.Tasks; using System.Data; using System.Collections.Generic; using StarMicronics.StarIO; using StarMicronics.StarIOExtension; using StarMicronics.StarIODeviceSetting; namespace StarMauiPrinter.Services { /// <summary> /// STARプリンター操作用サービスクラス /// </summary> internal class PrintService { // プリンター接続情報 デフォルトIPアドレス public string PrinterIp { get; set; } = "192.168.11.75"; // デフォルトポート番号 public int PrinterPort { get; set; } = 9100; // タイムアウト設定(ミリ秒) private const int Timeout = 10000; /// <summary> /// プリンター接続情報を指定可能なコンストラクター /// </summary> /// <param name="printerIp">IPアドレス</param> /// <param name="printerPort">ポート番号</param> public PrintService(string printerIp = "192.168.11.75", int printerPort = 9100) { PrinterIp = printerIp; PrinterPort = printerPort; } /// <summary> /// テスト用レシート印刷メソッド(非同期) /// </summary> /// <param name="portTimeout">接続タイムアウト時間</param> /// <returns>操作結果メッセージ</returns> public async Task<string> PrintTestReceipt(int portTimeout = Timeout) { string portName = $"TCP:{PrinterIp}"; string portSettings = "Standard"; IPort port = null; try { // プリンターポートを取得 port = Factory.I.GetPort(portName, portSettings, portTimeout); // 日本JIS X 9001規格に準拠した領収書コマンドを生成 byte[] command = GenerateJapaneseReceipt(); //byte[] command = GenerateJapaneseReceipt_StarPRNT(); // 非同期印刷実行 await Task.Run(() => port.WritePort(command, 0, (uint)command.Length)); return "✅ 印刷が正常に完了しました!"; } catch (PortException pEx) { return $"❌ ハードウェア接続異常:\n•{pEx.Message}"; } catch (EncoderFallbackException) { return "❌ 文字コード異常:Shift_JIS非対応文字検出"; } catch (Exception ex) { return $"❌ システム例外:{ex.GetType().Name}\n{ex.Message}"; } finally { if (port != null) { // StarMicronics SDK仕様に基づきリソースを解放 Factory.I.ReleasePort(port); port = null; } } } /// <summary> /// 日本の請求書仕様に準拠した印刷指示書を生成します /// </summary> private byte[] GenerateJapaneseReceipt() { // NO string no = "NO.026"; // 時間 string formattedDateTime = DateTime.Now.ToString("yy/MM/dd (HH:mm)"); // 担当者名 string representative = "祐希"; // お客様名 string customer = "みおん"; // 総合計 string total = "10,000"; // お支払 string payment = "10,000"; // 支払い方法 string paymentType = "現金"; // お預り string deposit = "10,000"; // お釣り string change = "0"; // セット料金 string setfee = "10,000"; // 小計 string subtotal = "5,000"; // プリンターの初期化 List<byte> commands = new List<byte>(); // ESC @ プリンターリセット commands.AddRange(new byte[] { 0x1B, 0x40 }); // 日本語Shift_JISエンコーディング設定 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var shiftJis = Encoding.GetEncoding("Shift_JIS"); commands.AddRange(new byte[] { 0x1B, 0x61, 0x01, 0x1D, 0x21, 0x11 }); commands.AddRange(shiftJis.GetBytes( $" 【御計算書】 {formattedDateTime} {no}\n")); // 担当者・顧客情報 commands.AddRange(new byte[] { 0x1B, 0x61, 0x00 }); commands.AddRange(shiftJis.GetBytes( $"担当者:{representative} お客様:{customer} 様\n")); // 明細情報 commands.AddRange(new byte[] { 0x1B, 0x61, 0x02 }); commands.AddRange(shiftJis.GetBytes( $"[4-L] 初期インデックス 21:56~\n\n" + $" 総合計: ¥{total}\n" + $"-----------------------------------------------\n" + $" お支払: {paymentType} ¥{payment}\n" + $" (お預り: ¥{deposit} お釣り: ¥{change})\n")); // 料金詳細セクション commands.AddRange(new byte[] { 0x1B, 0x61, 0x02 }); commands.AddRange(shiftJis.GetBytes( $" セット料金: {setfee}\n" + $" ---------------------------\n" + $" 小計: {subtotal}\n")); // 終了処理 自動用紙切断コマンド(ESC V 66) commands.AddRange(new byte[] { 0x1D, 0x56, 0x41 }); return commands.ToArray(); } } }
-
注册平台服务:
在MauiProgram.cs
中添加:// PrintService builder.Services.AddSingleton<PrintService>();
三、Blazor组件集成
1. 打印頁面Print.razor
@page "/print"
@inject Services.PrintService PrintService
<h3>StarPrintTest</h3>
<button class="btn btn-primary" @onclick="PrintTest">印刷</button>
<p>@message</p>
@code {
private string message;
private async Task PrintTest()
{
try
{
// 印刷サービスを呼び出す
message = await PrintService.PrintTestReceipt();
}
catch (Exception ex)
{
message = $"エラー: {ex.Message}";
}
}
}
2. 數據操作頁面(Weather.razor)
@page "/weather"
@inject Services.OrderService OrderService
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Weather</h1>
<div style="display: flex; align-items: center; gap: 10px;">
<label for="orderNumber">ID:</label>
<input type="text" id="id" @bind="id" placeholder="IDを入力してください" />
<button @onclick="SearchOrder">検索</button>
<button @onclick="ShowAddForm">新規</button>
<label style="color:red">
@if (!string.IsNullOrEmpty(errorMessage))
{
@errorMessage
}
</label>
</div>
@if (orders == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>订单番号</th>
<th>注文時間</th>
<th>商品番号</th>
<th>商品名称</th>
<th>単価</th>
<th>件数</th>
<th>担当者</th>
<th>お客様</th>
<th>支払い方法</th>
<th>最終支払い金額</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in orders)
{
<tr>
<td>@forecast.Id</td>
<td>@forecast.OrderNumber</td>
<td>@forecast.OrderTime</td>
<td>@forecast.ProductCode</td>
<td>@forecast.ProductName</td>
<td>@forecast.UnitPrice.ToString("N0") 円</td>
<td>@forecast.Quantity 件</td>
<td>@forecast.ResponsiblePerson</td>
<td>@forecast.CustomerName</td>
<td>@forecast.PaymentMethod</td>
<td>@forecast.TotalAmount.ToString("N0") 円</td>
<td>
<button @onclick="() => DeleteOrder(forecast)">削除</button>
<button @onclick="() => EditOrder(forecast)">変更</button>
</td>
</tr>
}
</tbody>
</table>
}
<div class="modal fade @(showEditForm ? "show d-block" : "")" tabindex="-1" role="dialog" style="background-color: rgba(0,0,0,0.5);">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@((newOrder?.Id ?? 0) == 0 ? "新規注文" : "注文の編集")</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="CancelEdit"></button>
</div>
<div class="modal-body">
<EditForm Model="newOrder" OnValidSubmit="SaveOrder">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-2">
<label>注文番号</label>
<InputText class="form-control" @bind-Value="newOrder.OrderNumber" />
</div>
<div class="mb-2">
<label>注文時間</label>
<InputText class="form-control" @bind-Value="newOrder.OrderTime" @bind-Value:format="yyyy-MM-dd" />
@* <InputDate @bind-Value="newOrder.OrderTime" /> *@
@* <InputText type="datetime-local" class="form-control" @bind-Value="newOrder.OrderTime" @bind-Value:format="yyyy/MM/dd HH:mm" /> *@
</div>
<div class="mb-2">
<label>商品番号</label>
<InputText class="form-control" @bind-Value="newOrder.ProductCode" />
</div>
<div class="mb-2">
<label>商品名称</label>
<InputText class="form-control" @bind-Value="newOrder.ProductName" />
</div>
<div class="mb-2">
<label>単価</label>
<InputNumber class="form-control" @bind-Value="newOrder.UnitPrice" />
</div>
<div class="mb-2">
<label>件数</label>
<InputNumber class="form-control" @bind-Value="newOrder.Quantity" />
</div>
<div class="mb-2">
<label>担当者</label>
<InputText class="form-control" @bind-Value="newOrder.ResponsiblePerson" />
</div>
<div class="mb-2">
<label>お客様</label>
<InputText class="form-control" @bind-Value="newOrder.CustomerName" />
</div>
<div class="mb-2">
<label>支払い方法</label>
<InputText class="form-control" @bind-Value="newOrder.PaymentMethod" />
</div>
<div class="mb-2">
<label>最終支払い金額</label>
<InputNumber class="form-control" @bind-Value="newOrder.TotalAmount" />
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" class="btn btn-secondary" @onclick="CancelEdit">取消</button>
</div>
</EditForm>
</div>
</div>
</div>
</div>
@code {
private List<Models.Order> orders;
private string id = "";
private string errorMessage = "";
private bool showEditForm = false;
private Models.Order newOrder = new Models.Order();
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
}
private async Task LoadDataAsync()
{
orders = await OrderService.GetAllAsync();
}
private async Task DeleteOrder(Models.Order order)
{
var isConfirmed = await JS.InvokeAsync<bool>("confirm", "本当に削除しますか?");
if (isConfirmed)
{
clearMassg();
await OrderService.DeleteAsync(order);
await LoadDataAsync();
}
}
private void clearMassg()
{
errorMessage = string.Empty;
}
private async Task SearchOrder()
{
clearMassg();
if (!string.IsNullOrEmpty(id))
{
if (!int.TryParse(id, out int outid))
{
errorMessage = "IDは数字でなければなりません";
id = string.Empty;
return;
}
var order = await OrderService.GetByIdAsync(Convert.ToInt32(id));
if (order != null)
{
orders = new List<Models.Order> { order };
}
else
{
orders = new List<Models.Order>();
}
}
else
{
await LoadDataAsync();
}
}
private void ShowAddForm()
{
clearMassg();
newOrder = new Models.Order();
showEditForm = true;
}
private void EditOrder(Models.Order order)
{
clearMassg();
newOrder = new Models.Order
{
Id = order.Id,
OrderNumber = order.OrderNumber,
OrderTime = order.OrderTime,
ProductCode = order.ProductCode,
ProductName = order.ProductName,
UnitPrice = order.UnitPrice,
Quantity = order.Quantity,
ResponsiblePerson = order.ResponsiblePerson,
CustomerName = order.CustomerName,
PaymentMethod = order.PaymentMethod,
TotalAmount = order.TotalAmount
};
showEditForm = true;
}
private async Task SaveOrder()
{
await OrderService.SaveAsync(newOrder);
showEditForm = false;
newOrder = new Models.Order();
await LoadDataAsync();
}
private void CancelEdit()
{
clearMassg();
showEditForm = false;
}
}
关键注意事项
- 数据库事务:在批量操作时使用
BeginTransaction
提升性能。 - 打印机兼容性:不同型号Star打印机需调整指令格式(如ESC/POS指令集)。
- 异步处理:避免在UI线程执行耗时操作(如数据库查询、打印任务)。
- 错误处理:捕获SQLite和打印机连接中的异常,确保应用稳定性。