2次元コード生成ツールの開発

2次元コード生成ツールの開発

この記事では、C#とAvalonia実装のデスクトップ版、Blazorフロントエンドと. NET Web API実装のオンライン版を含み、要件分析、コアコード実装、UI設計、MVVMパターンのアプリケーションをカバーしています。

最后更新 2025/03/10 19:54
沙漠尽头的狼
预计阅读 15 分钟
分类
.NET
标签
.NET C# Blazor Avalonia UI MVVM

I.要件分析とプログラム設計

背景の問題

交通量の多い現代都市では、駐車場の問題が続き、車の移動も自動車所有者が日常的に直面する問題の一つになっています。以前は、他の人が移動車に連絡しやすくするために、電話番号が書かれたメモを車内の目立つ場所に置いたり、携帯電話番号が刻まれた移動車の装飾品を置いたりすることが多かった。この伝統的な方法はシンプルに見えますが、多くの隠れた危険があります。

今日、個人情報のセキュリティは重要であり、公共の場所で電話番号を公開することは、プライバシーの扉を開くようなものです。犯罪者は、悪意のある嫌がらせ、販売電話、詐欺的なテキストメッセージが続き、人々が迷惑になるためにこの情報を使用します。ある“ナンバーコピーファミリー”が駐車場をシャトルして移動電話を収集し、マーケティング会社や詐欺グループにこの情報を大量に販売し、所有者の生活が様々なスパム情報で満たされているというニュース報道がありました。

プライバシー侵害に加えて、車両番号の悪用もしばしば行われます。時には明らかに短時間の一時的な駐車場ですが、頻繁に移動電話を受け、非緊急時にはランダムに電話して所有者の時間を遅らせている人もいます。さらに深刻なのは、悪意のある人々が携帯電話を使って詐欺を行い、車両違反や事故などの虚偽の理由で所有者にリンクや転送をクリックさせ、所有者に財産損失をもたらす可能性があることです。

解決策は

上記の問題を解決するために、以下の機能を実現するために、簡単で実用的な2次元コード生成ツールを開発しました。

  1. 所有者の連絡先を含む2次元コードをすばやく生成
  2. 車の所有者に電話をかける。
  3. カスタマイズされた車両移動のヒントテキストをサポート
  4. 携帯電話番号を暗号化して所有者のプライバシーを保護
  5. オフラインデスクトップ版とオンラインWeb版の2種類の利用方法を提供

従来の直接表示される電話番号とは異なり、当社のQRコードツールは暗号化技術を使用して、所有者の携帯電話番号が直接公開されないようにします。同時に、スキャンスキャンを通じて特別な移動ページをジャンプすることで、使用体験が向上するだけでなく、番号が悪用されるリスクも低減します。この記事では、C#とAvaloniaをベースとしたデスクトップアプリケーションと、Blazorフロントエンドと. NET Web APIをベースとしたオンラインWebバージョンの2つの実装について詳しく説明します。

以下のように、詳細なページが表示されます:

詳細ページでは、** をクリックして所有者に電話 ** をかけるか、緑色のハイパーリンク ** をクリックして移動コードを生成します **:

2.コア二次元コード生成コード

首先,让我们看一下核心的二维码生成逻辑。这部分代码封装在CodeWF.Tools项目的QrCodeGenerator类中:

using ImageMagick;
using ImageMagick.Drawing;
using ZXing;
using ZXing.QrCode;

namespace CodeWF.Tools.Image;

