Skip to content

XtremlyRed/HiLibrary.CSharp

Repository files navigation

HiLibrary.CSharp

A comprehensive C# utility library providing common helpers, extensions, and WPF components for .NET applications.

License

Features

HiLibrary.CSharp is organized into two main packages:

  • HiLibrary - Core utilities, extensions, and helpers for any .NET application
  • HiLibrary.Wpf - WPF-specific components including animations, dialogs, popups, and converters

Supported Frameworks

Framework Version
.NET Framework 4.5.1, 4.7.2
.NET Core 3.1
.NET 6.0, 8.0, 9.0
.NET Standard 2.0

Installation

# Install core library
dotnet add package Hi.Core

# Install WPF library (for WPF projects)
dotnet add package Hi.Wpf

HiLibrary (Core)

Commands

Powerful command implementations for MVVM pattern with synchronous and asynchronous support.

BindingCommand

// Simple command
public ICommand SaveCommand => new BindingCommand(() => Save());

// Command with CanExecute
public ICommand DeleteCommand => new BindingCommand(
    () => Delete(),
    () => SelectedItem != null
);

// Implicit conversion from Action
BindingCommand command = () => DoSomething();

// Global exception handling
BindingCommand.SetGlobalCommandExceptionCallback(ex => 
{
    Logger.LogError(ex, "Command execution failed");
});

BindingCommandAsync

// Async command
public IBindingCommandAsync LoadCommand => new BindingCommandAsync(
    async () => await LoadDataAsync(),
    () => !IsLoading
);

// Check if command is executing
if (LoadCommand.IsExecuting)
{
    // Show loading indicator
}

Generic Commands with Parameters

// Command with parameter
public ICommand SelectCommand => new BindingCommand<Item>(item => Select(item));

// Async command with parameter
public IBindingCommandAsync<int> FetchCommand => new BindingCommandAsync<int>(
    async id => await FetchAsync(id)
);

Event Manager

A flexible pub/sub event aggregator supporting synchronous and asynchronous events with channel-based messaging.

// Create event manager
IEventManager eventManager = new EventManager();

// Subscribe to events
IUnsubscrible subscription = eventManager.GetEvent<UserLoggedInEvent>()
    .Subscribe(e => HandleUserLogin(e), EventThreadPolicy.Current);

// Publish events
eventManager.GetEvent<UserLoggedInEvent>().Publish(new UserLoggedInEvent(user));

// Channel-based messaging
eventManager.GetEvent<NotificationEvent>()
    .Subscribe("alerts", notification => ShowAlert(notification));

eventManager.GetEvent<NotificationEvent>()
    .Publish("alerts", new NotificationEvent("New message"));

// Async events
var asyncEvent = eventManager.GetAsyncEvent<DataChangedEvent>();
await asyncEvent.PublishAsync(new DataChangedEvent());

// Unsubscribe when done
subscription.Unsubscribe();

Thread Policies:

  • EventThreadPolicy.Current - Execute on the subscriber's original thread
  • EventThreadPolicy.PublishThread - Execute on the publisher's thread
  • EventThreadPolicy.NewThread - Execute on a new thread pool thread

Context

Type-safe context containers for storing and retrieving values.

// Application-wide context
IApplicationContext appContext = new ApplicationContext();

// Store values by type
appContext.SetValue(new UserSettings());
appContext.SetValue("theme", "dark");

// Retrieve values
var settings = appContext.GetValue<UserSettings>();
var theme = appContext.GetValue<string>("theme");

// Safe retrieval
if (appContext.TryGetValue<UserSettings>(out var userSettings))
{
    // Use settings
}

Deferred Execution

Execute actions after a specified delay with debouncing support.

// Simple deferred action
IDeferredToken token = Defer.Deferred(500) // 500ms delay
    .Invoke(() => SearchAsync(query));

// Restart the timer (debouncing)
token.Restart();

// With TimeSpan
IDeferredToken token2 = Defer.Deferred(TimeSpan.FromSeconds(1))
    .Invoke(async () => await SaveChangesAsync());

// With context information
Defer.Deferred(1000).Invoke(context =>
{
    Console.WriteLine($"Executed on thread {context.ThreadId}");
    Console.WriteLine($"Was abandoned: {context.IsAbandoned}");
});

// Execute on UI thread
Defer.Deferred(500).Invoke(
    () => UpdateUI(),
    invokeInCurrentThread: true
);

// Dispose to cancel
token.Dispose();

Awaiter (Semaphore Wrapper)

A simple wrapper around SemaphoreSlim for resource management.

// Create awaiter with initial and max count
var awaiter = new Awaiter(initialCount: 3, maxCount: 5);

// Wait for resource
await awaiter.WaitAsync();
try
{
    // Use resource
}
finally
{
    awaiter.Release();
}

