Cómo Configurar la Corrección de Errores en C# | IronQR
Migrating from ZXing.Net.MAUI to IronBarcode
Esta guía proporciona una ruta de migración completa desde ZXing .NET.MAUI a IronBarcode para desarrolladores de .NET MAUI . Abarca los motivos por los que los equipos emprenden esta migración, una comparación de características para fundamentar la decisión, los pasos mecánicos para reemplazar el paquete y actualizar los archivos del proyecto, ejemplos de código antes y después para cada patrón de uso principal, una referencia de traducción de API, una sección de solución de problemas para los problemas que surjan durante la transición, una lista de verificación de migración para realizar un seguimiento del progreso y un resumen de los resultados que ofrece la migración.
¿Por qué migrar desde ZXing .NET.MAUI?
La decisión de migrar de ZXing .NET.MAUI suele estar motivada por una o más condiciones concretas del proyecto. No se trata de preferencias estilísticas, sino de casos en los que la arquitectura o el estado de mantenimiento de la biblioteca impiden que se cumpla un requisito.
MAUI para Windows no es compatible: ZXing .NET.MAUI no tiene una implementación de cámara para Windows ni planes públicos para desarrollar una. La biblioteca está construida en torno a las API de cámara de las plataformas iOS y Android. Si un proyecto MAUI agrega un destino de Windows después de la compilación inicial (un patrón común en equipos que comienzan con un enfoque móvil primero), ZXing .NET.MAUI no puede servir para ese destino en absoluto. No hay ningún fragmento de código, ninguna alternativa ni solución provisional.
Problema de autoenfoque del iPhone 15 Pro: El rastreador de problemas de GitHub para Redth/ZXing.Net.Maui documenta que los dispositivos iPhone 15 Pro y Pro Max (iPhone16,1 y iPhone16,2) no logran un enfoque confiable para la detección de códigos de barras al utilizar CameraBarcodeReaderView. El código de barras es visible en el encuadre de la cámara, pero el sistema de enfoque automático no enfoca con la suficiente nitidez como para que el decodificador pueda extraer un resultado. La única solución documentada consiste en indicar al usuario que ajuste manualmente la distancia entre el dispositivo y el código de barras, una instrucción que requiere una interfaz de usuario visible y paciencia por parte del usuario, y que no es un resultado de producción aceptable para un flujo de trabajo principal.
Fuga de recursos de la cámara: CameraBarcodeReaderView no implementa IDisposable. Cuando el usuario abandona una página de escaneo, los recursos de la cámara no se liberan mediante un patrón de eliminación estándar. La solución documentada es configurar IsDetecting = false en OnDisappearing(), lo que reduce el impacto pero no libera formalmente la cámara. Las aplicaciones que navegan con frecuencia hacia y desde las páginas de escaneo acumulan un consumo de recursos que puede manifestarse como un aumento del uso de memoria, un agotamiento de la batería y fallos intermitentes en la inicialización de la cámara al regresar a la página de escaneo.
Fallo silencioso en la especificación de formato: ZXing .NET.MAUI hereda el requisito de ZXing.Net de declarar cada formato de código de barras que se va a escanear antes de que comience el escaneo. Los formatos omitidos de BarcodeReaderOptions.Formats son ignorados silenciosamente incluso cuando son claramente visibles en el marco de la cámara. Un usuario que apunta el dispositivo a un formato de código de barras que el desarrollador no previó no ve ningún error; la aplicación simplemente no detecta nada. En entornos donde los formatos de códigos de barras están controlados por proveedores externos, clientes o sistemas de terceros, este fallo silencioso se convierte en un problema de soporte persistente.
Estabilidad Pre-1.0: ZXing.Net.MAUI se publica en la versión v0.7.4 — una versión estable en NuGet, pero aún pre-1.0 según el versionado semántico. Los cambios de API de versión menor siguen siendo posibles antes de 1.0, la cadencia de corrección de errores depende de la disponibilidad de mantenedores de la comunidad y no hay un SLA de soporte comercial. Para aplicaciones empresariales sujetas a auditorías de dependencias o análisis de composición de software, una biblioteca comunitaria pre-1.0 sin soporte comercial puede no superar el proceso de aprobación.
El problema fundamental
El problema estructural es que CameraBarcodeReaderView bloquea al desarrollador en un bucle de eventos centrado en la cámara con gestión manual del ciclo de vida y una lista de formatos fija. Para cualquier cosa más allá de la exploración básica de cámaras en iOS y Android con formatos de códigos de barras conocidos, la arquitectura alcanza su límite:
// ZXing.Net.MAUI: event loop, format list, lifecycle boilerplate on every scan page
public partial class ScannerPage : ContentPage
{
public BarcodeReaderOptions ReaderOptions { get; }
public ScannerPage()
{
InitializeComponent();
ReaderOptions = new BarcodeReaderOptions
{
Formats = BarcodeFormats.QRCode |
BarcodeFormats.Code128 |
BarcodeFormats.Ean13 |
BarcodeFormats.UpcA,
TryHarder = true,
AutoRotate = true
};
BindingContext = this;
}
private void OnBarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
foreach (var barcode in e.Results)
ResultLabel.Text = $"{barcode.Format}: {barcode.Value}";
});
CameraView.IsDetecting = false;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
CameraView.IsDetecting = false; // Required — no Dispose() available
}
protected override void OnAppearing()
{
base.OnAppearing();
CameraView.IsDetecting = true;
}
}
// ZXing.Net.MAUI: event loop, format list, lifecycle boilerplate on every scan page
public partial class ScannerPage : ContentPage
{
public BarcodeReaderOptions ReaderOptions { get; }
public ScannerPage()
{
InitializeComponent();
ReaderOptions = new BarcodeReaderOptions
{
Formats = BarcodeFormats.QRCode |
BarcodeFormats.Code128 |
BarcodeFormats.Ean13 |
BarcodeFormats.UpcA,
TryHarder = true,
AutoRotate = true
};
BindingContext = this;
}
private void OnBarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
foreach (var barcode in e.Results)
ResultLabel.Text = $"{barcode.Format}: {barcode.Value}";
});
CameraView.IsDetecting = false;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
CameraView.IsDetecting = false; // Required — no Dispose() available
}
protected override void OnAppearing()
{
base.OnAppearing();
CameraView.IsDetecting = true;
}
}
Imports ZXing.Net.MAUI
Public Partial Class ScannerPage
Inherits ContentPage
Public ReadOnly Property ReaderOptions As BarcodeReaderOptions
Public Sub New()
InitializeComponent()
ReaderOptions = New BarcodeReaderOptions With {
.Formats = BarcodeFormats.QRCode Or
BarcodeFormats.Code128 Or
BarcodeFormats.Ean13 Or
BarcodeFormats.UpcA,
.TryHarder = True,
.AutoRotate = True
}
BindingContext = Me
End Sub
Private Sub OnBarcodesDetected(sender As Object, e As BarcodeDetectionEventArgs)
MainThread.BeginInvokeOnMainThread(Sub()
For Each barcode In e.Results
ResultLabel.Text = $"{barcode.Format}: {barcode.Value}"
Next
End Sub)
CameraView.IsDetecting = False
End Sub
Protected Overrides Sub OnDisappearing()
MyBase.OnDisappearing()
CameraView.IsDetecting = False ' Required — no Dispose() available
End Sub
Protected Overrides Sub OnAppearing()
MyBase.OnAppearing()
CameraView.IsDetecting = True
End Sub
End Class
IronBarcode reemplaza el bucle de eventos con una única llamada asíncrona al pulsar un botón, elimina por completo la configuración de formato y suprime la gestión del ciclo de vida de la página:
// NuGet: dotnet add package IronBarcode
// IronBarcode: stateless, all platforms, auto-detection, no lifecycle boilerplate
using IronBarCode;
public partial class ScannerPage : ContentPage
{
public ScannerPage() => InitializeComponent();
private async void ScanButton_Clicked(object sender, EventArgs e)
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return;
using var stream = await photo.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var results = BarcodeReader.Read(ms.ToArray());
ResultLabel.Text = results.FirstOrDefault()?.Value ?? "No barcode found";
}
// No OnAppearing / OnDisappearing needed
}
// NuGet: dotnet add package IronBarcode
// IronBarcode: stateless, all platforms, auto-detection, no lifecycle boilerplate
using IronBarCode;
public partial class ScannerPage : ContentPage
{
public ScannerPage() => InitializeComponent();
private async void ScanButton_Clicked(object sender, EventArgs e)
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return;
using var stream = await photo.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var results = BarcodeReader.Read(ms.ToArray());
ResultLabel.Text = results.FirstOrDefault()?.Value ?? "No barcode found";
}
// No OnAppearing / OnDisappearing needed
}
Imports IronBarCode
Public Partial Class ScannerPage
Inherits ContentPage
Public Sub New()
InitializeComponent()
End Sub
Private Async Sub ScanButton_Clicked(sender As Object, e As EventArgs)
Dim photo = Await MediaPicker.CapturePhotoAsync()
If photo Is Nothing Then Return
Using stream = Await photo.OpenReadAsync()
Using ms As New MemoryStream()
Await stream.CopyToAsync(ms)
Dim results = BarcodeReader.Read(ms.ToArray())
ResultLabel.Text = If(results.FirstOrDefault()?.Value, "No barcode found")
End Using
End Using
End Sub
' No OnAppearing / OnDisappearing needed
End Class
Comparación de características entre IronBarcode y ZXing .NET.MAUI
| Característica | ZXing.Net.MAUI | IronBarcode |
|---|---|---|
| Estado de publicación | Estable, pre-1.0 (v0.7.4) | Lanzamiento comercial estable |
| iOS MAUI | Sí (el enfoque del iPhone 15 Pro está roto) | Sí |
| Android MAUI | Sí (problemas de compilación de la cámara 1.5.0) | Sí |
| MAUI de Windows | No soportado | Sí |
| macOS MAUI | No soportado | Sí |
| Servidor / ASP.NET Core | No | Sí |
| Visor de la cámara en directo | Sí | No (Interfaz de usuario del sistema MediaPicker) |
| Se requiere especificación de formato | Sí | No (detección automática, más de 50 formatos) |
| Gestión del ciclo de vida de la cámara | Manual (Detectando) | No procede |
| Implementación de Dispose() | No | No aplicable — apátrida |
| Enfoque automático del iPhone 15 Pro | Roto (documentado) | No procede |
| Extracción de códigos de barras PDF | No | Sí |
| Ruta de archivo ingresada | No (solo cámara) | Sí |
| Recuperación de códigos de barras dañados | Solo esfuérzate más | Sí (con tecnología de aprendizaje automático) |
| Generación de códigos de barras | Sí (a través de ZXing .NET) | Sí |
| Apoyo comercial | None | Sí |
| Licencia | MIT (gratuito) | Comercial |
Inicio rápido
Paso 1: Elimine ZXing .NET.Maui.Controls y limpie MauiProgram.cs
Elimine el paquete NuGet ZXing .NET .MAUI:
dotnet remove package ZXing.Net.Maui.Controls
dotnet remove package ZXing.Net.Maui.Controls
ZXing.Net.MAUI requiere una llamada de registro única en MauiProgram.cs. Elimine esta línea si está presente:
// Eliminar this line from MauiProgram.cs
builder.UseBarcodeReader();
// Eliminar this line from MauiProgram.cs
builder.UseBarcodeReader();
La importación de using ZXing.Net.Maui; que admite esta llamada también se puede eliminar de MauiProgram.cs.
Paso 2: Instalar IronBarcode
dotnet add package IronBarcode
dotnet add package IronBarcode
El tutorial del escáner de códigos de barras de .NET MAUI cubre la configuración completa del proyecto, incluidas las entradas de permisos de cámara de Info.plist para iOS y las declaraciones de permisos de AndroidManifest.xml para Android.
Paso 3: Actualizar los espacios de nombres e inicializar la licencia.
Elimine las importaciones del espacio de nombres .NET.MAUI de ZXing de todos los archivos:
// Eliminar these from every .cs file that imported them
using ZXing.Net.Maui;
using ZXing.Net.Maui.Controls;
// Eliminar these from every .cs file that imported them
using ZXing.Net.Maui;
using ZXing.Net.Maui.Controls;
Añada el espacio de nombres de IronBarcode e inicie la clave de licencia al inicio de la aplicación. La ubicación adecuada es MauiProgram.cs o App.xaml.cs, antes de realizar cualquier operación con códigos de barras:
using IronBarCode;
// In MauiProgram.cs CreateMauiApp() or App.xaml.cs constructor
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
using IronBarCode;
// In MauiProgram.cs CreateMauiApp() or App.xaml.cs constructor
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronBarCode
' In MauiProgram.vb CreateMauiApp() or App.xaml.vb constructor
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY"
Ejemplos de migración de código
Sustitución del control de cámara XAML y el código subyacente
El control XAML de CameraBarcodeReaderView y su código subyacente de soporte son el objetivo principal de la migración. Esta modificación elimina la vista de la cámara del diseño de la página y reemplaza el patrón de escaneo basado en eventos con una captura asíncrona activada por un botón.
Enfoque ZXing .NET.MAUI:
XAML:
<ContentPage xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls">
<StackLayout>
<zxing:CameraBarcodeReaderView
x:Name="CameraView"
Options="{Binding ReaderOptions}"
BarcodesDetected="OnBarcodesDetected"
VerticalOptions="FillAndExpand" />
<Label x:Name="ResultLabel" Text="Scanning..." />
</StackLayout>
</ContentPage>
<ContentPage xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls">
<StackLayout>
<zxing:CameraBarcodeReaderView
x:Name="CameraView"
Options="{Binding ReaderOptions}"
BarcodesDetected="OnBarcodesDetected"
VerticalOptions="FillAndExpand" />
<Label x:Name="ResultLabel" Text="Scanning..." />
</StackLayout>
</ContentPage>
Código subyacente:
using ZXing.Net.Maui;
using ZXing.Net.Maui.Controls;
public partial class ScannerPage : ContentPage
{
public BarcodeReaderOptions ReaderOptions { get; }
public ScannerPage()
{
InitializeComponent();
ReaderOptions = new BarcodeReaderOptions
{
Formats = BarcodeFormats.QRCode | BarcodeFormats.Code128 | BarcodeFormats.Ean13,
TryHarder = true,
AutoRotate = true
};
BindingContext = this;
}
private void OnBarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
foreach (var barcode in e.Results)
ResultLabel.Text = $"{barcode.Format}: {barcode.Value}";
});
CameraView.IsDetecting = false;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
CameraView.IsDetecting = false;
}
protected override void OnAppearing()
{
base.OnAppearing();
CameraView.IsDetecting = true;
}
}
using ZXing.Net.Maui;
using ZXing.Net.Maui.Controls;
public partial class ScannerPage : ContentPage
{
public BarcodeReaderOptions ReaderOptions { get; }
public ScannerPage()
{
InitializeComponent();
ReaderOptions = new BarcodeReaderOptions
{
Formats = BarcodeFormats.QRCode | BarcodeFormats.Code128 | BarcodeFormats.Ean13,
TryHarder = true,
AutoRotate = true
};
BindingContext = this;
}
private void OnBarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
foreach (var barcode in e.Results)
ResultLabel.Text = $"{barcode.Format}: {barcode.Value}";
});
CameraView.IsDetecting = false;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
CameraView.IsDetecting = false;
}
protected override void OnAppearing()
{
base.OnAppearing();
CameraView.IsDetecting = true;
}
}
Imports ZXing.Net.Maui
Imports ZXing.Net.Maui.Controls
Public Partial Class ScannerPage
Inherits ContentPage
Public ReadOnly Property ReaderOptions As BarcodeReaderOptions
Public Sub New()
InitializeComponent()
ReaderOptions = New BarcodeReaderOptions With {
.Formats = BarcodeFormats.QRCode Or BarcodeFormats.Code128 Or BarcodeFormats.Ean13,
.TryHarder = True,
.AutoRotate = True
}
BindingContext = Me
End Sub
Private Sub OnBarcodesDetected(sender As Object, e As BarcodeDetectionEventArgs)
MainThread.BeginInvokeOnMainThread(Sub()
For Each barcode In e.Results
ResultLabel.Text = $"{barcode.Format}: {barcode.Value}"
Next
End Sub)
CameraView.IsDetecting = False
End Sub
Protected Overrides Sub OnDisappearing()
MyBase.OnDisappearing()
CameraView.IsDetecting = False
End Sub
Protected Overrides Sub OnAppearing()
MyBase.OnAppearing()
CameraView.IsDetecting = True
End Sub
End Class
Enfoque de IronBarcode :
XAML:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
<StackLayout>
<Button Text="Scan Barcode" Clicked="ScanButton_Clicked" />
<Label x:Name="ResultLabel" Text="Tap to scan..." />
</StackLayout>
</ContentPage>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
<StackLayout>
<Button Text="Scan Barcode" Clicked="ScanButton_Clicked" />
<Label x:Name="ResultLabel" Text="Tap to scan..." />
</StackLayout>
</ContentPage>
Código subyacente:
// NuGet: dotnet add package IronBarcode
using IronBarCode;
public partial class ScannerPage : ContentPage
{
public ScannerPage() => InitializeComponent();
private async void ScanButton_Clicked(object sender, EventArgs e)
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return;
using var stream = await photo.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var results = BarcodeReader.Read(ms.ToArray());
var first = results.FirstOrDefault();
ResultLabel.Text = first != null
? $"{first.Format}: {first.Value}"
: "No barcode found";
}
// No OnAppearing or OnDisappearing required — no camera state exists between scans
}
// NuGet: dotnet add package IronBarcode
using IronBarCode;
public partial class ScannerPage : ContentPage
{
public ScannerPage() => InitializeComponent();
private async void ScanButton_Clicked(object sender, EventArgs e)
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return;
using var stream = await photo.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var results = BarcodeReader.Read(ms.ToArray());
var first = results.FirstOrDefault();
ResultLabel.Text = first != null
? $"{first.Format}: {first.Value}"
: "No barcode found";
}
// No OnAppearing or OnDisappearing required — no camera state exists between scans
}
Imports IronBarCode
Public Partial Class ScannerPage
Inherits ContentPage
Public Sub New()
InitializeComponent()
End Sub
Private Async Sub ScanButton_Clicked(sender As Object, e As EventArgs)
Dim photo = Await MediaPicker.CapturePhotoAsync()
If photo Is Nothing Then Return
Using stream = Await photo.OpenReadAsync()
Using ms = New MemoryStream()
Await stream.CopyToAsync(ms)
Dim results = BarcodeReader.Read(ms.ToArray())
Dim first = results.FirstOrDefault()
ResultLabel.Text = If(first IsNot Nothing, $"{first.Format}: {first.Value}", "No barcode found")
End Using
End Using
End Sub
' No OnAppearing or OnDisappearing required — no camera state exists between scans
End Class
El espacio de nombres XAML de zxing:, la lista de formatos de BarcodeReaderOptions, el contenedor de MainThread.BeginInvokeOnMainThread() y ambas anulaciones del ciclo de vida se eliminan por completo. La continuación del método async se ejecuta en el contexto de llamada, que es el hilo principal para un controlador de eventos de UI, por lo que no se necesita una gestión explícita de hilos.
Eliminación de la especificación de formato
Se eliminan todos los bloques de configuración de BarcodeReaderOptions dispersos a través de múltiples páginas o escenarios de escaneo en el código. IronBarcode realiza la detección automática de formatos en los más de 50 formatos compatibles sin necesidad de configuración previa.
Enfoque ZXing .NET.MAUI:
// ZXing.Net.MAUI: every anticipated format must be listed explicitly
// Formats not listed here will silently fail to detect
var readerOptions = new BarcodeReaderOptions
{
Formats = BarcodeFormats.QRCode |
BarcodeFormats.DataMatrix |
BarcodeFormats.Aztec |
BarcodeFormats.Pdf417 |
BarcodeFormats.Code128 |
BarcodeFormats.Code39 |
BarcodeFormats.Ean13 |
BarcodeFormats.UpcA |
BarcodeFormats.Codabar,
TryHarder = true
};
// ZXing.Net.MAUI: every anticipated format must be listed explicitly
// Formats not listed here will silently fail to detect
var readerOptions = new BarcodeReaderOptions
{
Formats = BarcodeFormats.QRCode |
BarcodeFormats.DataMatrix |
BarcodeFormats.Aztec |
BarcodeFormats.Pdf417 |
BarcodeFormats.Code128 |
BarcodeFormats.Code39 |
BarcodeFormats.Ean13 |
BarcodeFormats.UpcA |
BarcodeFormats.Codabar,
TryHarder = true
};
' ZXing.Net.MAUI: every anticipated format must be listed explicitly
' Formats not listed here will silently fail to detect
Dim readerOptions As New BarcodeReaderOptions With {
.Formats = BarcodeFormats.QRCode Or
BarcodeFormats.DataMatrix Or
BarcodeFormats.Aztec Or
BarcodeFormats.Pdf417 Or
BarcodeFormats.Code128 Or
BarcodeFormats.Code39 Or
BarcodeFormats.Ean13 Or
BarcodeFormats.UpcA Or
BarcodeFormats.Codabar,
.TryHarder = True
}
Enfoque de IronBarcode :
// IronBarcode: no format configuration needed
// All formats are detected automatically on every read call
var results = BarcodeReader.Read(imageBytes);
// Optional: restrict to specific formats for performance tuning (not required for correctness)
var options = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.QRCode | BarcodeEncoding.Code128
};
var tunedResults = BarcodeReader.Read(imageBytes, options);
// IronBarcode: no format configuration needed
// All formats are detected automatically on every read call
var results = BarcodeReader.Read(imageBytes);
// Optional: restrict to specific formats for performance tuning (not required for correctness)
var options = new BarcodeReaderOptions
{
ExpectBarcodeTypes = BarcodeEncoding.QRCode | BarcodeEncoding.Code128
};
var tunedResults = BarcodeReader.Read(imageBytes, options);
Imports IronBarcode
' IronBarcode: no format configuration needed
' All formats are detected automatically on every read call
Dim results = BarcodeReader.Read(imageBytes)
' Optional: restrict to specific formats for performance tuning (not required for correctness)
Dim options As New BarcodeReaderOptions With {
.ExpectBarcodeTypes = BarcodeEncoding.QRCode Or BarcodeEncoding.Code128
}
Dim tunedResults = BarcodeReader.Read(imageBytes, options)
IronBarcode ofrece sugerencias de formato para situaciones donde el rendimiento es un factor crítico, pero son opcionales. Un código de barras con un formato no incluido en el objeto de opciones se detectará y se devolverá igualmente.
Eliminación de la gestión del ciclo de vida de IsDetecting
Se eliminan todas las anulaciones de OnAppearing y OnDisappearing que existen para alternar CameraView.IsDetecting. Si esas anulaciones de método contienen otra lógica del ciclo de vida de la página, conserve esa lógica y elimine solo las líneas de IsDetecting.
Enfoque ZXing .NET.MAUI:
// Required boilerplate on every page — omitting this causes camera resource leaks
protected override void OnDisappearing()
{
base.OnDisappearing();
if (CameraView != null)
CameraView.IsDetecting = false;
}
protected override void OnAppearing()
{
base.OnAppearing();
if (CameraView != null)
CameraView.IsDetecting = true;
}
// Required boilerplate on every page — omitting this causes camera resource leaks
protected override void OnDisappearing()
{
base.OnDisappearing();
if (CameraView != null)
CameraView.IsDetecting = false;
}
protected override void OnAppearing()
{
base.OnAppearing();
if (CameraView != null)
CameraView.IsDetecting = true;
}
' Required boilerplate on every page — omitting this causes camera resource leaks
Protected Overrides Sub OnDisappearing()
MyBase.OnDisappearing()
If CameraView IsNot Nothing Then
CameraView.IsDetecting = False
End If
End Sub
Protected Overrides Sub OnAppearing()
MyBase.OnAppearing()
If CameraView IsNot Nothing Then
CameraView.IsDetecting = True
End If
End Sub
Enfoque de IronBarcode :
// Delete both methods if they contain only IsDetecting management.
// If they contain other logic, remove only the IsDetecting lines and keep the rest.
// IronBarcode is stateless — there is no camera view running between button taps.
// Delete both methods if they contain only IsDetecting management.
// If they contain other logic, remove only the IsDetecting lines and keep the rest.
// IronBarcode is stateless — there is no camera view running between button taps.
' Delete both methods if they contain only IsDetecting management.
' If they contain other logic, remove only the IsDetecting lines and keep the rest.
' IronBarcode is stateless — there is no camera view running between button taps.
Windows MAUI: El mismo código, sin compilación condicional.
Al usar ZXing .NET.MAUI, agregar un destino de Windows a un proyecto provocaba fallos de compilación o requería stubs específicos de la plataforma porque la implementación para Windows nunca se había escrito. Con IronBarcode, el mismo código que funciona en iOS y Android se compila y ejecuta en Windows sin modificaciones.
Enfoque ZXing .NET.MAUI:
// Windows MAUI: either fails to compile or requires a platform-specific stub
// There is no documented path to Windows support
#if ANDROID || IOS
// ZXing.Net.MAUI scanning — Windows has no implementation
#endif
// Windows MAUI: either fails to compile or requires a platform-specific stub
// There is no documented path to Windows support
#if ANDROID || IOS
// ZXing.Net.MAUI scanning — Windows has no implementation
#endif
Enfoque de IronBarcode :
// NuGet: dotnet add package IronBarcode
// No platform conditionals — same code runs on iOS, Android, Windows, and macOS
private async void ScanButton_Clicked(object sender, EventArgs e)
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return;
using var stream = await photo.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var results = BarcodeReader.Read(ms.ToArray());
ResultLabel.Text = results.FirstOrDefault()?.Value ?? "No barcode found";
}
// NuGet: dotnet add package IronBarcode
// No platform conditionals — same code runs on iOS, Android, Windows, and macOS
private async void ScanButton_Clicked(object sender, EventArgs e)
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return;
using var stream = await photo.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var results = BarcodeReader.Read(ms.ToArray());
ResultLabel.Text = results.FirstOrDefault()?.Value ?? "No barcode found";
}
Imports System.IO
Imports System.Linq
Imports IronBarCode
' NuGet: dotnet add package IronBarcode
' No platform conditionals — same code runs on iOS, Android, Windows, and macOS
Private Async Sub ScanButton_Clicked(sender As Object, e As EventArgs)
Dim photo = Await MediaPicker.CapturePhotoAsync()
If photo Is Nothing Then Return
Using stream = Await photo.OpenReadAsync()
Using ms As New MemoryStream()
Await stream.CopyToAsync(ms)
Dim results = BarcodeReader.Read(ms.ToArray())
ResultLabel.Text = If(results.FirstOrDefault()?.Value, "No barcode found")
End Using
End Using
End Sub
En Windows, MediaPicker.CapturePhotoAsync() se asigna al selector de archivos de Windows, lo que permite al usuario seleccionar un archivo de imagen, comportamiento adecuado para un entorno de escritorio. La guía de códigos de barras para escritorio de MAUI cubre en detalle la configuración de MAUI para Windows y macOS.
Lectura de códigos de barras PDF (Nueva funcionalidad)
ZXing .NET.MAUI no tiene una API para leer códigos de barras de documentos PDF. Si se trata de un nuevo requisito que permite la migración, se aplica el siguiente patrón:
Enfoque ZXing .NET.MAUI:
// ZXing.Net.MAUI: no API for PDF or file-based barcode reading
// Cannot fulfill this requirement — a separate library is required
// ZXing.Net.MAUI: no API for PDF or file-based barcode reading
// Cannot fulfill this requirement — a separate library is required
' ZXing.Net.MAUI: no API for PDF or file-based barcode reading
' Cannot fulfill this requirement — a separate library is required
Enfoque de IronBarcode :
// NuGet: dotnet add package IronBarcode
using IronBarCode;
// Read all barcodes from all pages of a PDF
var pdfResults = BarcodeReader.Read("invoice.pdf");
foreach (var barcode in pdfResults)
Console.WriteLine($"Page {barcode.PageNumber}: {barcode.Format} — {barcode.Value}");
// Read from a user-selected file using MAUI FilePicker
private async void ReadFileButton_Clicked(object sender, EventArgs e)
{
var file = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Select image or PDF"
});
if (file == null) return;
var fileResults = BarcodeReader.Read(file.FullPath);
foreach (var result in fileResults)
ResultLabel.Text += $"\n{result.Format}: {result.Value}";
}
// NuGet: dotnet add package IronBarcode
using IronBarCode;
// Read all barcodes from all pages of a PDF
var pdfResults = BarcodeReader.Read("invoice.pdf");
foreach (var barcode in pdfResults)
Console.WriteLine($"Page {barcode.PageNumber}: {barcode.Format} — {barcode.Value}");
// Read from a user-selected file using MAUI FilePicker
private async void ReadFileButton_Clicked(object sender, EventArgs e)
{
var file = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Select image or PDF"
});
if (file == null) return;
var fileResults = BarcodeReader.Read(file.FullPath);
foreach (var result in fileResults)
ResultLabel.Text += $"\n{result.Format}: {result.Value}";
}
Imports IronBarCode
' Read all barcodes from all pages of a PDF
Dim pdfResults = BarcodeReader.Read("invoice.pdf")
For Each barcode In pdfResults
Console.WriteLine($"Page {barcode.PageNumber}: {barcode.Format} — {barcode.Value}")
Next
' Read from a user-selected file using MAUI FilePicker
Private Async Sub ReadFileButton_Clicked(sender As Object, e As EventArgs)
Dim file = Await FilePicker.PickAsync(New PickOptions With {
.PickerTitle = "Select image or PDF"
})
If file Is Nothing Then Return
Dim fileResults = BarcodeReader.Read(file.FullPath)
For Each result In fileResults
ResultLabel.Text &= $"\n{result.Format}: {result.Value}"
Next
End Sub
El flujo de trabajo completo para la lectura de códigos de barras en PDF, que incluye documentos de varias páginas, metadatos de números de página y documentos de formato mixto, está documentado en la guía para leer códigos de barras en PDF .
Referencia de mapeo de la API .NET.MAUI de ZXing a IronBarcode
| ZXing.Net.MAUI | IronBarcode | Notas |
|---|---|---|
builder.UseBarcodeReader() |
No es necesario | Eliminar de MauiProgram.cs |
using ZXing.Net.Maui; |
using IronBarCode; |
Reemplazo de espacio de nombres |
using ZXing.Net.Maui.Controls; |
No es necesario | Eliminar |
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;..." |
No es necesario | Eliminar de XAML |
<zxing:CameraBarcodeReaderView> |
<Button> + MediaPicker.CapturePhotoAsync() |
Reemplazo arquitectónico |
Options="{Binding ReaderOptions}" |
No es necesario | Quitar la atadura |
BarcodesDetected="OnBarcodesDetected" |
valor de retorno de BarcodeReader.Read() |
Evento → retorno asíncrono |
new BarcodeReaderOptions { Formats = BarcodeFormats.X \| ... } |
No es necesario | La detección automática reemplaza las listas de formato. |
BarcodeDetectionEventArgs e |
IEnumerable<BarcodeResult> |
Entrega de resultados diferente |
e.Results |
Valor de retorno de BarcodeReader.Read() |
|
barcode.Value |
result.Value |
Mismo nombre de propiedad |
barcode.Format |
result.Format |
Mismo nombre de propiedad |
BarcodeFormats.QRCode |
BarcodeEncoding.QRCode |
Renombrar enumeración |
BarcodeFormats.Code128 |
BarcodeEncoding.Code128 |
Renombrar enumeración |
BarcodeFormats.Ean13 |
BarcodeEncoding.EAN13 |
Renombrar enumeración |
BarcodeFormats.UpcA |
BarcodeEncoding.UPCA |
Renombrar enumeración |
CameraView.IsDetecting = false |
No es necesario | Eliminar de OnDisappearing |
CameraView.IsDetecting = true |
No es necesario | Eliminar de OnAppearing |
| API sin entrada de archivos | BarcodeReader.Read("path/to/image.png") |
Nueva capacidad |
| No hay API de entrada de PDF | BarcodeReader.Read("document.pdf") |
Nueva capacidad |
| Solo para iOS y Android | iOS, Android, Windows, macOS, servidor | Expansión de la plataforma |
Problemas comunes de migración y soluciones
Problema 1: No hay equivalente al visor en directo
ZXing.Net.MAUI: La CameraBarcodeReaderView muestra una transmisión continua de cámara en la disposición de la página, mostrando al usuario una vista previa en vivo con retroalimentación de escaneo superpuesta.
Solución: IronBarcode no proporciona un control de visor en tiempo real. El patrón de reemplazo usa MediaPicker.CapturePhotoAsync(), que abre la interfaz de la cámara del sistema de la plataforma. La cámara del sistema proporciona su propia vista previa en tiempo real e indicador de enfoque. Después de que el usuario capture la imagen y confirme, el resultado se pasa a BarcodeReader.Read(). Si un visor en la aplicación persistente es un elemento UX necesario que no puede ser reemplazado por la interfaz de la cámara del sistema, la capa de integración de la cámara debe construirse por separado utilizando Microsoft.Maui.Media o APIs de cámara de la plataforma, con IronBarcode manejando el paso de decodificación.
Número 2: Declaraciones de autorización para el uso de la cámara
ZXing .NET.MAUI: Es posible que se hayan declarado permisos de cámara en el proyecto como parte de las instrucciones de configuración de ZXing .NET.MAUI.
Solución: Los permisos de cámara siguen siendo necesarios para la llamada MediaPicker.CapturePhotoAsync() que utiliza el patrón MAUI de IronBarcode. Verifique que NSCameraUsageDescription esté presente en Info.plist para iOS y que <uses-permission android:name="android.permission.CAMERA" /> esté presente en AndroidManifest.xml para Android. IronBarcode en sí mismo no accede directamente a la cámara, procesa imágenes, pero la llamada MediaPicker que proporciona imágenes requiere permiso de cámara. La configuración de permisos se explica en el tutorial del escáner de códigos de barras .NET MAUI .
Problema 3: La compilación de Windows ahora tiene éxito donde antes fallaba.
ZXing .NET.MAUI: Los proyectos que intentaban incluir un destino de Windows con ZXing .NET.MAUI normalmente encontraban errores de compilación o requerían que la biblioteca se excluyera de la compilación de Windows mediante lógica condicional de MSBuild.
Solución: Tras desinstalar ZXing .NET.MAUI e instalar IronBarcode, la compilación de MAUI para Windows se realiza correctamente sin ninguna condición de plataforma. Eliminar cualquier #if ANDROID || Los mecanismos de protección de IOS que se colocaron alrededor de las llamadas a .NET.MAUI de ZXing para excluirlas de la compilación de Windows. La llamada de IronBarcode BarcodeReader.Read() se compila y ejecuta en todos los marcos de destino. Si MediaPicker.CapturePhotoAsync() fue excluido de las compilaciones de Windows, esa exclusión también puede eliminarse: el método es compatible con MAUI de Windowsy se asigna al selector de archivos. Verifique que la solución completa se compile correctamente para todos los marcos de trabajo de destino después de eliminar las condiciones.
Problema 4: Referencias de enumeración de BarcodeFormats
ZXing.Net.MAUI: El enum BarcodeFormats de ZXing.Net.Maui se utiliza extensamente en las configuraciones de BarcodeReaderOptions. Una vez eliminado el paquete, cualquier referencia restante produce errores de compilación.
Solución: Elimine todos los bloques de inicialización de BarcodeReaderOptions que se usaron para configurar listas de formatos. IronBarcode no requiere especificación de formato para su correcto funcionamiento. Si algún código restante hace referencia a valores BarcodeFormats para registro, visualización o propósitos de comparación, reemplácelos con valores BarcodeEncoding del espacio de nombres IronBarCode. Ejecute grep -rn "BarcodeFormats\." --include="*.cs" . para encontrar todas las referencias restantes después de la eliminación del paquete.
Lista de verificación de migración de ZXing .NET.MAUI
Tareas previas a la migración
Antes de realizar cambios, audite todo el uso de ZXing .NET.MAUI en el código fuente:
grep -rn "ZXing.Net.Maui" --include="*.cs" --include="*.xaml" .
grep -rn "CameraBarcodeReaderView" --include="*.cs" --include="*.xaml" .
grep -rn "BarcodeDetectionEventArgs" --include="*.cs" .
grep -rn "BarcodeReaderOptions" --include="*.cs" .
grep -rn "BarcodeFormats\." --include="*.cs" .
grep -rn "IsDetecting" --include="*.cs" .
grep -rn "UseBarcodeReader" --include="*.cs" .
grep -rn "zxing:" --include="*.xaml" .
grep -rn "e\.Results" --include="*.cs" .
grep -rn "ZXing.Net.Maui" --include="*.cs" --include="*.xaml" .
grep -rn "CameraBarcodeReaderView" --include="*.cs" --include="*.xaml" .
grep -rn "BarcodeDetectionEventArgs" --include="*.cs" .
grep -rn "BarcodeReaderOptions" --include="*.cs" .
grep -rn "BarcodeFormats\." --include="*.cs" .
grep -rn "IsDetecting" --include="*.cs" .
grep -rn "UseBarcodeReader" --include="*.cs" .
grep -rn "zxing:" --include="*.xaml" .
grep -rn "e\.Results" --include="*.cs" .
Documente todas las páginas escaneadas identificadas en la auditoría. Tome nota de qué anulaciones OnAppearing y OnDisappearing contienen solo manejo de IsDetecting (para ser eliminado) frente a aquellas que contienen otra lógica (para ser modificadas parcialmente). Tome nota de cualquier instancia de BarcodeReaderOptions que pueda contener listas de formatos utilizadas más allá de la configuración de detección.
Tareas de actualización de código
- Eliminar el paquete NuGet
ZXing.Net.Maui.Controlsdel archivo del proyecto - Eliminar
builder.UseBarcodeReader()deMauiProgram.cs - Eliminar las importaciones de los espacios de nombres
using ZXing.Net.Maui;yusing ZXing.Net.Maui.Controls;de todos los archivos - Instalar el paquete NuGet
IronBarcode - Añadir
using IronBarCode;a todos los archivos que utilizarán lectura de códigos de barras - Añadir
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";al inicio de la aplicación - Eliminar la declaración del espacio de nombres
xmlns:zxingde todos los archivos XAML - Eliminar todos los elementos
<zxing:CameraBarcodeReaderView>de los archivos XAML - Reemplazar la vista de cámara eliminada con un control
<Button>en cada archivo XAML - Añadir
Clicked="ScanButton_Clicked"a cada botón de escaneo - Eliminar los métodos de los manejadores de eventos
OnBarcodesDetectedde todos los archivos subyacentes - Añadir métodos
async void ScanButton_Clickedque implementenMediaPicker.CapturePhotoAsync()+BarcodeReader.Read()a cada página - Eliminar todos los bloques de inicialización de
BarcodeReaderOptions - Eliminar todas las anulaciones de
OnDisappearingyOnAppearingque existen únicamente para el manejo deIsDetecting - Eliminar las líneas de
IsDetectingde cualquier anulaciones deOnDisappearingyOnAppearingque contengan otra lógica - Elimine cualquier
#if ANDROID|| Las protecciones de compilación condicional de IOS que aislaban ZXing .NET.MAUI de las compilaciones de Windows - Reemplazar cualquier referencia de
BarcodeFormats.Xenum restante con equivalentes deBarcodeEncoding.X
Pruebas posteriores a la migración
Una vez que la migración se haya compilado correctamente, verifique lo siguiente:
- Android MAUI: el botón de escaneo abre la cámara del sistema, toma una foto y devuelve un resultado de código de barras correcto; confírmelo con la guía de escaneo de Android.
- iOS MAUI: el mismo flujo funciona en iOS, incluso en el hardware del iPhone 15 Pro donde anteriormente se produjo el problema del enfoque automático.
- Windows MAUI: la compilación para Windows se realiza sin errores y el botón de escaneo abre el selector de archivos y devuelve un resultado correcto a partir de la imagen seleccionada.
- Formatos: pruebe el escaneo de códigos de barras en formatos que no estaban incluidos en la lista anterior de
BarcodeFormatspara confirmar la detección automática - Navegación de página: navegue hacia y desde la página de escaneo varias veces y verifique que no se produzcan aumentos de memoria ni fallos de inicialización de la cámara.
- Lectura de PDF: si la migración agrega la lectura de códigos de barras de PDF como una nueva capacidad, verifique que los PDF de varias páginas devuelvan resultados con metadatos de número de página correctos.
Principales ventajas de migrar a IronBarcode
Cobertura de plataforma ampliada: tras la migración, la aplicación es compatible con Windows y macOS MAUI, además de iOS y Android, todo ello desde el mismo paquete y con el mismo patrón de escaneo. Los proyectos que anteriormente requerían módulos específicos de la plataforma o excluían a Windows de la funcionalidad de códigos de barras ahora tienen una cobertura completa sin necesidad de código adicional.
Fiabilidad del Hardware de Última Generación: El enfoque de captura de imágenes a través de MediaPicker y BarcodeReader.Read() no se ve afectado por el modelo de autoenfoque CameraBarcodeReaderView que falla en el hardware del iPhone 15 Pro y Pro Max. La cámara del sistema gestiona el enfoque de forma independiente, e IronBarcode procesa la imagen capturada después de que el usuario confirme la toma.
Eliminación de la Gestión de Recursos de Cámara: Eliminar CameraBarcodeReaderView elimina toda la categoría de errores de fuga de recursos de cámara. No hay estado de IsDetecting que rastrear, no hay código repetitivo de OnAppearing y OnDisappearing que mantener en cada página de escaneo, y no hay acumulación de recursos de cámara a través de ciclos de navegación. La API sin estado hace que las páginas de escaneo sean indistinguibles de cualquier otra página en términos del ciclo de vida de los recursos.
Cobertura de formatos sin configuración: Cualquier formato de código de barras que se encuentre en el campo se detecta automáticamente. Se eliminan los fallos de escaneo causados por entradas faltantes en una lista de BarcodeFormats. Las solicitudes de asistencia de los usuarios cuyos códigos de barras fueron ignorados silenciosamente porque un proveedor cambió el formato de sus etiquetas cesan.
Procesamiento de archivos y documentos: La migración permite leer códigos de barras de documentos PDF, archivos de imagen y flujos de bytes sin necesidad de bibliotecas adicionales. Los flujos de trabajo que anteriormente estaban fuera del alcance de ZXing.Net.MAUI, como leer códigos de barras de facturas subidas, procesar boletos digitales, escanear por lotes directorios de imágenes, están disponibles a través de la misma llamada BarcodeReader.Read() utilizada para capturas de cámara.
Estabilidad de nivel profesional: IronBarcode se distribuye como una versión comercial estable con un ritmo de desarrollo activo, soporte comercial y actualizaciones periódicas que siguen el ritmo de los lanzamientos de versiones de .NET . Las auditorías de dependencias, el análisis de la composición del software y los procesos de aprobación Enterprise se encuentran con una biblioteca compatible que cuenta con un compromiso de mantenimiento documentado, en lugar de un paquete de prelanzamiento de la comunidad.
Preguntas Frecuentes
¿Por qué debería migrar de ZXing.Net.MAUI a IronBarcode?
Entre las razones más comunes se incluyen la simplificación de licencias (eliminando la complejidad de SDK + clave de tiempo de ejecución), la eliminación de límites de rendimiento, la obtención de compatibilidad nativa con PDF, la mejora de la implementación de Docker/CI/CD y la reducción de la repetición de API en el código de producción.
¿Cómo puedo sustituir las llamadas a la API de ZXing.Net.MAUI por IronBarcode?
Sustituya la creación de instancias y la repetición de licencias por IronBarCode.License.LicenseKey = "key". Sustituya las llamadas al lector por BarcodeReader.Read(path) y las llamadas al escritor por BarcodeWriter.CreateBarcode(data, encoding). Los métodos estáticos no requieren gestión de instancias.
¿Cuánto código cambia al migrar de ZXing.Net.MAUI a IronBarcode?
La mayoría de las migraciones se traducen en menos líneas de código. Se eliminan la repetición de licencias, los constructores de instancias y la configuración explícita de formatos. Las operaciones básicas de lectura/escritura se traducen a equivalentes IronBarcode más cortos con objetos de resultado más limpios.
¿Necesito tener instalados tanto ZXing.Net.MAUI como IronBarcode durante la migración?
No. La mayoría de las migraciones son sustituciones directas en lugar de operaciones paralelas. Migre una clase de servicio cada vez, sustituya la referencia NuGet y actualice los patrones de instanciación y llamada a la API antes de pasar a la siguiente clase.
¿Cuál es el nombre del paquete NuGet para IronBarcode?
El paquete es 'IronBarCode' (con B y C mayúsculas). Instálalo con 'Install-Package IronBarCode' o 'dotnet add package IronBarCode'. La directiva using en el código es 'using IronBarCode;'.
¿Cómo simplifica IronBarcode el despliegue de Docker en comparación con ZXing.Net.MAUI?
IronBarcode es un paquete NuGet sin archivos SDK externos ni configuración de licencia montada. En Docker, establezca la variable de entorno IRONBARCODE_LICENSE_KEY y el paquete se encargará de la validación de la licencia al inicio.
¿IronBarcode detecta automáticamente todos los formatos de código de barras tras migrar desde ZXing.Net.MAUI?
Sí, IronBarcode detecta automáticamente la simbología de todos los formatos compatibles. No es necesaria la enumeración explícita de BarcodeTypes. Si ya se conoce el formato y el rendimiento es importante, BarcodeReaderOptions permite restringir el espacio de búsqueda como optimización.
¿Puede IronBarcode leer códigos de barras de PDF sin una biblioteca independiente?
Sí. BarcodeReader.Read("document.pdf") procesa archivos PDF de forma nativa. Los resultados incluyen PageNumber, Format, Value y Confidence para cada código de barras encontrado. No es necesario ningún paso externo de procesamiento de PDF.
¿Cómo gestiona IronBarcode el procesamiento paralelo de códigos de barras?
Los métodos estáticos de IronBarcode no tienen estado y son seguros para los hilos. Utilice Parallel.ForEach directamente sobre listas de archivos sin gestión de instancias por subproceso. BarcodeReaderOptions.MaxParallelThreads controla el presupuesto interno de hilos.
¿Qué propiedades de resultado cambian al migrar de ZXing.Net.MAUI a IronBarcode?
Cambios de nombre comunes: BarcodeValue se convierte en Value, BarcodeType se convierte en Format. Los resultados de IronBarcode también añaden Confidence y PageNumber. Una búsqueda y sustitución en toda la solución gestiona los cambios de nombre en el código de procesamiento de resultados existente.
¿Cómo configuro las licencias de IronBarcode en un proceso CI/CD?
Almacene IRONBARCODE_LICENSE_KEY como un secreto de canalización y asigne IronBarCode.License.LicenseKey en el código de inicio de la aplicación. Un secreto cubre todos los entornos, incluidos los de desarrollo, prueba, montaje y producción.
¿Admite IronBarcode la generación de códigos QR con estilos personalizados?
Sí. QRCodeWriter.CreateQrCode() admite colores personalizados mediante ChangeBarCodeColor(), incrustación de logotipos mediante AddBrandLogo(), niveles de corrección de errores configurables y múltiples formatos de salida, incluidos PNG, JPG, PDF y stream.

