A bundle of dotnet utilities
- Nuget:
Core library
Install-Package Blackcat -Version 1.0.1
Some utilities for WinForm flatform
Install-Package Blackcat.WinForm -Version 1.0.0
- Dll files (comming soon): release page
- Clone this repo as a submodule and add reference to your .net project
Saves or loads configurations from file automatically.
- Defines your configuration class
public class MyConfig
{
public string Config1 {get;set;}
public int Config2 {get;set;}
}
- Gets configuration and use it
// MyConfig will be loaded from file or create new one if needed
var myConfig = ConfigLoader.Default.Get<MyConfig>();
textBox1.Text = myConfig.Config1;
- Saves configuration: your configuration will be saved to json file automatically when winform application's closed. Json file will be something like:
{
"Metadata": {
"Modified": "2019-11-11T14:29:35.3268223+07:00"
},
"Configs": [
{
"Key": "MyObject",
"Data": {
"Config1": "This is a string config",
"Config2": 123,
}
}
]
}
Saves immediately whenever config property changes
// Inherits from AutoNotifyPropertyChanged class and marks properties as virtual.
public class MyConfig : AutoNotifyPropertyChanged
{
public virtual string Config1 {get;set;}
public virtual int Config2 {get;set;}
}
// Change save mode to:
// OnChange: save immediately when config changes
// OnExit: save when app's closed
// ReadOnly: don't save
ConfigLoader.Default.SaveMode = SaveMode.OnChange;
var myConfig = ConfigLoader.Default.Get<MyConfig>();
myConfig.Config1 = "New config";// config will be saved after this call
Edits config at runtime
[ConfigClass(Description = "Describe your config here")]
public class MyConfig
{
[Description("First string config")]
public string Config1 { get; set; }
[Description("Second int config")]
public int Config2 { get; set; }
}
var myConfig = ConfigLoader.Default.Get<MyConfig>();
using (var form = new SettingsForm { Settings = myConfig })
{
form.ShowDialog(this);
var changed = form.SettingsChanged;
}
Lightweight event aggregator/messenger for loosely coupled communication
- Defines an event which will carry data from caller to subscriber
public class MyEvent
{
public string Data1 {get; set;}
public int Data2 {get; set;}
}
- Subscribes events
class MyService
{
MyService()
{
// Start subscribing incomming events (call Unregister to unsubscribe events)
EventBus.Default.Register(this);
}
[Subscribe]
private void SubscribeMethod1(MyEvent myEvent)
{
// do you stuff here
}
[Subscribe]
private void SubscribeMethod2(MyEvent2 myEvent2)
{
// do you stuff here
}
}
- Raises an event
// Somewhere else in your project
EventBus.Default.Post(new MyEvent {Data1 = "my data1", Data2 = 123});// SubscribeMethod1 will be called
EventBus.Default.Post(new MyEvent1 {...});// SubscribeMethod2 will be called
Subscribes an event in background thread
// Currently we're supporting several thread modes:
// Post: default mode, invokes subscribers immediately in current caller thread
// Thread: invokes subscribers in a new background thread if caller thread is main thread, otherwise invokes subscribers immediately in current caller thread
// Async: Always invokes subsribers in a new background thread
// Main: Invokes subscribers in main thread (UI thread) in blocking mode
// MainOrder: Invokes subsribers in main thread (UI thread) in non-blocking mode
[Subscribe(ThreadMode = ThreadMode.Thread)]
private void WillBeCalledInBackgroundThread(MyEvent myEvent)
{
// this stuff will be called in background thread
}
Prevents further propagation of the current event
[Subscribe]
private PostHandled CancelableSubscriber(MyEvent myEvent)
{
// do you stuff here
return new PostHandled {Canceled = true};
}
Returns data for caller (only supports for Post or Main ThreadMode)
// From subscribers
[Subscribe]
private PostHandled ReturnValueForCaller1(MyEvent myEvent)
{
// do you stuff here
return new PostHandled {Data = "any data here"};
}
[Subscribe]
private string ReturnValueForCaller2(MyEvent myEvent)
{
// do you stuff here
return "any data here";
}
// From caller
var results = EventBus.Default.Post(new MyEvent{...});
Keeps the last sticky event of a certain type in memory. Then the sticky event can be delivered to subscribers or queried explicitly.
// Posts a sticky event
EventBus.Default.Post(new MyEvent{...}, true);// MyEvent likes other events but it's still remaining in memory after called
EventBus.Default.Post(new MyEvent{...}, true);// this will replace the first event
// Queries a specific sticky event
var myStickyEvent = EventBus.Default.GetStickyEvent(typeof(MyEvent));
// Removes a specific sticky event
EventBus.Default.RemoveStickyEvent(myStickyEvent);
Communicates with another process uses EventBus
Client process
var eventbus = ClientEventBus.StartNew("my-eventbus");
eventbus.Post(new MyEvent{...});
Main process (ex: a windows service)
var eventbus = ServerEventBus.StartNew("my-eventbus);
eventbus.Register(this);
.....
[Subscribe]
private void ListenMyEvent(MyEvent myEvent)
{
...
}
Handles and reports unhandled-exception automatically
private AppCrash appCrash = new AppCrash(); // defines it as a global variable to prevent GC collects this.
If an unhandled-exception occurs, a crash report will be shown with notepad, the log file is also saved in CrashLogs folder.
Show report with a custom form.
// defines you report form
public class YourReportForm : Form, IReportWindow
{
....
public void Initialize(Exception exception, string productName, string devMail)
{
textBox1.Text = exception.Message;
textBox2.Text = productName;
textBox3.Text = devMail;
}
}
// Registers you report form
var appCrash = new AppCrash
{
ProductName = "My product",
DeveloperMail = "xuanson33bk@gmail.com"
};
appCrash.Register(typeof(YourReportForm));
Custom crash logs folder location
var appCrash = new AppCrash {CrashDir = @"C:\newCrashLogsDir"};
Inversion of Control Container (bases on https://github.com/grumpydev/TinyIoC)
// You can annotate your class by Component, Service, Repository or Controller, they are the same but for easier to understand their roles.
[Controller]
public class MyApp
{
// Properties which are annotated with Autowired attribute will
// be injected automatically
[Autowired]
public MyComponent MyComponent {get; set;}
// myService will be injected automatically
public MyApp(MyService myService) {...}
....
}
[Service]
public class MyService {....}
[Component]
public class MyComponent{....}
// Somewhere else...
// App32Context will scan the entry assembly to find classes which are annotated with Controller, Component, Repository or Service attributes.
using (var context = new App32Context())
{
var myApp = context.Resolve<MyApp>();
myApp.Start();
}
Scans classes in specific assemblies
// If you don't pass any assembly, the entry assembly will be used
using (var context = new App32Context(assembly1, assemly2, assembly3))
{
....
}
Creates multiple instances of components
// By default, only one instance of a component will be created (singleton mode). If you want to create a new instance of a component each time it's injected to another component, set Singleton to false
[Service(Singleton = false)]
public class MyService {....}
IConfigLoader, IEventBus and AppCrash are registered automatically so you don't need to worry about these guys
using (var context = new App32Context())
{
var config = context.Resolve<IConfigLoader>();
....
}
Manipulates on types which don't need to reference explicitly.
Gets type
// Gets Control class from winform namespace, if current project doesn't reference to winform, return type will be null
var type = DynamicInvoker.GetType("System.Windows.Forms", "Control");
Registers event
// Registers ApplicationExit event on Application class
var type = DynamicInvoker.GetType("System.Windows.Forms", "Application");
if (type != null)
{
DynamicInvoker.AddEventHandler<EventHandler>(type, "ApplicationExit", Application_ApplicationExit);
}
Invokes method
// Invokes CreateControl method on Control instance
var type = DynamicInvoker.GetType("System.Windows.Forms", "Control");
if (type != null)
{
var control = Activator.CreateInstance(type);
DynamicInvoker.InvokeMethod(control, "CreateControl");
}
Gets or sets property value
// Gets ProductName from Application class
var type1 = DynamicInvoker.GetType("System.Windows.Forms", "Application");
if (type1 != null)
{
var productName = DynamicInvoker.GetPropertyValue(type1, "ProductName") as string;
}
// Sets "My text" to Text property
var type2 = DynamicInvoker.GetType("System.Windows.Forms", "Control");
if (type2 != null)
{
var control = Activator.CreateInstance(type2);
DynamicInvoker.SetPropertyValue(control, "Text", "My text");
}
Inter process communication which allows processes to communicate each other and synchronize their actions
1 client - 1 server communication
Sender process (client)
using (var sender = new Sender("my-intercomm"))
{
var response = await sender.SendAsync<string>("Hi server, I'm client1");
Console.WriteLine("Response from the other process is: " + response);
}
Receiver process (server)
using (var receiver = new SingleReceiver("my-intercomm"))
{
var request = await receiver.ReceiveAsync<string>();
await receiver.SendAsync("Hello " + request);
}
n client - 1 server communication
Setup your sender process likes the basic usage section.
Receiver process (server)
using (var receiver = new MultiReceiver("my-intercomm"))
{
await receiver.WaitForSessionAsync(async session =>
{
var request = await session.ReceiveAsync<string>();
await session.SendAsync("Hello " + request);
});
}
Currently this library supports communicating through tcp and pipe:
- Blackcat.Intercomm.Pipe: for pipe supported
- Blackcat.Intercomm.Tcp: for tcp supported