// Extension methods for locked execution
await awaiter.LockInvokeAsync(async () =>
{
    await DoWorkAsync();
});

// With timeout
bool acquired = await awaiter.WaitAsync(
    millisecondsTimeout: 5000,
    cancellationToken: cts.Token
);

Extension Methods

Enumerable Extensions

// Null/empty checks
if (collection.IsNullOrEmpty()) { }
if (collection.IsNotNullOrEmpty()) { }

// Conditional filtering
var filtered = items.WhereIf(includeDeleted, x => x.IsDeleted);

// Find index
int index = items.IndexOf(x => x.Id == targetId);

// Pagination
var page = items.Paginate(pageIndex: 2, pageSize: 10);

// ForEach with action
items.ForEach(item => Process(item));
items.ForEach((item, index) => Process(item, index));

// Async ForEach
await items.ForEachAsync(async item => await ProcessAsync(item));

// Join to string
string csv = items.Join(x => x.Name, ", ");

// To read-only collections
IReadOnlyList<T> readOnlyList = items.ToReadOnlyList();
IReadOnlyDictionary<K, V> readOnlyDict = dict.ToReadOnlayDictionary();

// Chunking (for older frameworks)
var chunks = items.Chunk(100);

Task Extensions

// Await TaskCompletionSource directly
var tcs = new TaskCompletionSource<int>();
int result = await tcs;

// Await multiple TaskCompletionSources
var sources = new[] { tcs1, tcs2, tcs3 };
int[] results = await sources;

// Await TimeSpan as delay
await TimeSpan.FromSeconds(1);

// Await collection of tasks
var tasks = items.Select(x => ProcessAsync(x));
await tasks;

String Extensions

// Various string utility methods
string formatted = text.ToTitleCase();
bool isEmpty = text.IsNullOrEmpty();

DateTime Extensions

// DateTime utility methods
var startOfDay = date.StartOfDay();
var endOfMonth = date.EndOfMonth();

Type Conversion

Flexible type conversion with custom converter registration.

// Convert using registered converters
int number = "123".ConvertTo<int>();
DateTime date = "2024-01-01".ConvertTo<DateTime>();

// Register custom converter
TypeConverterExtensions.ConvertRegister<string, CustomType>(
    str => CustomType.Parse(str)
);

// Use custom converter
var custom = "value".ConvertTo<CustomType>();

Thrower (Argument Validation)

Utility methods for argument validation with detailed exception messages.

public void Process(string input, object data)
{
    Thrower.IsNullOrEmpty(input, nameof(input));
    Thrower.IsNullOrWhiteSpace(input, nameof(input));
    Thrower.IsNull(data, nameof(data));
}

HiLibrary.Wpf

Animations

Fluent API for creating WPF property animations.

// Create animation
var handler = element.BeginAnimation(UIElement.OpacityProperty)
    .From(0.0)
    .To(1.0)
    .Duration(TimeSpan.FromMilliseconds(300))
    .EasingFunction(new CubicEase())
    .Delay(TimeSpan.FromMilliseconds(100))
    .AutoReverse(true)
    .RepeatBehavior(RepeatBehavior.Forever)
    .Completed(() => Console.WriteLine("Animation completed"))
    .Build();

// Start animation
handler.Begin();

// Supported property types:
// - Numeric: byte, short, int, long, float, double, decimal
// - Geometry: Point, Point3D, Vector, Vector3D, Rect, Size
// - Other: Color, Quaternion, Rotation3D

Dialog Service

Show modal and non-modal dialogs.

IDialogService dialogService = new DialogService();

// Show non-modal dialog
dialogService.Show(new MyDialogView(), new DialogParameter
{
    Title = "Settings",
    Width = 400,
    Height = 300
});

// Show modal dialog
bool? result = dialogService.ShowDialog(new ConfirmView(), new DialogParameter
{
    Title = "Confirm Action"
});

Popup Service

Display popups for messages, confirmations, and custom content.

IPopupService popupService = new PopupService();

// Show message
await popupService.ShowAsync("Operation completed successfully", "Success");

// Show confirmation
ButtonResult result = await popupService.ConfirmAsync(
    "Are you sure you want to delete this item?",
    "Confirm Delete"
);

if (result == ButtonResult.OK)
{
    // Delete item
}

// Show custom popup
var customResult = await popupService.PopupAsync<CustomResult>(
    new CustomPopupView(),
    new PopupParameter { /* options */ }
);

// Show in specific host
await popupService.ShowAsyncIn("DialogHost", "Message", "Title");

Notification Service

Display toast-style notifications.

INotificationService notificationService = new NotificationService();

// Show notification
await notificationService.NotifyAsync(
    "File saved successfully",
    TimeSpan.FromSeconds(3)
);

// Show in specific host
await notificationService.NotifyAsyncIn(
    "NotificationHost",
    "New message received",
    TimeSpan.FromSeconds(5)
);