public static class QrCodeGenerator
{
    public static void GenerateQrCode(string title, string content, string imagePath, string? subTitle = "")
    {
        var qrCodeWriter = new BarcodeWriterPixelData
        {
            Format = BarcodeFormat.QR_CODE,
            Options = new QrCodeEncodingOptions
            {
                Width = 400,
                Height = 400,
                Margin = 1,
                ErrorCorrection = ZXing.QrCode.Internal.ErrorCorrectionLevel.H,
                CharacterSet = "UTF-8",
                DisableECI = true
            }
        };

        var pixelData = qrCodeWriter.Write(content);

        using var qrCodeImage = new MagickImage();
        var settings = new PixelReadSettings((uint)pixelData.Width, (uint)pixelData.Height, StorageType.Char, PixelMapping.RGBA);
        qrCodeImage.ReadPixels(pixelData.Pixels, settings);

        var backgroundHeight = string.IsNullOrWhiteSpace(subTitle) ? 600u : 630u;
        using var background = new MagickImage(MagickColors.White, 500, backgroundHeight);

        background.BorderColor = new MagickColor("#2888E2");
        background.Border(8);

        var titleText = new Drawables()
            .Font("SimHei")
            .FontPointSize(95)
            .FillColor(new MagickColor("#FF5722"))
            .TextAlignment(TextAlignment.Center)
            .Text(250, 120, title);
        background.Draw(titleText);

        background.Composite(qrCodeImage, 50, 170, CompositeOperator.Over);

        if (!string.IsNullOrWhiteSpace(subTitle))
        {
            var subTitleText = new Drawables()
                .Font("SimHei")
                .FontPointSize(20)
                .FillColor(new MagickColor("#333333"))
                .TextAlignment(TextAlignment.Center)
                .Text(250, 600, subTitle);
            background.Draw(subTitleText);
        }

        //using var logo = new MagickImage("logo.png");
        //logo.Resize(100, 100);
        //background.Composite(logo, 250, 250, CompositeOperator.Over);

        background.Quality = 100;
        background.Write(imagePath);
    }
}

上記のコードはNuGetパッケージZXing. Net. Bindings.Magickを使用しています。ZXing. NETはQRコード行列データの生成に使用され、Magick. NETは画像処理と合成に使用され、以下の機能を実現します。

  1. 高いエラーレベル(Hレベル)をした高QRコードを作成し、二次元コードの部分が隠れていても正常に
  2. 白と青の背景画像を生成します。
  3. 上部にカスタムのタイトルテキストを追加
  4. 2次元コード画像を背景に合成する
  5. QRコードの下部に、カスタム小見出しテキストを追加できます
  6. 高画質PNG画像として保存

この設計により、最終的に生成された2次元コードは美しく実用的です。

挪车二维码

3.オフラインデスクトップ版の実現

1. ユーザーインターフェースデザイン

使用Avalonia框架设计用户界面,界面定义在NuoCheView.axaml文件中:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="clr-namespace:CodeWF.Modules.Converter.ViewModels"
             xmlns:prism="http://prismlibrary.com/"
             xmlns:u="https://irihi.tech/ursa"
             xmlns:i18n="https://codewf.com"
             xmlns:language="clr-namespace:Localization"
             prism:ViewModelLocator.AutoWireViewModel="True"
             x:CompileBindings="True"
             x:DataType="vm:NuoCheViewModel"
             d:DesignHeight="450"
             d:DesignWidth="800"
             x:Class="CodeWF.Modules.Converter.Views.NuoCheView"
             mc:Ignorable="d">
    <StackPanel Margin="10" HorizontalAlignment="Center">
        <u:Form LabelPosition="Left" LabelWidth="*">
            <TextBox Width="300" u:FormItem.Label="{i18n:I18n {x:Static language:NuoCheView.InputPhoneNumber}}"
                     u:FormItem.IsRequired="True" Text="{Binding PhoneNumber}" />
            <TextBox Width="300" u:FormItem.Label="{i18n:I18n {x:Static language:NuoCheView.InputTitle}}"
                     u:FormItem.IsRequired="True" Text="{Binding InputTitle}" />
            <StackPanel Orientation="Horizontal" u:FormItem.Label="{i18n:I18n {x:Static language:NuoCheView.EnableSubTitle}}">
                <CheckBox IsChecked="{Binding EnableSubTitle}" Command="{Binding EnableSubTitleHandler}" VerticalAlignment="Center" Content="显示" Margin="5 0" />
                <TextBox Width="300" Text="{Binding SubTitle}" IsEnabled="{Binding EnableSubTitle}" VerticalAlignment="Center" />
            </StackPanel>
        </u:Form>

        <StackPanel Orientation="Horizontal" Spacing="10" Margin="0,0,0,10">
            <Button Content="{i18n:I18n {x:Static language:NuoCheView.CreateButtonContent}}"
                    Command="{Binding GenerateQrCode}" />
            <Button Content="{i18n:I18n {x:Static language:NuoCheView.SaveQrCodeFileTitle}}"
                    Command="{Binding SaveQrCode}" />
            <HyperlinkButton Content="{i18n:I18n {x:Static language:NuoCheView.PreviewNuoCheUrl}}"
                             NavigateUri="{Binding GeneratedUrl}" Classes="WithIcon Underline"
                             VerticalAlignment="Center" />
        </StackPanel>

        <Image Source="{Binding QrCodeImage}" Width="340" Height="340" DragDrop.AllowDrop="True">
            <Interaction.Behaviors>
                <EventTriggerBehavior EventName="PointerPressed">
                    <InvokeCommandAction Command="{Binding RaisePointerPressed}" PassEventArgsToCommand="True" />
                </EventTriggerBehavior>
            </Interaction.Behaviors>
        </Image>
    </StackPanel>
