# AtomUI 6.0.8 API Change Examples This document supplements the main `6.0.8` release notes with the most important API changes and migration examples from `6.0.7`. For the authoritative release entries, see the repository root `CHANGELOG.md` and `CHANGELOG.zh-CN.md`. Chinese version: [6.0.8 API 变更示例](6.0.8-api-changes.zh-CN.md). ## ImagePreviewer Image Source Migration `6.0.8` unifies ImagePreviewer image sources around `IImagePreviewSource`: - `SourceUri` -> `Source` - `SourceUris` -> `Sources` - `FallbackSourceUri` -> `FallbackSource` - Use `UriImagePreviewSource` for URI, local file, and Avalonia resource images. - Use `StreamImagePreviewSource` for database, authenticated network, encrypted file, memory, and other lazy stream sources. ### 6.0.7 ```xml ``` ### 6.0.8 ```csharp using AtomUI.Desktop.Controls; public IImagePreviewSource PreviewSource { get; } = new UriImagePreviewSource("https://example.com/photo.jpg"); public IImagePreviewSource PreviewFallbackSource { get; } = new UriImagePreviewSource("avares://Demo/Assets/fallback.png"); ``` ```xml ``` ### Multiple Images, Cover, and Preloading `CoverIndex` only controls which image is shown as the closed-state cover. It does not change `CurrentIndex`. ```csharp public IList PreviewImages { get; } = [ new UriImagePreviewSource("https://example.com/photo-1.jpg"), new UriImagePreviewSource("https://example.com/photo-2.jpg"), new UriImagePreviewSource("https://example.com/photo-3.jpg") ]; public int CurrentPreviewIndex { get; set; } ``` ```xml ``` ### Lazy Stream Sources `OpenReadAsync` should return a new readable stream each time it is called. ImagePreviewer owns and disposes the returned stream. ```csharp public IImagePreviewSource SecurePreviewSource { get; } = new StreamImagePreviewSource( openReadAsync: cancellationToken => { Stream stream = File.OpenRead("/secure/photos/invoice.png"); return new ValueTask(stream); }, displayName: "invoice.png", contentType: "image/png", identity: "/secure/photos/invoice.png"); ``` ## Upload Composable Trigger Migration In `6.0.8`, the root Upload control owns file state, the queue, Form values, and lifecycle. File selection, directory selection, and drop input are handled by composable controls. ### 6.0.7 ```xml ``` ### 6.0.8 ```csharp using AtomUI.Desktop.Controls; using System.Collections.ObjectModel; public ObservableCollection Attachments { get; } = []; public IFileUploadTransport UploadTransport { get; } = new MyUploadTransport(); ``` ```xml ``` ### Drag-and-Drop Upload ```xml ``` ### Form Value Projection `FileValueMode` controls the value Upload writes into Form: ```xml ``` ## New OtpLineEdit Control `OtpLineEdit` uses one `Text` value for the complete verification code. Cells, separators, and masking only affect visuals. ```xml ``` ```xml ``` ```csharp public string? Code { get; set; } ``` ## Form and DataValidationErrors In `6.0.8`, errors produced by Form validators are written to Avalonia `DataValidationErrors`. If a ViewModel validator, binding validator, or application validation layer already provides a native validation error, AtomUI controls display that native error with highest priority. The default trigger is `OnChanged`. Configure `OnBlur` explicitly when validation should run after focus leaves the field: ```xml ``` ## Default Two-Way Bindings Several selection and value properties now use `BindingMode.TwoWay` by default. Common scenarios no longer need explicit `Mode=TwoWay`: ```xml ``` If application logic needs a read-only projection, set `Mode=OneWay` explicitly on the binding. ## DataGrid Filter Model Column filters now live directly on column APIs, and both the filter item source and selected values are bindable: ```xml ``` Tree filters use `FilterChildrenMemberPath`: ```xml ``` ## Select, ComboBox, and Cascader Overflow Tips Enable overflow tooltips when selected content cannot be fully displayed: ```xml ``` ## TabControl and TabStrip Reordering ```xml Content 1 Content 2 Content 3 ``` `TabActivationTrigger="PointerReleased"` is the default and fits tab reordering scenarios. Use `PointerPressed` when tabs should activate as soon as the pointer is pressed. ## Dialog Async Close Validation ```csharp await Dialog.ShowDialogAsync( new TextBlock { Text = "Close without saving?" }, options: new DialogOptions { Title = "Unsaved changes", BeforeCloseAsync = async context => { if (context.Reason == DialogCloseReason.Rejected) { return true; } return await ConfirmSaveAsync(); } }); ```