Primary APIs:
RefreshContainer (RefreshRequested, PullDirection, RequestRefresh(), Visualizer)RefreshVisualizer (RefreshRequested, RefreshVisualizerState, Orientation, RequestRefresh())RefreshRequestedEventArgs.GetDeferral() and RefreshCompletionDeferral.Complete()ScrollViewer, ItemsControl, BorderReference docs:
12-html-css-animations-transitions-and-motion-system.md06-html-rich-content-lists-cards-tables-and-virtualization.md03-reactive-threading.md| HTML/CSS idiom | Avalonia mapping |
|---|---|
| touch pull-to-refresh gesture | RefreshContainer PullDirection + RefreshRequested |
| refresh spinner state machine | RefreshVisualizerState |
| explicit refresh button/JS call | RequestRefresh() |
| async refresh completion callback | GetDeferral() / Complete() |
HTML/JS baseline:
<section class="feed" id="feed"></section>
<button id="refresh">Refresh</button>
.feed {
overflow-y: auto;
min-height: 60vh;
}
const feed = document.getElementById("feed");
const refresh = document.getElementById("refresh");
async function reloadFeed() {
// fetch latest feed
}
refresh?.addEventListener("click", async () => {
await reloadFeed();
});
Avalonia pattern:
<RefreshContainer PullDirection="TopToBottom"
RefreshRequested="OnFeedRefreshRequested">
<ScrollViewer>
<ItemsControl ItemsSource="{CompiledBinding FeedItems}" />
</ScrollViewer>
</RefreshContainer>
<section class="activity-feed">
<article class="item">Build #102 completed</article>
<article class="item">Deployment started</article>
<article class="item">Smoke tests passed</article>
</section>
.activity-feed {
display: grid;
gap: .5rem;
}
.activity-feed .item {
border: 1px solid #2a3348;
border-radius: .5rem;
padding: .6rem .75rem;
}
<RefreshContainer PullDirection="TopToBottom"
RefreshRequested="OnFeedRefreshRequested">
<ScrollViewer>
<ItemsControl ItemsSource="{CompiledBinding FeedItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#2A3348"
BorderThickness="1"
CornerRadius="8"
Padding="10,8"
Margin="0,0,0,8">
<TextBlock Text="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</RefreshContainer>
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
var feedItems = new[]
{
"Build #102 completed",
"Deployment started",
"Smoke tests passed"
};
var itemsControl = new ItemsControl { ItemsSource = feedItems };
var refreshContainer = new RefreshContainer
{
PullDirection = PullDirection.TopToBottom,
Content = new ScrollViewer { Content = itemsControl }
};
refreshContainer.RefreshRequested += async (_, e) =>
{
var deferral = e.GetDeferral();
try
{
await Task.Delay(250);
// Reload feed items from service and update bound collection.
}
finally
{
deferral.Complete();
}
};
// Programmatic equivalent of a "Refresh" button in web UI.
refreshContainer.RequestRefresh();
finally blocks to avoid stuck refreshing state.Dispatcher.UIThread when refresh work runs off-thread.RefreshContainer wraps the scrolling surface.GetDeferral() result is always completed.