</UserControl>

インターフェースはシンプルで、主に以下を含む:

  1. 三つの入力ボックス:携帯電話番号と二次元コードタイトル、オプションの二次元コード小見出し
  2. 3つの操作ボタン:二次元コードの生成、二次元コードの保存、移動ページのプレビュー
  3. 画像表示エリア:生成されたQRコードを表示し、ドラッグアンドドロップ操作をサポートします。

効果は以下の通り。

离线版界面截图

2. ビューモデルの実装

NuoCheViewModel.cs中实现业务逻辑:

using Avalonia.Input;
using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;
using AvaloniaXmlTranslator;
using CodeWF.Core.IServices;
using CodeWF.LogViewer.Avalonia;
using CodeWF.Tools.Image;
using HashidsNet;
using ReactiveUI;

namespace CodeWF.Modules.Converter.ViewModels;

public class NuoCheViewModel : ReactiveObject
{
    private readonly INotificationService _notificationService;
    private readonly IFileChooserService _fileChooserService;
    private string _inputTitle;
    private long _phoneNumber;
    private Bitmap? _qrCodeImage;
    private string _qrCodeImagePath;
    private string _generatedUrl;
    private string _subTitle;
    private bool _enableSubTitle;

    public NuoCheViewModel(INotificationService notificationService, IFileChooserService fileChooserService)
    {
        _notificationService = notificationService;
        _fileChooserService = fileChooserService;

        InputTitle = I18nManager.Instance.GetResource(Localization.NuoCheView.DefaultInputTitle);
        PhoneNumber = 16899999999;
        SubTitle = $"{I18nManager.Instance.GetResource(Localization.NuoCheView.DefaultSubTitlePrefix)}: {PhoneNumber}";
        EnableSubTitle = false;
    }

    public string InputTitle
    {
        get => _inputTitle;
        set => this.RaiseAndSetIfChanged(ref _inputTitle, value);
    }

    public long PhoneNumber
    {
        get => _phoneNumber;
        set => this.RaiseAndSetIfChanged(ref _phoneNumber, value);
    }

    public Bitmap? QrCodeImage
    {
        get => _qrCodeImage;
        private set => this.RaiseAndSetIfChanged(ref _qrCodeImage, value);
    }

    public string QrCodeImagePath
    {
        get => _qrCodeImagePath;
        private set => this.RaiseAndSetIfChanged(ref _qrCodeImagePath, value);
    }

    public string GeneratedUrl
    {
        get => _generatedUrl;
        private set => this.RaiseAndSetIfChanged(ref _generatedUrl, value);
    }

    public string SubTitle
    {
        get => _subTitle;
        set => this.RaiseAndSetIfChanged(ref _subTitle, value);
    }

    public bool EnableSubTitle
    {
        get => _enableSubTitle;
        set => this.RaiseAndSetIfChanged(ref _enableSubTitle, value);
    }

    public void EnableSubTitleHandler()
    {
        SubTitle = $"{I18nManager.Instance.GetResource(Localization.NuoCheView.DefaultSubTitlePrefix)}: {PhoneNumber}";
    }

