Ecng system framework
$ dotnet add package Ecng.ComponentModelMVVM 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