├── Icon.png ├── opensource.snk ├── docs └── assets.pptx ├── tests ├── ObservableCollections.R3.Tests │ ├── _GlobalUsings.cs │ ├── ObservableCollections.R3.Tests.csproj │ └── ObservableCollectionExtensionsTest.cs └── ObservableCollections.Tests │ ├── _GlobalUsings.cs │ ├── ObservableCollections.Tests.csproj │ ├── ObservableDictionaryTest.cs │ ├── ObservableStackTest.cs │ ├── ToNotifyCollectionChangedTest.cs │ ├── ObservableHashSetTest.cs │ ├── ObservableRingBufferTest.cs │ ├── AlternateIndexListTest.cs │ ├── ViewContainer.cs │ ├── ObservableQueueTest.cs │ └── RingBufferTest.cs ├── sandbox ├── BlazorApp │ ├── wwwroot │ │ ├── favicon.ico │ │ └── css │ │ │ ├── open-iconic │ │ │ ├── font │ │ │ │ ├── fonts │ │ │ │ │ ├── open-iconic.eot │ │ │ │ │ ├── open-iconic.otf │ │ │ │ │ ├── open-iconic.ttf │ │ │ │ │ └── open-iconic.woff │ │ │ │ └── css │ │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── FONT-LICENSE │ │ │ └── site.css │ ├── Pages │ │ ├── Index.razor │ │ ├── Counter.razor │ │ ├── Index.razor.cs │ │ ├── Error.cshtml.cs │ │ ├── _Host.cshtml │ │ ├── FetchData.razor │ │ └── Error.cshtml │ ├── appsettings.json │ ├── appsettings.Development.json │ ├── BlazorApp.csproj │ ├── Data │ │ ├── WeatherForecast.cs │ │ └── WeatherForecastService.cs │ ├── _Imports.razor │ ├── Shared │ │ ├── MainLayout.razor │ │ ├── SurveyPrompt.razor │ │ ├── NavMenu.razor.css │ │ ├── NavMenu.razor │ │ └── MainLayout.razor.css │ ├── App.razor │ ├── Properties │ │ └── launchSettings.json │ ├── Program.cs │ └── Startup.cs ├── WinUI3App │ ├── Properties │ │ └── launchSettings.json │ ├── MainWindow.xaml.cs │ ├── MainPage.xaml.cs │ ├── App.xaml.cs │ ├── MainPageViewModel.cs │ ├── App.xaml │ ├── MainWindow.xaml │ ├── app.manifest │ ├── MainPage.xaml │ ├── Package.appxmanifest │ ├── SampleService.cs │ └── WinUI3App.csproj ├── WpfApp │ ├── App.xaml │ ├── App.xaml.cs │ ├── AssemblyInfo.cs │ ├── WpfApp.csproj │ └── MainWindow.xaml ├── AvaloniaApp │ ├── App.axaml │ ├── App.axaml.cs │ ├── Program.cs │ ├── app.manifest │ ├── AvaloniaApp.csproj │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs └── ConsoleApp │ ├── ConsoleApp.csproj │ └── Program.cs ├── .github ├── workflows │ ├── prevent-github-change.yaml │ ├── stale.yaml │ ├── build-debug.yaml │ └── build-release.yaml └── dependabot.yaml ├── src ├── ObservableCollections.R3 │ ├── Shims.cs │ └── ObservableCollections.R3.csproj └── ObservableCollections │ ├── Shims │ ├── CompilerServices.cs │ ├── Nullables.cs │ ├── CollectionsMarshalEx.cs │ └── Collections.cs │ ├── ObservableCollections.csproj │ ├── ISynchronizedViewFilter.cs │ ├── Internal │ ├── FixedArray.cs │ ├── ResizableArray.cs │ └── CloneCollection.cs │ ├── ICollectionEventDispatcher.cs │ ├── ObservableStack.cs │ ├── ObservableQueue.cs │ ├── ObservableRingBuffer.cs │ ├── NotifyCollectionChangedEventArgs.cs │ ├── AlternateIndexList.cs │ ├── ObservableDictionary.cs │ ├── ObservableHashSet.cs │ └── ObservableList.OptimizeView.cs ├── LICENSE ├── Directory.Build.props ├── .editorconfig ├── .gitignore └── ObservableCollections.sln /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/Icon.png -------------------------------------------------------------------------------- /opensource.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/opensource.snk -------------------------------------------------------------------------------- /docs/assets.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/docs/assets.pptx -------------------------------------------------------------------------------- /tests/ObservableCollections.R3.Tests/_GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using FluentAssertions; -------------------------------------------------------------------------------- /tests/ObservableCollections.Tests/_GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using FluentAssertions; 3 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/sandbox/BlazorApp/wwwroot/favicon.ico -------------------------------------------------------------------------------- /sandbox/WinUI3App/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "WinUI3App": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cysharp/ObservableCollections/HEAD/sandbox/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /sandbox/WinUI3App/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace WinUI3App; 2 | 3 | public sealed partial class MainWindow 4 | { 5 | public MainWindow() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | 4 | 5 | 6 | @foreach (var item in ItemsView) 7 | { 8 | 9 | 10 | 11 | } 12 |
@item
-------------------------------------------------------------------------------- /sandbox/BlazorApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft": "Warning", 7 | "Microsoft.Hosting.Lifetime": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace WinUI3App; 2 | 3 | public sealed partial class MainPage 4 | { 5 | public MainPage() 6 | { 7 | InitializeComponent(); 8 | } 9 | 10 | private MainPageViewModel ViewModel { get; } = new MainPageViewModel(new SampleService()); 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/prevent-github-change.yaml: -------------------------------------------------------------------------------- 1 | name: Prevent github change 2 | on: 3 | pull_request: 4 | paths: 5 | - ".github/**/*.yaml" 6 | - ".github/**/*.yml" 7 | 8 | jobs: 9 | detect: 10 | permissions: 11 | contents: read 12 | uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | stale: 10 | permissions: 11 | contents: read 12 | pull-requests: write 13 | issues: write 14 | uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main 15 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 |

Counter

4 | 5 |

Current count: @currentCount

6 | 7 | 8 | 9 | @code { 10 | private int currentCount = 0; 11 | 12 | private void IncrementCount() 13 | { 14 | currentCount++; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/BlazorApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Data/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorApp.Data 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sandbox/WpfApp/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sandbox/WpfApp/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace WpfApp 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | 3 | namespace WinUI3App; 4 | 5 | public partial class App 6 | { 7 | private Window? _window; 8 | 9 | public App() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | protected override void OnLaunched(LaunchActivatedEventArgs args) 15 | { 16 | _window = new MainWindow(); 17 | _window.Activate(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorApp 10 | @using BlazorApp.Shared 11 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/ObservableCollections.R3/Shims.cs: -------------------------------------------------------------------------------- 1 | #if NETSTANDARD2_0 || NETSTANDARD2_1 2 | 3 | namespace System.Threading; 4 | 5 | internal static class CancellationTokenExtensions 6 | { 7 | public static CancellationTokenRegistration UnsafeRegister(this CancellationToken cancellationToken, Action callback, object? state) 8 | { 9 | return cancellationToken.Register(callback, state, useSynchronizationContext: false); 10 | } 11 | } 12 | 13 | #endif -------------------------------------------------------------------------------- /sandbox/AvaloniaApp/App.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Sorry, there's nothing at this address.

9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/MainPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using ObservableCollections; 2 | 3 | namespace WinUI3App; 4 | 5 | public class MainPageViewModel 6 | { 7 | public MainPageViewModel(SampleService sampleService) 8 | { 9 | var view = sampleService.Items.CreateView(x => x); 10 | Items = view.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current); 11 | } 12 | 13 | public NotifyCollectionChangedSynchronizedViewList Items { get; private set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/ObservableCollections/Shims/CompilerServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace System.Runtime.CompilerServices 6 | { 7 | internal static class RuntimeHelpersEx 8 | { 9 | internal static bool IsReferenceOrContainsReferences() 10 | { 11 | #if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6 12 | return true; 13 | #else 14 | return RuntimeHelpers.IsReferenceOrContainsReferences(); 15 | #endif 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/App.xaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Shared/SurveyPrompt.razor: -------------------------------------------------------------------------------- 1 |
2 | 3 | @Title 4 | 5 | 6 | Please take our 7 | brief survey 8 | 9 | and tell us what you think. 10 |
11 | 12 | @code { 13 | // Demonstrates how a parent component can supply parameters 14 | [Parameter] 15 | public string Title { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/ObservableCollections/Shims/Nullables.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | #if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6 6 | 7 | namespace System.Diagnostics.CodeAnalysis 8 | { 9 | internal sealed class MaybeNullWhenAttribute : Attribute 10 | { 11 | public MaybeNullWhenAttribute(bool returnValue) 12 | { 13 | } 14 | } 15 | 16 | internal sealed class DoesNotReturnAttribute : Attribute 17 | { 18 | public DoesNotReturnAttribute() 19 | { 20 | } 21 | } 22 | } 23 | 24 | #endif -------------------------------------------------------------------------------- /sandbox/ConsoleApp/ConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 10.0 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /sandbox/WpfApp/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /sandbox/WpfApp/WpfApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net6.0-windows 6 | 12 7 | enable 8 | true 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/build-debug.yaml: -------------------------------------------------------------------------------- 1 | name: Build-Debug 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build-dotnet: 13 | permissions: 14 | contents: read 15 | runs-on: ubuntu-24.04 16 | timeout-minutes: 10 17 | steps: 18 | - uses: Cysharp/Actions/.github/actions/checkout@main 19 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main 20 | - run: dotnet build -c Release 21 | - run: dotnet test -c Release --no-build 22 | - run: dotnet pack -c Release --no-build -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg -o $GITHUB_WORKSPACE/artifacts 23 | -------------------------------------------------------------------------------- /sandbox/AvaloniaApp/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace AvaloniaApp 6 | { 7 | public partial class App : Application 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | public override void OnFrameworkInitializationCompleted() 15 | { 16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 17 | { 18 | desktop.MainWindow = new MainWindow(); 19 | } 20 | 21 | base.OnFrameworkInitializationCompleted(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" # Check for updates to GitHub Actions every week 8 | cooldown: 9 | default-days: 14 # Wait 14 days before creating another PR for the same dependency. This will prevent vulnerability on the package impact. 10 | ignore: 11 | # I just want update action when major/minor version is updated. patch updates are too noisy. 12 | - dependency-name: "*" 13 | update-types: 14 | - version-update:semver-patch 15 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Pages/Index.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using ObservableCollections; 3 | 4 | namespace BlazorApp.Pages; 5 | 6 | public partial class Index : IDisposable 7 | { 8 | ObservableList list; 9 | public ISynchronizedView ItemsView { get; set; } 10 | int count = 99; 11 | 12 | protected override void OnInitialized() 13 | { 14 | list = new ObservableList(); 15 | ItemsView = list.CreateView(x => x); 16 | 17 | ItemsView.CollectionStateChanged += action => 18 | { 19 | InvokeAsync(StateHasChanged); 20 | }; 21 | } 22 | 23 | void OnClick() 24 | { 25 | list.Add(count++); 26 | } 27 | 28 | public void Dispose() 29 | { 30 | ItemsView.Dispose(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:9278", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "BlazorApp": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "applicationUrl": "http://localhost:5000", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "IIS Express": { 21 | "commandName": "IISExpress", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace BlazorApp 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sandbox/AvaloniaApp/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace AvaloniaApp 5 | { 6 | internal class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .WithInterFont() 20 | .LogToTrace() 21 | .UseR3(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/ObservableCollections.Tests/ObservableCollections.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | all 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Data/WeatherForecastService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace BlazorApp.Data 6 | { 7 | public class WeatherForecastService 8 | { 9 | private static readonly string[] Summaries = new[] 10 | { 11 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 12 | }; 13 | 14 | public Task GetForecastAsync(DateTime startDate) 15 | { 16 | return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast 17 | { 18 | Date = startDate.AddDays(index), 19 | TemperatureC = Random.Shared.Next(-20, 55), 20 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 21 | }).ToArray()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ObservableCollections/Shims/CollectionsMarshalEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | 5 | #if !NET7_0_OR_GREATER 6 | 7 | #pragma warning disable CS0649 8 | #pragma warning disable CS8618 9 | #pragma warning disable CS8619 10 | 11 | namespace System.Runtime.InteropServices; 12 | 13 | internal static class CollectionsMarshal 14 | { 15 | /// 16 | /// similar as AsSpan but modify size to create fixed-size span. 17 | /// 18 | public static Span AsSpan(List? list) 19 | { 20 | if (list is null) return default; 21 | 22 | ref var view = ref Unsafe.As, ListView>(ref list!); 23 | return view._items.AsSpan(0, view._size); 24 | } 25 | 26 | internal sealed class ListView 27 | { 28 | public T[] _items; 29 | public int _size; 30 | public int _version; 31 | } 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /sandbox/AvaloniaApp/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace BlazorApp.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | [IgnoreAntiforgeryToken] 14 | public class ErrorModel : PageModel 15 | { 16 | public string RequestId { get; set; } 17 | 18 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 19 | 20 | private readonly ILogger _logger; 21 | 22 | public ErrorModel(ILogger logger) 23 | { 24 | _logger = logger; 25 | } 26 | 27 | public void OnGet() 28 | { 29 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ObservableCollections.R3/ObservableCollections.R3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;netstandard2.1;net6.0;net8.0 5 | 12 6 | disable 7 | enable 8 | 9 | 10 | R3 Extensions of ObservableCollections. 11 | true 12 | true 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | PerMonitorV2 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/ObservableCollections.R3.Tests/ObservableCollections.R3.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Cysharp, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | $(NoWarn);CS1591 5 | 6 | 7 | false 8 | $(Version) 9 | Cysharp 10 | Cysharp 11 | collection 12 | © Cysharp, Inc. 13 | https://github.com/Cysharp/ObservableCollections 14 | README.md 15 | $(PackageProjectUrl) 16 | git 17 | MIT 18 | Icon.png 19 | $(MSBuildThisFileDirectory)opensource.snk 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace BlazorApp.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @{ 5 | Layout = null; 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | BlazorApp 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | An error has occurred. This application may no longer respond until reloaded. 25 | 26 | 27 | An unhandled exception has occurred. See browser dev tools for details. 28 | 29 | Reload 30 | 🗙 31 |
32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Pages/FetchData.razor: -------------------------------------------------------------------------------- 1 | @page "/fetchdata" 2 | 3 | @using BlazorApp.Data 4 | @inject WeatherForecastService ForecastService 5 | 6 |

Weather forecast

7 | 8 |

This component demonstrates fetching data from a service.

9 | 10 | @if (forecasts == null) 11 | { 12 |

Loading...

13 | } 14 | else 15 | { 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | @foreach (var forecast in forecasts) 27 | { 28 | 29 | 30 | 31 | 32 | 33 | 34 | } 35 | 36 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
37 | } 38 | 39 | @code { 40 | private WeatherForecast[] forecasts; 41 | 42 | protected override async Task OnInitializedAsync() 43 | { 44 | forecasts = await ForecastService.GetForecastAsync(DateTime.Now); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # Visual Studio Spell checker configs (https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker?view=vs-2022#how-to-customize-the-spell-checker) 13 | spelling_exclusion_path = ./exclusion.dic 14 | 15 | [*.cs] 16 | indent_size = 4 17 | charset = utf-8-bom 18 | end_of_line = unset 19 | 20 | # Solution files 21 | [*.{sln,slnx}] 22 | end_of_line = unset 23 | 24 | # MSBuild project files 25 | [*.{csproj,props,targets}] 26 | end_of_line = unset 27 | 28 | # Xml config files 29 | [*.{ruleset,config,nuspec,resx,runsettings,DotSettings}] 30 | end_of_line = unset 31 | 32 | [*{_AssemblyInfo.cs,.notsupported.cs}] 33 | generated_code = true 34 | 35 | # C# code style settings 36 | [*.{cs}] 37 | dotnet_diagnostic.IDE0044.severity = none # IDE0044: Make field readonly 38 | 39 | # https://stackoverflow.com/questions/79195382/how-to-disable-fading-unused-methods-in-visual-studio-2022-17-12-0 40 | dotnet_diagnostic.IDE0051.severity = none # IDE0051: Remove unused private member 41 | dotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure 42 | -------------------------------------------------------------------------------- /src/ObservableCollections/ObservableCollections.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;netstandard2.1;net6.0;net8.0 5 | 12 6 | disable 7 | enable 8 | 9 | 10 | High performance observable collections and synchronized views, for WPF, Blazor, Unity. 11 | true 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ObservableCollections/ISynchronizedViewFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ObservableCollections 4 | { 5 | // Obsolete... 6 | [Obsolete("this interface is obsoleted. Use ISynchronizedViewFilter instead.")] 7 | public interface ISynchronizedViewFilter 8 | { 9 | bool IsMatch(T value); 10 | } 11 | 12 | public interface ISynchronizedViewFilter 13 | { 14 | bool IsMatch(T value, TView view); 15 | } 16 | 17 | internal class SynchronizedViewValueOnlyFilter(Func isMatch) : ISynchronizedViewFilter 18 | { 19 | public bool IsMatch(T value, TView view) => isMatch(value); 20 | 21 | class NullViewFilter : ISynchronizedViewFilter 22 | { 23 | public bool IsMatch(T value, TView view) => true; 24 | } 25 | } 26 | 27 | public class SynchronizedViewFilter(Func isMatch) : ISynchronizedViewFilter 28 | { 29 | public static readonly ISynchronizedViewFilter Null = new NullViewFilter(); 30 | 31 | public bool IsMatch(T value, TView view) => isMatch(value, view); 32 | 33 | class NullViewFilter : ISynchronizedViewFilter 34 | { 35 | public bool IsMatch(T value, TView view) => true; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sandbox/AvaloniaApp/AvaloniaApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0 5 | enable 6 | true 7 | app.manifest 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /sandbox/WinUI3App/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /sandbox/BlazorApp/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | 26 |
27 | 28 | @code { 29 | private bool collapseNavMenu = true; 30 | 31 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 32 | 33 | private void ToggleNavMenu() 34 | { 35 | collapseNavMenu = !collapseNavMenu; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/ObservableCollections.Tests/ObservableDictionaryTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Collections.Specialized; 5 | using System.Linq; 6 | using Xunit; 7 | 8 | namespace ObservableCollections.Tests 9 | { 10 | public class ObservableDictionaryTest 11 | { 12 | [Fact] 13 | public void View() 14 | { 15 | var dict = new ObservableDictionary(); 16 | var view = dict.CreateView(x => new ViewContainer(x.Value)); 17 | 18 | dict.Add(10, -10); // 0 19 | dict.Add(50, -50); // 1 20 | dict.Add(30, -30); // 2 21 | dict.Add(20, -20); // 3 22 | dict.Add(40, -40); // 4 23 | 24 | void Equal(params int[] expected) 25 | { 26 | dict.Select(x => x.Value).OrderByDescending(x => x).Should().Equal(expected); 27 | } 28 | 29 | Equal(-10, -20, -30, -40, -50); 30 | 31 | dict[99] = -100; 32 | Equal(-10, -20, -30, -40, -50, -100); 33 | 34 | dict[10] = -5; 35 | Equal(-5, -20, -30, -40, -50, -100); 36 | 37 | dict.Remove(20); 38 | Equal(-5, -30, -40, -50, -100); 39 | 40 | dict.Clear(); 41 | Equal(new int[0]); 42 | } 43 | 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sandbox/AvaloniaApp/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 |