    public void GenerateQrCode()
    {
        if (string.IsNullOrWhiteSpace(InputTitle))
        {
            _notificationService.Show(I18nManager.Instance.GetResource(Localization.NuoCheView.Title),
                I18nManager.Instance.GetResource(Localization.NuoCheView.NeedInputTip));
            return;
        }

        try
        {
            var encodedPhone = new Hashids("codewf").EncodeLong(PhoneNumber);

            GeneratedUrl =
                $"https://codewf.com/nuoche?p={encodedPhone}";
            QrCodeImagePath = Path.Combine(Path.GetTempPath(), "nuoche.png");

            QrCodeGenerator.GenerateQrCode(InputTitle, GeneratedUrl, QrCodeImagePath, 
                EnableSubTitle ? SubTitle : null);

            QrCodeImage = new Bitmap(QrCodeImagePath);
        }
        catch (Exception ex)
        {
            Logger.Error(I18nManager.Instance.GetResource(Localization.NuoCheView.CreateErrorMessage), ex);
            _notificationService.Show(I18nManager.Instance.GetResource(Localization.NuoCheView.Title),
                $"{I18nManager.Instance.GetResource(Localization.NuoCheView.CreateErrorMessage)}: {ex}");
        }
    }


    public async Task SaveQrCode()
    {
        if (QrCodeImage == null || string.IsNullOrEmpty(QrCodeImagePath))
        {
            _notificationService.Show(I18nManager.Instance.GetResource(Localization.NuoCheView.SaveNotificationTitle),
                I18nManager.Instance.GetResource(Localization.NuoCheView.SaveNoQrCodeMessage));
            return;
        }

        try
        {
            var file = await _fileChooserService.SaveFileAsync(
                I18nManager.Instance.GetResource(Localization.NuoCheView.SaveQrCodeFileTitle),
                new[]
                {
                    new FilePickerFileType(
                        I18nManager.Instance.GetResource(Localization.NuoCheView.SaveQrCodeFileFormat))
                    {
                        Patterns = new[] { "*.png" }
                    }
                }
            );

            if (file != null)
            {
                File.Copy(QrCodeImagePath, file, true);
                _notificationService.Show(
                    I18nManager.Instance.GetResource(Localization.NuoCheView.SaveQrCodeSuccessTitle),
                    I18nManager.Instance.GetResource(Localization.NuoCheView.SaveQrCodeSuccessMessage));
            }
        }
        catch (Exception ex)
        {
            _notificationService.Show(I18nManager.Instance.GetResource(Localization.NuoCheView.SaveNotificationTitle),
                $"{I18nManager.Instance.GetResource(Localization.NuoCheView.SaveQrCodeErrorMessage)}: {ex.Message}");
        }
    }

    public async Task RaisePointerPressed(PointerPressedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(QrCodeImagePath) || !File.Exists(QrCodeImagePath))
        {
            return;
        }

        var dragData = new DataObject();
        if (await _fileChooserService.StorageProvider.TryGetFileFromPathAsync(QrCodeImagePath) is { } storageFile)
        {
            dragData.Set(DataFormats.Files, new[] { storageFile });
        }

        await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
    }
}

ビューモデルには次のが実装されている。

  1. ** データバインディング **:ユーザー入力とUIステータスの管理
  2. ** QRコード生成**:コア生成ロジックを呼び出し、結果を表示します。
  3. ** ファイル保存** 生成された二次元コードをユーザ指定の場所に保存します
  4. ** ドラッグ&ドロップサポート **:QRコード画像を他のアプリケーションに直接ドラッグできます。
  5. ** エラー処理 **:フレンドリーなエラー通知を提供します。

值得一提的是,我们使用了Hashids库对手机号进行编码,提高了隐私保护。这样,即使二维码被公开展示,他人也无法直接获取车主的真实手机号(当然最后一步进入拨号界面时会显示真实手机号,这里可考虑使用虚拟号码屏蔽真实手机号)。

2つのトラックを保存する効果:

拖拽保存挪车二维码

IV.オンラインWeb版の実現

デスクトップ版に加えて、ソフトウェアをインストールせずに使用できるBlazorベースのオンラインQRコード生成ツールも開発しました。

オンライン·アクセスhttps//dotnet9.com/nuoche

PCからの車両コードの作成:

在线PC端创建挪车码

携帯電話で車を作る:

在线手机端创建挪车码

1. オンライン車両の特徴

デスクトップ版と比較して、オンライン版は以下の特徴があります。

  1. ** インストール不要 **:ブラウザから直接アクセス
  2. ** 双方向機能 **:二次元コードを生成することも、解析後の移動ページに直接アクセスすることもできます。
  3. ** モバイルフレンドリー **:モバイルデバイスでの表示とインタラクションを最適化します。
  4. ** エレガントなUIデザイン **:モダンなUIコンポーネントとインタラクションを使用します。

2. 実現の方法

オンライン版は Blazor で実装され、コアコード構造は以下の通りです。

@page "/NuoChe"
@using HashidsNet
@inject IOptions<SiteOption> SiteOption
@layout EmptyLayout

@code {
    [SupplyParameterFromQuery]
    public string? P { get; set; }

    public const string Slug = "nuoche";

    private long? _decodePhone;

    protected override void OnInitialized()
    {
        base.OnInitialized();
        if (!string.IsNullOrWhiteSpace(P))
        {
            _decodePhone = new Hashids("codewf").DecodeLong(P)[0];
        }
    }
}

<PageTitle>免费挪车码</PageTitle>

<div class="nuoche-container">
    @if (string.IsNullOrWhiteSpace(P))
    {
        <div class="generator-container">
            <h1 class="generator-title">挪车码在线生成器</h1>
            <div class="input-group">
                <label for="phoneNumber">手机号码</label>
                <input type="tel" id="phoneNumber" placeholder="请输入手机号码" />
            </div>
            <div class="input-group">
                <label for="title">标题</label>
                <input type="text" id="title" placeholder="扫码挪车" value="扫码挪车" />
            </div>
            <div class="input-group">
                <div class="checkbox-container">
                    <input type="checkbox" id="enableSubtitle" />
                    <label for="enableSubtitle">添加小标题</label>
                </div>
                <input type="text" id="subtitle" placeholder="扫码联系车主或拨打电话: 16800000000" disabled />
            </div>
            <div class="button-group">
                <button class="primary-button" onclick="generateQrCode()">
                    <i class="fas fa-qrcode"></i> 生成二维码
                </button>
            </div>
            <div class="qr-code-container" style="display: none">
                <img alt="挪车码" />
                <div class="action-buttons">
                    <a target="_blank" class="preview-link">
                        <i class="fas fa-external-link-alt"></i> 预览
                    </a>
                    <a class="download-link">
                        <i class="fas fa-download"></i> 下载
                    </a>
                </div>
                <div class="alert alert-warning mt-2">
                    <i class="fas fa-clock"></i> 请注意:生成的文件仅保留2分钟,请及时下载。
                </div>
            </div>
        </div>
    }
    else
    {
        <div class="card">
            <div class="card-header">
                <i class="fas fa-car"></i>
                <h1 class="title">临时停靠,请多关照</h1>
            </div>
            <div class="card-body">
                <p class="description">如果我的车阻碍了您的车辆通行,点击下方按钮通知我,给您带来不便敬请谅解。</p>
                <a class="phone-button" href="tel:@_decodePhone">
                    <i class="fas fa-phone-alt"></i> 拨打车主电话
                </a>
                <a class="generate-link" href="/nuoche">去生成一个挪车码</a>
            </div>
        </div>
    }
</div>

この実装には2つの主な機能ページがあります。

  1. **生成ページ **:ユーザーが/nuocheに直接アクセスすると、携帯電話番号とタイトルを入力できるQRコード生成インターフェイスが表示されます。
  2. ** 移動ページ **:ユーザーがQRコードスキャン(pパラメータ付き)でアクセスすると、ワンクリックでオーナーに電話をかけるインターフェイスが表示されます

この設計により、生成、スキャン、接続といった二次元コードの完全なライフサイクルを実現し、ユーザーに便利な移動ソリューションを提供します。