Converters

Pre-built value converters for common scenarios.

// Boolean converters
Converters.BooleanReverse                    // true -> false, false -> true
Converters.BooleanToVisibility               // true -> Visible, false -> Collapsed
Converters.BooleanToVisibilityReverse        // true -> Collapsed, false -> Visible

// Null check converters
Converters.IsNull                            // null -> true
Converters.IsNotNull                         // not null -> true
Converters.IsNullOrEmpty                     // null/empty -> true
Converters.IsNotNullOrEmpty                  // not null/empty -> true
Converters.IsNullOrWhiteSpace                // null/whitespace -> true
Converters.IsNotNullOrWhiteSpace             // not null/whitespace -> true

// Visibility converters
Converters.IsNullToVisibility
Converters.IsNotNullToVisibility
Converters.IsNullOrEmptyToVisibility
Converters.IsNotNullOrEmptyToVisibility

// Comparison converters (to Visibility)
Converters.EqualToVisibility
Converters.NotEqualToVisibility
Converters.GreaterThanToVisibility
Converters.GreaterThanOrEqualToVisibility
Converters.LessThanToVisibility
Converters.LessThanOrEqualToVisibility

// Enum converters
Converters.GetEnumDescription               // Get [Description] attribute
Converters.GetEnumDisplayName               // Get [Display] attribute

// Color converters
Converters.StringToColor                    // "#FF0000" -> Color
Converters.StringToBrush                    // "#FF0000" -> Brush

Usage in XAML:

<Window xmlns:hi="clr-namespace:HiLibrary;assembly=HiLibrary.Wpf">
    <Button Visibility="{Binding HasItems, 
        Converter={x:Static hi:Converters.BooleanToVisibility}}" />
    
    <TextBlock Visibility="{Binding Name, 
        Converter={x:Static hi:Converters.IsNotNullOrEmptyToVisibility}}" />
</Window>

UI Dispatcher

Create background UI threads with their own Dispatcher.

// Create async
Dispatcher backgroundDispatcher = await UIDispatcher.RunNewAsync("RenderThread");

// Create sync
Dispatcher dispatcher = UIDispatcher.RunNew("BackgroundUI");

// Execute on background dispatcher
backgroundDispatcher.InvokeAsync(() =>
{
    // This runs on the background UI thread
    var visual = CreateVisual();
});

Controls

TransitioningControl

A content control that animates content changes.

<hi:TransitioningControl Content="{Binding CurrentView}">
    <!-- Content transitions automatically when changed -->
</hi:TransitioningControl>

EnumComboBox

A ComboBox that automatically populates with enum values.

<hi:EnumComboBox EnumType="{x:Type local:MyEnum}" 
                  SelectedValue="{Binding SelectedOption}" />

Performance Utilities

Tools for performance monitoring and UI optimization.

// Color animation performer
var colorPerformer = new ColorPerformer();

// Transition performer
var transitionPerformer = new TransitionPerformer();

Examples

MVVM ViewModel with Commands

public class MainViewModel : INotifyPropertyChanged
{
    public IBindingCommand SaveCommand { get; }
    public IBindingCommandAsync LoadCommand { get; }
    
    public MainViewModel()
    {
        SaveCommand = new BindingCommand(Save, () => CanSave);
        LoadCommand = new BindingCommandAsync(LoadAsync, () => !IsLoading);
    }
    
    private void Save() { /* ... */ }
    private async Task LoadAsync() { /* ... */ }
}

Event-Driven Architecture

public class OrderService
{
    private readonly IEventManager _eventManager;
    
    public OrderService(IEventManager eventManager)
    {
        _eventManager = eventManager;
    }
    
    public async Task PlaceOrderAsync(Order order)
    {
        await ProcessOrderAsync(order);
        
        // Notify other parts of the application
        _eventManager.GetEvent<OrderPlacedEvent>()
            .Publish(new OrderPlacedEvent(order));
    }
}

public class NotificationHandler
{
    public NotificationHandler(IEventManager eventManager)
    {
        eventManager.GetEvent<OrderPlacedEvent>()
            .Subscribe(OnOrderPlaced, EventThreadPolicy.Current);
    }
    
    private void OnOrderPlaced(OrderPlacedEvent e)
    {
        ShowNotification($"Order {e.Order.Id} placed successfully");
    }
}

Debounced Search

public class SearchViewModel
{
    private IDeferredToken? _searchToken;
    
    public string SearchText
    {
        get => _searchText;
        set
        {
            _searchText = value;
            DebouncedSearch();
        }
    }
    
    private void DebouncedSearch()
    {
        _searchToken?.Restart();
        _searchToken ??= Defer.Deferred(300)
            .Invoke(async () => await SearchAsync(_searchText), 
                    invokeInCurrentThread: true);
    }
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

XtremlyRed

About

csharp library repository

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages