SodaFlow.Bindable.ObjectModel.Core
4.0.1
dotnet add package SodaFlow.Bindable.ObjectModel.Core --version 4.0.1
NuGet\Install-Package SodaFlow.Bindable.ObjectModel.Core -Version 4.0.1
<PackageReference Include="SodaFlow.Bindable.ObjectModel.Core" Version="4.0.1" />
<PackageVersion Include="SodaFlow.Bindable.ObjectModel.Core" Version="4.0.1" />
<PackageReference Include="SodaFlow.Bindable.ObjectModel.Core" />
paket add SodaFlow.Bindable.ObjectModel.Core --version 4.0.1
#r "nuget: SodaFlow.Bindable.ObjectModel.Core, 4.0.1"
#:package SodaFlow.Bindable.ObjectModel.Core@4.0.1
#addin nuget:?package=SodaFlow.Bindable.ObjectModel.Core&version=4.0.1
#tool nuget:?package=SodaFlow.Bindable.ObjectModel.Core&version=4.0.1
SodaFlow Bindable Object Model library for .NET core library.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.7.2
- SodaFlow.Core (>= 5.0.0 && < 6.0.0)
- System.ValueTuple (>= 4.6.2)
-
.NETStandard 2.0
- SodaFlow.Core (>= 5.0.0 && < 6.0.0)
- System.ValueTuple (>= 4.6.2)
-
net6.0
- SodaFlow.Core (>= 5.0.0 && < 6.0.0)
- System.ValueTuple (>= 4.6.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on SodaFlow.Bindable.ObjectModel.Core:
| Package | Downloads |
|---|---|
|
SodaFlow.Bindable.ObjectModel
SodaFlow Bindable Object Model library for C#. |
|
|
SodaFlow.FSharp.Bindable.ObjectModel
SodaFlow Bindable Object Model library for F#. |
GitHub repositories
This package is not used by any popular GitHub repositories.
4.0.1
Fixed: a bindable can be built from a looped cell. Each bindable used to sample its cell in its constructor. Inside a
loop block the looped cell has no value yet, so building a one-way value, a two-way value, or a command from it threw
"BehaviorLoop was sampled before it was looped".
A bindable now takes its initial value when the transaction that builds it closes, and the loop is defined by then. As
in 4.0.0, the first read returns that value immediately: building a bindable posts nothing to the scheduler and raises
no notification, and a command with no enablement cell is available from the start.
One visible difference: a bindable built in a transaction that then changes its cell starts with the changed value. It
used to start with the earlier value and announce the change with PropertyChanged or CanExecuteChanged once the
scheduler ran. Now there is no change to announce.
As before, hand a bindable to the binding thread only after the transaction that builds it has closed. A read inside the
loop block itself, before the block returns, throws, just as Sample on the looped cell does there. Reads after the loop
closes work.
Requires SodaFlow.Core 5.x, as 4.0.0 did. The members of SodaFlow.Core that this release calls are all in 5.0.0.
4.0.0
BREAKING: BindingScheduler.Default is gone, and no bindable selects a scheduler for itself any more. Each one used to
take, in order, the scheduler it was given, then Default, then the SynchronizationContext of the thread that built it,
and last of all ran its handlers inline. That last step was silent:
a view model built on a background thread raised PropertyChanged off the UI thread, and the binding engine failed later
and far from the cause.
Each construction now takes a scheduler and throws ArgumentNullException without one. The factories in
SodaFlow.Bindable.ObjectModel and SodaFlow.FSharp.Bindable.ObjectModel are the only way to build a bindable, and each
takes the scheduler once. Build one factory at startup, on the UI thread, with
SynchronizationContextBindingScheduler.Capture (), and give it to each view model; a test gives
BindingScheduler.Immediate.
BREAKING for the two language surfaces: the internal construction methods take the scheduler as a required argument, and
ToBindableActionImpl takes it before the enablement cell. A package built against 3.x does not run against this; take
the 4.x of each with it.
Requires SodaFlow.Core 5.x. It could not stay on 3.x: its dependency on SodaFlow.Core is a range that ends before the
next major, thus 3.0.2 cannot install beside SodaFlow.Core 5.0.0. Nothing here calls the internals that SodaFlow.Core
5.0.0 changed.
3.0.2
Adds the package icon that nuget.org shows beside this package.
Two exception messages are built with a target-typed new and with their concatenation laid out differently. The messages
themselves, and everything else, are unchanged.
Every package here ships this release together, so the dependency versions move with it.
3.0.1
Fixed: a two-way bindable now raises PropertyChanged for a write made through it, so more than one control can bind to
the same value.
The reconciliation pass compared the cell against the cached value to decide whether anything had changed. The setter
has already moved that cached value optimistically, so a write the graph accepted unchanged looked like nothing having
happened and was never announced. Only the control that wrote it knew: a checkbox and a slider bound to the same
property left the slider unable to follow the checkbox, permanently.
Notifications now go out whenever the settled value differs from either the cached value - the writer's correction, as
before - or the value last announced, which is what every other binding is showing. A write that changes nothing is
still silent, and what is announced is still what the graph settled on rather than what was optimistically written, so a
value the graph rejected or normalized never reaches a binding.
Expect one notification where there was none, per write that changes the value. Code that treated PropertyChanged as
meaning "the graph changed this, not the view" no longer can, and never reliably could: an update arriving from the
graph in the same turn was indistinguishable already.
3.0.0
BREAKING: requires SodaFlow.Core 4.x, where it required 3.x. That package removed internal members, and this one is
rebuilt against what replaced them.
BREAKING: IBindableAction.Execute rejects null whatever the action's type argument, where it previously accepted null
for a type which could represent one.
Passing null to an action over a reference type used to fire its stream with null; it now throws
InvalidOperationException, which is what it always did for a value type. One rule instead of two, and the rule the
nullable annotations already stated. Code relying on a null firing has to send it another way - the sink is still
there - or stop.
This one is quiet. The signature is unchanged, so nothing stops compiling; the exception arrives at runtime, from a call
which used to work.
Fixed: a two-way bindable no longer puts a value back on screen that the caller has already replaced, and no longer
discards a write.
The update handler wrote back the value the update carried. Running later, on the binding thread, after a setter had
moved the cached value on, that put the older value up and then raised a second notification to take it away again -
visible in a text box as a flicker back to what was just typed over. It samples the cell instead, so an update arriving
late says what is true rather than what was true when it fired.
The setter skips a write whose value matches the cached one. That reads the cache as a statement about the graph, when
it is only a statement about the last time the two were compared: between an update and the refresh it queues, they
disagree, and a write matching the stale cache was dropped even though the graph never held that value. The check now
stands down while a refresh is outstanding and lets the write through.
Documented, rather than changed: a bindable's Value belongs to the binding engine. The instance can be constructed on
any thread, but the property is read and written on the binding thread and nowhere else. Reaching for it from
application code is a procedural way around the graph anyway - the value it reports is one the graph already holds, and
a value pushed into it is one a sink can be sent directly - so this costs nothing that was worth having. A binding
engine already calls from the right thread; it is worth a look at application code which reads or sets these from a
background task.
Also documented: an IBindingScheduler must not wait for the action it is given. Post is called from inside a
transaction, which holds a process-wide lock, and the binding thread reaches this library through setters that open
transactions of their own - so a scheduler which hands work over and blocks until it finishes can deadlock against a
binding thread already waiting for that lock. Anything built on a dispatcher is fine; a hand-written scheduler needs the
care.
BREAKING: IBindingScheduler gains CheckAccess, so a scheduler written outside this package has to implement it. It
answers whether the calling thread is the one the scheduler posts to - the name is DispatcherObject's, because the
question is the same one and the answer is used the same way - and it is deliberately biased: an implementation which
cannot tell MUST return true. A wrong true gives up a diagnostic that was never promised; a wrong false throws on
correct code.
What it buys: a bindable's Value now throws InvalidOperationException when it is read or written from anywhere but the
binding thread, instead of quietly returning a stale value - or, for a large struct, a torn one. Only raised where the
scheduler is certain, so nothing is accused that cannot be proven. ImmediateBindingScheduler answers true
unconditionally, having no thread of its own, so tests and headless hosts are unaffected.
This will turn code that has been working by luck into code that throws. That is the point, but it is worth knowing
before upgrading rather than after.
ToOneWayToSource takes an optional scheduler now. It had none - nothing flows back out to the view, so there was nothing
to marshal - which also left it the one bindable whose Value could not be checked. It still schedules nothing; the
scheduler is there to say which thread is the right one.
The check adds about 2ns to reading Value on .NET 8, and about 2.3ns on .NET Framework, allocating nothing. It cost far
more before the two halves were reordered: reading SynchronizationContext.Current is not the cheap thread-local fetch it
looks like, and on .NET Framework asking it first made the check 13.0ns rather than 3.1ns, and the whole read 18.3ns
rather than 6.4ns. The thread id is compared first, and the context only if that fails. See SodaFlow.Benchmarks, which
found it and which runs on both runtimes because this is the kind of thing that differs between them.
Requires System.ValueTuple 4.6.2, where it required 4.4.0. Nothing here uses it differently: this repository named two
versions of it, one in the shipping projects and one in the test projects, and now names a single version in both. A
consumer does nothing about this; NuGet resolves the higher floor.
2.0.0
No code change. This release exists to move a dependency, and is a major version because of what moving it does to a
consumer.
Dependencies between these packages are now declared as ranges bounded at the next major, so NuGet refuses a pairing
which would fail rather than resolving it and leaving the failure until the code runs. This package now requires
SodaFlow.Core 3.x, where it required 2.x before.
That ceiling is why this is not a minor version. Taking this release obliges a consumer to take SodaFlow.Core 3.x as
well, and one who names SodaFlow.Core directly, or who uses anything removed there, cannot adopt it without changing
their own code. A version they cannot adopt is not a minor one.
1.0.0
First release.
The engine behind the SodaFlow bindable object model. Declares the interfaces a XAML binding engine needs -
IOneWayBindableValue, ITwoWayBindableValue, IOneWayToSourceBindableValue and IBindableAction - along with the
implementations behind them and the IBindingScheduler that marshals notifications onto the binding thread.
You do not install this directly. Take SodaFlow.Bindable.ObjectModel for C# or SodaFlow.FSharp.Bindable.ObjectModel for
F#; both bring this with them, and both are what expose its operations.
Depends on SodaFlow.Core.
Full notes: https://github.com/MorseCode-Software/SodaFlow/releases