詳細ページを表示する前のページ:

扫码后显示截图

3. オンライン版コアJava Scriptインタラクティブ

async function generateQrCode() {
    const title = document.getElementById('title').value;
    const phoneNumber = document.getElementById('phoneNumber').value;
    const enableSubtitle = document.getElementById('enableSubtitle').checked;
    let subtitle = null;
    
    if (enableSubtitle) {
        subtitle = document.getElementById('subtitle').value.trim();
        if (!subtitle) {
            subtitle = `扫码联系车主或拨打电话: ${phoneNumber || "16800000000"}`;
            document.getElementById('subtitle').value = subtitle;
        }
    }

    if (!title || !phoneNumber) {
        alert('请输入标题和手机号码');
        return;
    }

    try {
        const response = await fetch('/api/Image/nuoche', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                title: title,
                phoneNumber: phoneNumber,
                subtitle: subtitle
            })
        });

        const data = await response.json();
        if (data.success) {
            const qrCodeContainer = document.querySelector('.qr-code-container');
            const img = qrCodeContainer.querySelector('img');
            img.src = data.qrCodeUrl;
            qrCodeContainer.querySelector('.preview-link').href = data.generatedUrl;

            // 添加下载功能
            const downloadLink = qrCodeContainer.querySelector('.download-link');
            downloadLink.onclick = () => {
                const a = document.createElement('a');
                a.href = data.qrCodeUrl;
                a.download = '挪车码.png';
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            };

            qrCodeContainer.style.display = 'block';
        } else {
            throw new Error(data.message);
        }
    } catch (error) {
        console.error('生成二维码失败:', error);
        alert('生成二维码失败,请稍后重试');
    }
}

// 添加小标题复选框的事件处理
document.addEventListener('DOMContentLoaded', function() {
    const enableSubtitleCheckbox = document.getElementById('enableSubtitle');
    const subtitleInput = document.getElementById('subtitle');
    const phoneInput = document.getElementById('phoneNumber');
    
    if (enableSubtitleCheckbox && subtitleInput) {
        enableSubtitleCheckbox.addEventListener('change', function() {
            subtitleInput.disabled = !this.checked;
            if (this.checked) {
                if (!subtitleInput.value.trim()) {
                    const phoneNumber = phoneInput.value.trim() || "16800000000";
                    subtitleInput.value = `扫码联系车主或拨打电话: ${phoneNumber}`;
                }
                subtitleInput.focus();
            }
        });
        
        // 当手机号码变化时,如果小标题已启用且使用的是默认格式,则更新小标题中的电话号码
        phoneInput.addEventListener('input', function() {
            if (enableSubtitleCheckbox.checked && subtitleInput.value.startsWith('扫码联系车主或拨打电话:')) {
                const phoneNumber = this.value.trim() || "16800000000";
                subtitleInput.value = `扫码联系车主或拨打电话: ${phoneNumber}`;
            }
        });
    }
});

在线版本的后端API实现与离线版类似,都是使用相同的核心二维码生成逻辑,但增加了文件上传处理、临时存储和清理等功能,这里不再细述,在线版源码戳这,挪车二维码接口定义如下:

[HttpPost("nuoche")]
[AllowAnonymous]
public async Task<IActionResult> NuoCheAsync([FromBody] NuoCheRequest request,
    [FromServices] IWebHostEnvironment env,
    [FromServices] IOptions<SiteOption> siteOption)
{
    try
    {
        if (string.IsNullOrWhiteSpace(request.Title) || string.IsNullOrWhiteSpace(request.PhoneNumber))
        {
            return BadRequest(new { success = false, message = "标题和手机号码不能为空" });
        }

        if (!long.TryParse(request.PhoneNumber, out long phoneNumberLong))
        {
            return BadRequest(new { success = false, message = "无效的手机号码" });
        }

        var encodedPhone = new Hashids("codewf").EncodeLong(phoneNumberLong);
        var generatedUrl = $"{siteOption.Value.Domain}/nuoche?p={encodedPhone}";

        var fileName = $"qrcode_{Guid.NewGuid():N}.png";
        var qrCodePath = Path.Combine(env.WebRootPath, IconFolder, fileName);

        Directory.CreateDirectory(Path.Combine(env.WebRootPath, IconFolder));

        QrCodeGenerator.GenerateQrCode(request.Title, generatedUrl, qrCodePath, request.SubTitle);

        var qrCodeUrl = $"/{IconFolder}/{fileName}";
        return Ok(new
        {
            success = true,
            qrCodeUrl,
            generatedUrl
        });
    }
    catch (Exception ex)
    {
        return BadRequest(new { success = false, message = ex.Message });
    }
}

