
Security News
GitHub Actions Pricing Whiplash: Self-Hosted Actions Billing Change Postponed
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.
MVVM infrastructure, property change notifications, validation attributes, and UI component helpers.
Base class implementing INotifyPropertyChanged and INotifyPropertyChanging.
using Ecng.ComponentModel;
public class Person : NotifiableObject
{
private string _name;
public string Name
{
get => _name;
set
{
if (_name == value) return;
NotifyChanging(); // Fires PropertyChanging
_name = value;
NotifyChanged(); // Fires PropertyChanged
}
}
}
// Subscribe to changes
var person = new Person();
person.PropertyChanged += (s, e) =>
{
Console.WriteLine($"{e.PropertyName} changed");
};
Extended base class for ViewModels with dispatcher support.
public class MainViewModel : ViewModelBase
{
private bool _isLoading;
public bool IsLoading
{
get => _isLoading;
set
{
_isLoading = value;
NotifyChanged();
}
}
}
Notifiable object that marshals property changes to the UI thread.
public class ThreadSafeModel : DispatcherNotifiableObject
{
// PropertyChanged events automatically dispatched to UI thread
}
Extended ObservableCollection<T> with additional features.
var collection = new ObservableCollectionEx<Item>();
// Add range (fires single notification)
collection.AddRange(items);
// Remove range
collection.RemoveRange(itemsToRemove);
// Reset with new items
collection.Reset(newItems);
Thread-safe observable collection with UI dispatcher support.
var collection = new DispatcherObservableCollection<Item>();
// Safe to modify from any thread
Task.Run(() =>
{
collection.Add(new Item()); // Automatically dispatched
});
Observable collection that transforms source items.
var models = new ObservableCollection<PersonModel>();
var viewModels = new ConvertibleObservableCollection<PersonModel, PersonViewModel>(
models,
model => new PersonViewModel(model));
// Adding to models automatically adds to viewModels
models.Add(new PersonModel());
Generic range with min/max bounds.
var range = new Range<int>(1, 100);
// Check containment
bool contains = range.Contains(50); // true
bool outside = range.Contains(150); // false
// Range properties
int min = range.Min; // 1
int max = range.Max; // 100
int length = range.Length; // 99
// Range operations
var other = new Range<int>(50, 150);
var intersection = range.Intersect(other); // Range(50, 100)
var subRange = range.SubRange(20, 80); // Range(20, 80)
Specialized numeric range with additional operations.
var range = new NumericRange<decimal>(0, 100);
// Percentage calculations, clamping, etc.
public class Settings
{
[IntValidation(Min = 1, Max = 100)]
public int Count { get; set; }
[DoubleValidation(Min = 0.0, Max = 1.0)]
public double Factor { get; set; }
[DecimalValidation(Min = 0)]
public decimal Price { get; set; }
[LongValidation(Min = 0, Max = 1000000)]
public long Volume { get; set; }
}
[TimeSpanValidation(Min = "00:00:01", Max = "24:00:00")]
public TimeSpan Timeout { get; set; }
[PriceValidation(Min = 0)]
public Price StockPrice { get; set; }
Financial price with type information.
var price = new Price(100.50m, PriceTypes.Limit);
// Price types
PriceTypes.Limit // Limit price
PriceTypes.Market // Market price
PriceTypes.Percent // Percentage
Define increment step for numeric properties.
[Step(0.01)]
public decimal Price { get; set; }
[Step(1)]
public int Quantity { get; set; }
[Icon("path/to/icon.png")]
public class MyCommand { }
[VectorIcon("fas fa-home")] // Font Awesome style
public class HomeCommand { }
Link to documentation.
[Doc("https://docs.example.com/my-feature")]
public class MyFeature { }
Mark property as a basic (commonly used) setting.
[BasicSetting]
public string ApiKey { get; set; }
Manage server connection credentials securely.
var credentials = new ServerCredentials
{
Email = "user@example.com",
Password = "secret".Secure(), // SecureString
Token = "api-token".Secure()
};
// Check if configured
bool isConfigured = credentials.IsConfigured;
Ensure only one instance of an application runs.
using var singleton = new ProcessSingleton("MyApp");
if (!singleton.TryAcquire())
{
Console.WriteLine("Another instance is already running");
return;
}
// Application continues...
Abstract UI thread dispatcher.
public interface IDispatcher
{
void Invoke(Action action);
Task InvokeAsync(Action action);
void BeginInvoke(Action action);
}
// Check if on UI thread
if (!dispatcher.CheckAccess())
{
dispatcher.Invoke(() => UpdateUI());
}
Process items from a channel with controlled concurrency.
var executor = new ChannelExecutor<WorkItem>(
processItem: async item =>
{
await ProcessAsync(item);
},
maxConcurrency: 4);
// Add work
executor.Enqueue(new WorkItem());
// Graceful shutdown
await executor.StopAsync();
Track simple statistics.
var stat = new Stat("ProcessedItems");
stat.Increment();
stat.Add(5);
Console.WriteLine($"Total: {stat.Value}");
Schedule recurring actions.
var planner = new PeriodicActionPlanner();
planner.Schedule(
TimeSpan.FromMinutes(5),
async () => await RefreshDataAsync());
// Stop scheduling
planner.Cancel();
Install-Package Ecng.ComponentModel
FAQs
Ecng system framework
We found that ecng.componentmodel demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.