V.機能比較と応用シナリオ

オフラインのデスクトップ版とオンラインのWeb版にはそれぞれ利点があり、以下はその特徴の比較です。

機能性は オフライン版。 オンライン版は
インストール要件 ダウンロードが必要です。 インストール不要、ブラウザアクセス
プラットフォームのサポート Windows、macOS、Linux 最新のブラウザ。
ネットワーク依存性 URLの初回生成のみネットワークが必要 完全にネットワーク依存
ファイルストレージ ローカル永続保存 サーバー一時保存
UIエクスペリエンス デスクトップアプリケーションのスタイル レスポンシブウェブデザイン
ドラッグのサポート 2つのドラッグをサポート ダイレクトドラッグのサポートなし
プライバシーの保護 高(ローカル処理) 中サーバ処理
小見出し機能。 カスタム小見出しをサポート カスタム小見出しをサポート

サブタイトル機能の追加は、二次元コードをより情報豊富にし、二次元コードの下に“コードコードコードをスキャンして所有者に連絡する”や連絡先の一部を表示するなどの追加のヒント情報を追加することができ、二次元コードの識別性と使いやすさを向上させます。

VI.概要と展望

この記事では、要件分析、コアコード実装、UI設計、マルチプラットフォーム展開など、2次元コード生成ツールの開発プロセスについて詳しく紹介します。このツールは、従来の移動連絡先情報に起因するプライバシー侵害や番号の悪用の問題を解決するだけでなく、最新のテクノロジーを通じて、より安全で便利な移動体験を提供します。

このツールは、次のシナリオで特に便利です。

  1. ** 臨時駐車場 **:団地、ショッピングモールなどに臨時駐車する場合の連絡先を提供します。
  2. ** 自動車ショーイベント **:車両展示時に連絡しやすいQRコードを配置
  3. ** 共有駐車場 **:専用駐車場での臨時連絡
  4. ** 緊急事態 **:特別な事情により車両の所有者に緊急連絡する必要がある場合

今後は、以下のようなツールをさらに改良する予定です。

  1. **多言語サポート **:英語、日本語などの多言語インターフェイスを追加し、国際ユーザーの利用に便利です。
  2. ** その他のカスタマイズオプション **:カスタムQRコードの色、スタイル、レイアウトをサポート
  3. ** 統計機能 **:スキャン回数統計を追加し、ユーザーがQRコードの使用状況を理解できるようにします。
  4. ** Enterprise Edition機能 **:フリート管理、駐車場などのエンタープライズユーザー向けのバッチ生成機能

ユーティリティとして、2次元コードジェネレータは現実世界の実用的な問題を解決すると同時に、最新の. NETテクノロジースタックを使用してクロスプラットフォームアプリケーションを構築する方法を示します。

この記事が役に立つことを願っています。質問があれば、コメント欄にコメントを残してください!

ソースコードReference

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2026/01/11

Avalonia ClipboardとDataGridの問題点

Avaloniaデスクトップソフトウェアの最近の開発で解決された2つの問題を文書化します:クリップボードのコピーのクラッシュ、タブの切り替えDataGridのキートン、原因の分析と解決策

继续阅读
同分类 / 同标签 2025/02/25

NET 10プレビュー 1リリース

今日. NET 10 Preview 1がリリースされ、私は初めてダウンロードし、Avalonia UIプロジェクトとブログサイトをアップグレードし、前者の機能テストとAOTリリースは正常で、後者のデバッグは正常ですが、Dockerは一時的に失敗しました。

继续阅读