IT.Tangdao.Framework
5.1.0
dotnet add package IT.Tangdao.Framework --version 5.1.0
NuGet\Install-Package IT.Tangdao.Framework -Version 5.1.0
<PackageReference Include="IT.Tangdao.Framework" Version="5.1.0" />
<PackageVersion Include="IT.Tangdao.Framework" Version="5.1.0" />
<PackageReference Include="IT.Tangdao.Framework" />
paket add IT.Tangdao.Framework --version 5.1.0
#r "nuget: IT.Tangdao.Framework, 5.1.0"
#:package IT.Tangdao.Framework@5.1.0
#addin nuget:?package=IT.Tangdao.Framework&version=5.1.0
#tool nuget:?package=IT.Tangdao.Framework&version=5.1.0
IT.Tangdao以下简称唐刀,是我开发的一款适用于WPF的专属框架。
内置了IOC容器ITangdaoContainer、
事件聚合器IEventAggregator、
内容读写器IContentAccess
路径优化类TangdaoPath、
文件监控器IFileMonitor、
文件路径查询器IFileLocator、
自带日志监控ITangdaoLogger、
进程参数传输器ITangdaoParameter、
UI线程直接传输类UIAmbientContext、
简单的翻页导航ISingleRouter、
带有拦截功能和传输功能的导航系统ITangdaoRouter、
虚假数据生成器TangdaoDataFaker、
任务调度器TangdaoTaskScheduler、
异步任务流ITangdaoQueueManager
异步任务控制器ITangdaoController
弱引用事件TangdaoWeakEvent
委托表传输机制IActionTable,复合委托表CompositeActionTable
命令和异步命令TangdaoCommand,TangdaoAsyncCommand
快速静态命令MinidaoCommand
Window管道,WindowPipe
观察者模式事件通知TangdaoMessenger
拦截器、在开发中
此外还内置了常用的WPF转换器和markup扩展
1、启动
1-1、修改App.xaml
<td:TangdaoApplication
x:Class="TangdaoDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TangdaoDemo"
xmlns:td="clr-namespace:IT.Tangdao.Framework;assembly=IT.Tangdao.Framework"
xmlns:vm="clr-namespace:TangdaoDemo.ViewModels">
<Application.Resources>
</Application.Resources>
</td:TangdaoApplication>
1-2、修改App.xaml.cs
public partial class App : TangdaoApplication
{
private static readonly ITangdaoLogger Logger = TangdaoLogger.Get(typeof(App));
protected override void Configure()
{
base.Configure();
}
protected override void RegisterServices(ITangdaoContainer container)
{
container.RegisterdaoSingleton<MainView>();
container.RegisterdaoSingleton<MainViewModel>();
Logger.WriteLocal("Tangdao测试代码启动");
}
protected override Window CreateWindow()
{
return CreateShell<MainView>();
}
private void RegisterExceptionEvents()
{
TangdaoExceptionHandler handler = new TangdaoExceptionHandler();
//Task线程内未捕获异常处理事件
TaskScheduler.UnobservedTaskException += handler.TaskScheduler_UnobservedTaskException;
//UI线程未捕获异常处理事件(UI主线程)
this.DispatcherUnhandledException += handler.App_DispatcherUnhandledException;
//非UI线程未捕获异常处理事件(例如自己创建的一个子线程)
AppDomain.CurrentDomain.UnhandledException += handler.CurrentDomain_UnhandledException;
}
}
2、唐刀命令
正常命令:TangdaoCommand
异步命令:TangdaoAsyncCommand
静态命令:MinidaoCommand
1-1、用法
TangdaoCommand taodao=new TangdaoCommand(()=>{});
TangdaoAsyncCommand taodaoAsync=new TangdaoAsyncCommand(async () => { });
MinidaoCommand.Create(()=>{});
MinidaoCommand.CreateFromTask(async () => { });
1-2、集成快捷命令
//当ViewModel继承DaoViewModelBase的时候,可以快速创建命令,省去new TangdaoCommand();
public ICommand SaveCommand => this.Cmd(Execute);
3、事件聚合器
IEventAggregator
3-1、用法
public IEventAggregator _daoEventAggregator;
_daoEventAggregator.Publish<T>();
_daoEventAggregator.Subscribe<T>(Execute);
T:EventBase
4、IOC容器
4-1、容器ITangdaoContainer
修改启动项
public partial class App : TangdaoApplication
{
protected override void RegisterServices(ITangdaoContainer container)
{
container.RegisterdaoSingleton<MainView>();
container.RegisterdaoSingleton<MainViewModel>();
container.RegisterdaoSingleton<HomeViewModel>();
container.RegisterdaoSingleton<IReadService, ReadService>();
}
protected override Window CreateWindow()
{
return CreateShell<MainView>();
}
}
除了单例注册外,还可以瞬态注册,工厂注册,Key值注册
container.RegisterdaoScoped<T>();
container.RegisterdaoTransient<T>();
container.RegisterdaoSingleton<T>();
container.AddKeyedTransient<T>();
container.RegisterdaoSingletonFactory<IWeatherService>(provider =>
new WeatherService(provider.GetService<ITangdaoLogger>(), provider.GetService<IConfig>()));
4-2、解析器ITangdaoProvider
Provider.GetService<T>();
4-3、服务定位器
TangdaoApplication.Provider.GetService(viewModel);
5、插件式注册
5-1、对于.Netframwork版本继承TangdaoModuleBase
public class DemoModule : TangdaoModuleBase
{
public override void RegisterServices(ITangdaoContainer container)
{
container.AddKeyedSingleton<IReadContentService, ReadConfigService>("config");
container.AddKeyedSingleton<IReadContentService, ReadJsonService>("json");
container.AddKeyedSingleton<IReadContentService, ReadXmlService>("xml");
}
public override void OnInitialized(ITangdaoProvider provider)
{
base.OnInitialized(provider);
}
}
5-2、对于.NetCore版本继承ITangdaoModule
public class DemoModule : ITangdaoModule
{
public override void RegisterServices(ITangdaoContainer container)
{
container.AddKeyedSingleton<IReadContentService, ReadConfigService>("config");
container.AddKeyedSingleton<IReadContentService, ReadJsonService>("json");
container.AddKeyedSingleton<IReadContentService, ReadXmlService>("xml");
}
public override void OnInitialized(ITangdaoProvider provider)
{
base.OnInitialized(provider);
}
}
5-3、AutoViewAttribute
当对于ViewModel使用特性的时候,自动关联View和ViewModel
[AutoView]
public class DefaultViewModel : DaoViewModelBase{}
使用方式需要ContentControl
<ContentControl Content="{Binding DefaultViewModel}" />
也就是本框架默认使用ViewModel-First
如果使用View-First
写了
<local: DefaultView/>
默认绑定不会生效
6、常用文件的读写
6-1、对XML文件的读写
xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LoginDto xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<UserName>Admin</UserName>
<Password>123</Password>
<IsRemember>true</IsRemember>
<IsAdmin>true</IsAdmin>
<Role>管理员</Role>
</LoginDto>
cs代码:
string foldPath = Path.Combine(IgniteInfoLocation.Cache, "LoginInfo.xml");
var isRememberValue = _access.Default.Read(foldPath).AsXml().SelectNode("IsRemember").Value;
支持XML的序列化和反序列化
Student student=XmlFolderHelper.Deserialize<Student>(xmlContent);
string xml=XmlFolderHelper.SerializeXML<Student>(student);
也可以使用缓存模式序列化,只要读过本地的文件,就可以使用缓存转成对象实例
_access.Cache.DeserializeCache<LoginDto>(foldPath.Value, DaoFileType.Xml);
6-2、对Config文件的读写
1、读取默认的App.config配置
<configuration>
<configSections>
<section name="Menu" type="System.Configuration.DictionarySectionHandler" />
<section name="Student" type="System.Configuration.DictionarySectionHandler" />
</configSections>
<Menu>
<add key="0" value="我的样本" />
<add key="1" value="动态记录" />
<add key="2" value="存储" />
<add key="3" value="实验" />
</Menu>
<Student>
<add key="Id" value="1" />
<add key="Name" value="张三" />
<add key="Age" value="18" />
<add key="Source" value="18" />
</Student>
</configuration>
cs代码:使用SelectAppConfig ,由于读取的是WPF自带的Appconfig,不需要带路径
_access.Default.Empty().AsConfig().SelectAppConfig(HandlerName);
也可以直接将读取到的数据转成List或ObservableCollection
_access.Default.Empty().AsConfig().SelectAppConfig(HandlerName).ToList(v => new TangdaoMenuItem { MenuName = v }).ToObservableCollection();
2、读取自定义配置config文件
对于.Framework版本
<section name="Tangdao" type="IT.Tangdao.Core.Common.TangdaoMenuSection,IT.Tangdao.Framework" />
对于.NetCore版本
<section name="Tangdao" type="IT.Tangdao.Core.Common.TangdaoMenuSection,IT.Tangdao.Core" />
<Tangdao>
<menus>
<add title="首页" value="DefaultViewModel" />
<add title="用户信息" value="UserInfoViewModel" />
<add title="设置" value="SetViewModel" />
<add title="监控" value="MonitorViewModel" />
<add title="维护" value="MaintionViewModel" />
<add title="配方" value="RecipeViewModel" />
<add title="参数" value="ParameBaseViewModel" />
</menus>
</Tangdao>
cs代码:使用SelectCustomConfig,注意,这里需要先指定路径Read(configPath)
var responseResult = _access.Default.Read(configPath).AsConfig().SelectCustomConfig(readTitle, section);
if (responseResult.Payload is Dictionary<string, string> data)
{
List<HomeMenuItem> menuItems = data.Select(kvp => new HomeMenuItem
{
Title = kvp.Key,
ViewModelName = kvp.Value
}).ToList();
return new ReadOnlyCollection<HomeMenuItem>(menuItems);
}
6-3、对Json文件的读写
var json = _access.Default.Read(foldPath).AsJson();
6-4、对ini文件的读写
ini文件内容
[parameters]
api_url=http://58.211.68.202:8082
sockt_port=7181
boot_up=1
socket_port=7181
var result = _access.Default.Read(foldPath).AsIni().SelectIni("socket_port");
对XML各种文件子节点的读取
<?xml version="1.0" encoding="utf-8"?>
<Student target="学生">
<Id target="009">1</Id>
<Age>18</Age>
<Name>李四</Name>
</Student>
实体类为
[XmlRoot("Student")]
public class Student
{
[XmlAttribute("target")]
public string Target { get; set; }
[XmlElement("Id")]
public int Id { get; set; }
[XmlElement("Age")]
public int Age { get; set; }
[XmlElement("Name")]
public string Name { get; set; }
}
使用SelectNode读取xml节点
例如xml文档如下
<?xml version="1.0" encoding="utf-8"?>
<UserInfo>
<Login Id="0">
<UserName>Admin</UserName>
<Password>2</Password>
<Role>管理员</Role>
<IsAdmin>True</IsAdmin>
<IP>192.168.0.1</IP>
</Login>
<Register Id="1">
<UserName>Ad</UserName>
<Password>12</Password>
<Role>普通用户</Role>
<IsAdmin>False</IsAdmin>
<IP>127.0.0.1</IP>
</Register>
</UserInfo>
使用方式
// 正确调用(多节点必须指定索引)
var ip1 = _access.Default.Read("Path").AsXml().SelectNode("IP").Value;
var ipAll = _access.Default.Read("Path").AsXml().SelectNodes();
优化繁琐的读取,不需要知道类的所有属性
var readResult = _access.Default.Read("Path").AsXml().SelectNodes("ProcessItem", x => new ProcessItem
{
Name = x.Element("Name")?.Value,
IsFeeding = (bool)x.Element("IsFeeding"),
IsBoardMade = (bool)x.Element("IsBoardMade"),
IsBoardCheck = (bool)x.Element("IsBoardCheck"),
IsSeal = (bool)x.Element("IsSeal"),
IsSafe = (bool)x.Element("IsSafe"),
IsCharge = (bool)x.Element("IsCharge"),
IsBlanking = (bool)x.Element("IsBlanking"),
});
直接通过反射+泛型
var readResult = _access.Default.Read("Path").AsXml().SelectNodes<ProcessItem>();
7、扩展
7-1、基于ViewModel命名约定的隐式键展开容器字典
public class Test
{
public static MultiKeyDictionary<string> keyValuePairs = new MultiKeyDictionary<string>();
public static MultiKeyDictionary<string> Print()
{
keyValuePairs["Sample"] = "Test";
//keyValuePairs["SampleView"] = "Test";
// keyValuePairs["SampleViewModel"] = "Test";
return keyValuePairs;
}
}
内部跟据View和ViewModel后缀自动绑定字典
增加自定义排序字典TangdaoSortedDictionary,以及对它的扩展
7-2、增加另外一种全新的方式去发送数据
MainViewModel: 发送
private void Execute()
{
ITangdaoParameter tangdaoParameter = new TangdaoParameter();
tangdaoParameter.Add("001",Name);
this.RunSameLevelWindowAsync<LoginView>(tangdaoParameter);
}
LoginViewModel:接收
public void Response(ITangdaoParameter tangdaoParameter)
{
Name = tangdaoParameter.Get<string>("001");
}
对PLC的读取进行了扩展
注意:已迁移到IT.Tangdao.Core.Bridge.dll或IT.Tangdao.Core.Bridge.dll
container.RegisterPlcServer(plc =>
{
plc.PlcType= PlcType.Siemens;
plc.PlcIpAddress = "127.0.0.1";
plc.Port = "502";
});
container.RegisterType<IPlcReadService,PlcReadService>();
var plcservice=provider.Resolve<IPlcReadService>();
plcservice.ReadAsync("DM200");
链式创建对象
var maybe = TangdaoOptional<string>.Some("Hello")
.Where(s => s.Length > 3)
.Select(s => s.ToUpper())
.ValueOrDefault("NONE");
Console.WriteLine(maybe); // HELLO
8、组件通信
//同级别窗体通信
//tangdaoParameter为发送的数据,可以在打开窗体的时候直接发送数据过去
this.RunSameLevelWindowAsync<LoginView>(tangdaoParameter);
//父子窗体通信
this.RunChildWindowAsync<LoginView>();
8-1、线程之间的数据传输 AmbientContext
//值类型使用
AmbientContext.Set(123);
AmbientContext.Get<int>();
//引用类型使用
Student student=new Student(1,"张三");
AmbientContext.SetCurrent(student);
AmbientContext.Get<Student>();
//按指定Key值传输数据
AmbientContext.SetCurrent("学生",student);
AmbientContext.Get<Student>("学生");
8-2、进程之间的数据传输 TangdaoContext
功能更加强大,可以进行数据传输,委托传输,字典传输,命令传输
TangdaoContext.SetTangdaoParameter<T>();
TangdaoContext.GetTangdaoParameter<T>();
8-3、Socket通信,注意:此功能已迁移到IT.Tangdao.Core.Bridge.dll或IT.Tangdao.Core.Bridge.dll
string connUri = "tcp://127.0.0.1:502";
var uri = new TangdaoUri(connUri);
Context = TangdaoChannelBuilder.Build(NetMode.Client, connUri);
container.RegisterdaoSingleton<ITangdaoChannel>();
container.RegisterdaoSingleton<ITangdaoRequest>();
container.RegisterdaoSingleton<ITangdaoResponse>();
9、日志TangdaoLogger
日志默认是写在桌面上的
private static readonly ITangdaoLogger Logger = TangdaoLogger.Get(typeof(Bootstrapper));
Logger.WriteLocal($"注册成功");
会在桌面生成Tangdaos目录的日志
也可以自定义配置日志的路径:
下面代码放在启动项即可
LogEntry logEntry = new LogEntry()
{
SaveDir = ConfigurationManager.AppSettings["LoggerPath"],
LogFormat = LogFormat.Json
};
LogEnsureConfig.Load(logEntry);
现在日志有三种格式,txt文本,xml文本和json文本
主要的设计模式及其应用:
1. 静态工厂模式
- 应用 : TangdaoLogger.Get<TType>() 方法
- 作用 :通过类型获取对应的日志记录器实例,简化了日志记录器的创建过程
2. 单例模式
- 应用 : TangdaoLogger 类使用 ConcurrentDictionary 缓存每个类型的日志记录器实例
- 作用 :确保每个类型只有一个日志记录器实例,减少内存占用
3. 策略模式
- 应用 : LogHelper.SaveLogToFile 方法根据不同的日志格式采用不同的保存策略
- 作用 :支持多种日志格式(XML、JSON、文本),易于扩展新的格式
4. 生产者-消费者模式
- 应用 : LoggerHandler 使用 BlockingCollection 来处理日志
- 作用 :实现了日志的异步处理,提高了应用性能
5. 扩展方法模式
- 应用 : TangdaoLoggerExtension 提供了 WriteLocal 扩展方法
- 作用 :为 ITangdaoLogger 接口添加了额外的功能,而不需要修改接口定义
6. 配置模式
- 应用 :使用 LogEnsureConfig 来加载和管理日志配置
- 作用 :集中管理日志配置,提供了统一的配置接口
7. 命令模式
- 应用 : LogItem 类封装了日志的所有信息
- 作用 :将日志信息封装成一个对象,便于在系统中传递和处理
8. 依赖注入模式
- 应用 : TangdaoLogger.LoggerFactory 属性
- 作用 :允许用户自定义日志记录器的创建方式,提高了系统的灵活性
9. 枚举策略模式
- 应用 :使用 LogFormat 枚举代替字符串常量
- 作用 :提高了类型安全性,避免了字符串拼写错误
10. 序列化模式
- 应用 :使用 TangdaoXmlSerializer 和 JsonConvert 进行序列化
- 作用 :支持不同格式的日志输出,便于日志的存储和分析
10、自动生成器
可以自动生成虚假数据,用于平时调试
在WPF可以这样使用
public class MainWindowViewModel : BindableBase
{
private ObservableCollection<Student> _students;
public ObservableCollection<Student> Students
{
get => _students;
set => SetProperty(ref _students, value);
}
public MainWindowViewModel()
{
Loaded();
}
private void Loaded()
{
var generator = new TangdaoDataFaker<Student>();
List<Student> randomStudents = generator.Build(10);
Students = new ObservableCollection<Student>(randomStudents);
}
}
public class Student
{
public int Id { get; set; }
[DaoFakeDataInfo("姓名")] // 使用 DaoName 枚举生成姓名
public string Name { get; set; }
public int Age { get; set; }
[DaoFakeDataInfo("爱好")] // 使用 DaoHobby 枚举生成爱好
public string Hobby { get; set; }
[DaoFakeDataInfo("城市")] // 使用 ChineseCities 数组生成城市
public string Address { get; set; }
}
11、增加路由导航
1、简单的导航,具有翻页功能ISingleRouter
使用方式,与ISingleNavigateView配合使用
使用IOC容器注册所有的视图
XAML Code:
<ContentControl
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Content="{Binding CurrentView}" />
<Border
Grid.Row="1"
Padding="10"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button
Width="100"
Command="{Binding PreviousCommand}"
Content="◄ 上一页"
IsEnabled="{Binding CanPrevious}" />
<ToggleButton
Width="200"
Margin="20,0"
Background="{Binding IsAutoRotating, Converter={StaticResource BoolToColorConverter}}"
Command="{s:Action ToggleAutoCarousel}"
Content="{Binding AutoRotateStatusText}"
IsChecked="{Binding IsAutoRotating, Mode=OneWay}" />
<Button
Width="100"
Command="{Binding NextCommand}"
Content="下一页 ►"
IsEnabled="{Binding CanNext}" />
</StackPanel>
</Border>
CS Code:
public class GlobalPhotoViewModel : Screen
{
private readonly ISingleRouter _router;
public ISingleNavigateView CurrentView => _router.CurrentView;
public bool CanPrevious => _router.CanPrevious;
public bool CanNext => _router.CanNext;
public bool IsAutoRotating => _router.IsAutoRotating;
public string AutoRotateStatusText => _router.IsAutoRotating ? "自动轮播开启中" : "自动轮播已禁用";
public GlobalPhotoViewModel(ISingleRouter router)
{
_router = router;
_router.PropertyChanged += OnRouterPropertyChanged;
_router.NavigationChanged += OnRouterNavigationChanged;
}
public void Previous()
{
_router.Previous();
}
public void Next()
{
_router.Next();
}
public void ToggleAutoCarousel() => _router.ToggleAutoCarousel();
private void OnRouterPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// 将路由器的属性变化转发到视图模型
NotifyOfPropertyChange(e.PropertyName);
if (e.PropertyName == nameof(ISingleRouter.IsAutoRotating))
{
NotifyOfPropertyChange(nameof(AutoRotateStatusText));
}
}
private void OnRouterNavigationChanged(object sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(CurrentView));
NotifyOfPropertyChange(nameof(CanPrevious));
NotifyOfPropertyChange(nameof(CanNext));
}
protected override void OnDeactivate()
{
_router.IsAutoRotating = false;
base.OnDeactivate();
}
}
2、工业级别导航,具有拦截器ITangdaoRouter
使用时与ITangdaoPage配合
XAML Code:
<ContentControl Grid.Row="0" Content="{Binding Router.CurrentView}" />
<StackPanel
Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="2"
Command="{Binding GoBackCommand}"
Content="◄"
IsEnabled="{Binding Router.CanGoBack}"
ToolTip="上一页" />
<Button
Margin="2"
Command="{Binding GoForwardCommand}"
Content="►"
IsEnabled="{Binding Router.CanGoForward}"
ToolTip="下一页" />
<Button
Margin="5"
Command="{s:Action GoToVacuumGaugeView}"
Content="真空表" />
<Button
Margin="5"
Command="{s:Action GoToDigitalSmartGaugeView}"
Content="数字智能测量仪" />
</StackPanel>
CS Code:
public class MainViewModel : BaseDeviceViewModel
{
public ITangdaoRouter Router { get; set; }
public IContainer _container;
public MainViewModel(ITangdaoRouter router)
{
Router = router;
_container = container;
Router.RouteComponent = this;
Router.RegisterPage<Test1ViewModel>();
Router.RegisterPage<Test2ViewModel>();
Router.RegisterPage<Test3ViewModel>();
GoBackCommand = MinidaoCommand.Create(ExecuteGoBack);
GoForwardCommand = MinidaoCommand.Create(ExecuteGoForward);
}
private void ExecuteGoForward()
{
Router.GoForward();
}
private void ExecuteGoBack()
{
Router.GoBack();
}
public ICommand GoBackCommand { get; set; }
public ICommand GoForwardCommand { get; set; }
public void GoToTest1View()
{
//Router.NavigateTo<Test1ViewModel>();或者
Router.NavigateTo("Test1ViewModel");
}
public void GoToTest2View()
{
//Router.NavigateTo<Test2ViewModel>();或者
Router.NavigateTo("Test2ViewModel");
}
public void GoToTest3View()
{
//Router.NavigateTo<Test3ViewModel>();或者
Router.NavigateTo("Test3ViewModel");
}
}
[AutoWireView]
public class Test1ViewModel : DaoViewModelBase, ITangdaoPage
{
public string PageTitle => "测试1";
public bool CanNavigateAway()
{
return true;
}
public void OnNavigatedFrom()
{
}
public void OnNavigatedTo(ITangdaoParameter parameter = null)
{
}
}
12、时间轮
class Program
{
static async Task Main(string[] args)
{
// 创建时间轮实例
var timeWheel = new TimeWheel<Student>();
timeWheel.Start(); // 启动时间轮
// 创建几个学生
var student1 = new Student { Id = 1, Name = "张三", Grade = 3 };
var student2 = new Student { Id = 2, Name = "李四", Grade = 2 };
var student3 = new Student { Id = 3, Name = "王五", Grade = 1 };
Console.WriteLine($"当前时间: {DateTime.Now:HH:mm:ss}");
// 添加任务:5秒后打印学生信息
await timeWheel.AddTaskAsync(5, student1, async s =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 处理学生1: {s}");
await Task.Delay(100); // 模拟异步工作
});
// 添加任务:10秒后升级学生年级
await timeWheel.AddTaskAsync(10, student2, async s =>
{
s.Grade++;
Console.WriteLine($"{DateTime.Now:HH:mm:ss} - {s.Name}升级到{s.Grade}年级");
await Task.Delay(100);
});
// 添加任务:15秒后发送通知
await timeWheel.AddTaskAsync(15, student3, async s =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss} - 发送通知给{s.Name}的家长");
await Task.Delay(100);
});
// 防止程序退出
Console.ReadLine();
}
}
13、文本监控
在程序启动时注册事件
protected override void Configure()
{
// 启动监控服务
var monitorService = Provider.GetService<IFileMonitor>();
monitorService.FileChanged += OnFileChanged;
monitorService.StartMonitoring();
}
private void OnFileChanged(object sender, DaoFileChangedEventArgs e)
{
Logger.WriteLocal($"XML 文件变化: {e.FilePath}, 变化类型: {e.ChangeType},变化详情:{e.ChangeDetails},old:{e.OldContent},new:{e.NewContent}");
}
注册代码
container.RegisterdaoSingleton(new FileMonitorConfig()
{
MonitorRootPath = ConfigurationManager.AppSettings["MontionPath"],
IncludeSubdirectories = true,
MonitorFileTypes = new List<DaoFileType>
{
DaoFileType.Xml,
},
DebounceMilliseconds = 800,
FileReadRetryCount = 3
});
container.RegisterdaoSingletonFactory<IFileMonitor>(provider =>
{
return new FileMonitor
{
};
});
14、任务调度器
TangdaoTaskScheduler.Execute(dao: daoTask =>
{
});
TangdaoTaskScheduler.Execute(daoAsync: daoTask =>
{
});
TangdaoTaskScheduler.Execute(daoAsync => { }, dao => { });
15、Markup的扩展
1、对Combobox进行定制列表
public class ComboboxOptions
{
public static void SetTheme()
{
OptionListExtension.OptionsPool["Accuracy"] = new[] { "X1", "Y1", "X2", "Y2" };
}
}
在启动时自定义设置自己需要的字典
然后当使用Combobox的下拉列表时候,可缓存直接使用,当多个视图都需要使用同一个下拉列表的时候非常方便
xmlns:markup="clr-namespace:IT.Tangdao.Core.Markup;assembly=IT.Tangdao.Core"
<ComboBox x:Name="com" ItemsSource="{markup:OptionList Key=Accuracy}" />
并且你什么都不写的时候我具有缺省默认数据
OptionsPool["Default"] = new[] { "全部", "Load", "Upload" };
如果你不想在程序启动使用代码复用,也可以直接写
<ComboBox ItemsSource="{markup:OptionList Values='X1,Y1,X2,Y2'}" />
2、 Bool-String转换器,可以指定显示结果
常规的bool转string需要在ViewModel设置想要动态改变还需要写触发器,我可以自己指定
<TextBlock Text="{marup:BoolToStringMode Binding={Binding BoolValue}, FalseValue='失败', TrueValue='成功'}" />
3、列表绑定枚举时使用
<daoMarkup:EnumBindSource x:Key="plcType" EnumType="{x:Type shared:PlcType}" />
4、获取当前控件所在的Window实例
<Button
Width="100"
Height="30"
Margin="20,20,20,-100"
HorizontalAlignment="Center"
Background="{StaticResource OceanBlueBrush}"
Command="{s:Action ExecuteCancel}"
CommandParameter="{markup:GetWindow}"
Content="取消"
IsCancel="True" />
6、列表中的按钮绑定
在一般情况下绑定比较复杂
对比如下:
<DataGridTemplateColumn Width="200" Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
Width="70"
Margin="0,0,15,0"
Background="LightGreen"
Command="{markup:AncestorBinding Path=DataContext.UpdateUserCommand}"
CommandParameter="{Binding Id}"
Content="修改"
FontSize="14"
Foreground="Black" />
<Button
Width="70"
Background="Red"
Command="{Binding DataContext.DeleteUserCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"
CommandParameter="{Binding Id}"
Content="删除"
FontSize="14"
Foreground="Black" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
16、路径处理
| 场景 | 用途 | 如何使用 |
|---|---|---|
| 加载本地资源文件(如JSON、图片、配置) | 避免硬编码路径,支持相对解决方案路径 | TangdaoPath.Solution().Combine("Assets", "Icons", "logo.png").Build() |
| 日志文件输出路径 | 确保日志写入到临时目录或指定目录 | TangdaoPath.Temp().Combine("logs", "app.log").Build() |
| 用户导出/保存文件 | 提供默认路径,避免平台差异 | TangdaoPath.GetEnvironmentDirectory("EXPORT_DIR").Combine("report.xlsx") |
| 插件加载路径 | 动态加载插件DLL,避免路径错误 | TangdaoPath.Solution().Combine("Plugins", "PluginA.dll").Build() |
| 配置文件路径 | 支持CI/CD中配置路径注入 | TangdaoPath.GetThisFilePath().Parent().Combine("settings.json") |
| 单元测试中模拟路径 | 避免使用真实文件系统 | 使用 AbsolutePath 和 RelativePath 构造虚拟路径,不依赖磁盘 |
1、WPF中加载图片资源
// ViewModel 或代码背后
var imagePath = TangdaoPath
.Solution()
.Combine("Assets", "Images", "user_avatar.png")
.Build();
if (imagePath.FileExists)
{
AvatarImageSource = new BitmapImage(new Uri(imagePath.Value));
}
2、
1、使用 PathTemplate 动态生成路径
var template = PathTemplate.Create("{Solution}/Exports/{UserId}/{Date}/report.xlsx");
var path = template.Resolve(new
{
Solution = TangdaoPath.Instance.GetSolutionDirectory().Value,
UserId = 12345,
Date = DateTime.Now.ToString("yyyy-MM-dd")
});
// 输出:C:\Projects\MySolution\Exports\12345\2025-10-10\report.xlsx
2、 邮件 / 短信模板
var mailTmpl = PathTemplate.Create(
"亲爱的 {UserName},您的验证码是 {Code},{Expire} 分钟内有效。");
var body = mailTmpl.Resolve(new { UserName = "张三", Code = "628491", Expire = "5" });
Console.WriteLine(body);
// 输出:亲爱的 张三,您的验证码是 628491,5 分钟内有效。
3、代码片段生成器(T4 替代)
var propTmpl = PathTemplate.Create("public {Type} {Name} { get; set; }");
var props = new[] { new { Type = "string", Name = "FirstName" },
new { Type = "int", Name = "Age" } };
foreach (var p in props)
Console.WriteLine(propTmpl.Resolve(p));
// 输出:
// public string FirstName { get; set; }
// public int Age { get; set; }
3、缓存解决方案目录,避免重复查找
// 全局初始化一次
var solutionDir = TangdaoPath.GetSolutionDirectory();
// 后续使用
var configPath = solutionDir.Combine("config", "appSettings.json");
var dbPath = solutionDir.Combine("data", "local.db");
4、清理临时文件
var tempDir = TangdaoPath.GetTempDirectory().Combine("MyApp");
if (tempDir.DirectoryExists)
{
Directory.Delete(tempDir.Value, true);
}
5、文件导出
public void Export()
{
try
{
var template = PathTemplate.Create("{Solution}/Exports/{Date}/report.txt");
var exportPath = template.Resolve(new
{
Solution = TangdaoPath.GetSolutionDirectory().Value,
Date = DateTime.Now.ToString("yyyy-MM-dd")
});
Directory.CreateDirectory(exportPath.Parent().Value);
File.WriteAllText(exportPath.Value, $"Hello Stylet! Exported at {DateTime.Now}");
}
catch (Exception ex)
{
}
}
6、创建备份
// 使用 Combine 构建路径
AbsolutePath sourcePath = TangdaoPath
.GetThisFilePath()
.Parent() // 移除文件名
.Combine("..") // 上级目录
.Combine("Models") // Models 文件夹
.Combine("User.cs"); // 具体文件
// 使用 Backup 创建备份
var backup = sourcePath.Backup(".bak");
17、自定义排序
带后期增加接口优化
List<Student> students = new List<Student>
{
new Student { Id=4, Name = "张三1111", Education = "一本" },
new Student { Id=5, Name = "李四2222", Education = "大专" },
new Student { Id=2, Name = "王五3333", Education = "研究生" },
new Student { Id=1, Name = "张三4444", Education = "一本" },
new Student { Id=0, Name = "李四5555", Education = "大专" },
new Student { Id=6, Name = "王五6666", Education = "研究生" },
};
var priority = new Dictionary<string, int>
{
["大专"] = 1,
["一本"] = 2,
["研究生"] = 3
};
var comparer = TangdaoSortProvider.Priority<Student>(s => s.Education, priority);
18、特性
AutoRegisterAttribute:自动注册特性
AutoWireViewAttribute:对ViewModel标注特性,自动关联View和ViewModel
[AutoRegister(Mode = RegisterMode.Singleton, Order = 6)]
[AutoWireView]
public class TaskViewModel : DaoViewModelBase{}
注册的时候,可以指定注册的模式和顺序,数字越小,最先注册
19、弱引用TangdaoWeakEvent
1、MessageReceived处理普通事件
TangdaoWeakEvent.Instance.OnMessageReceived += (sender, obj) => { };
TangdaoWeakEvent.Instance.Publish("Open", Message);
2、OnKeyMessageReceived 处理带Key的事件
TangdaoWeakEvent.Instance.OnKeyMessageReceived += (sender, obj) => { };
TangdaoWeakEvent.Instance.Publish("Open", KeyMessage);
3、OnHandlerTableReceived 处理事件列表
TangdaoWeakEvent.Instance.OnHandlerTableReceived += (sender, obj) => { };
TangdaoWeakEvent.Instance.Publish("Open", HandlerTable);
20、观察者模式事件通知TangdaoMessenger
当时有内部框架的IOC时,不需要外部委托SetResolver,当使用其他的IOC时需要通过SetResolver来解析
TangdaoMessenger.SetResolver(reg => Container.Get(reg.RegisterType) as INoticeObserver);
注册的不同写法:
1、
TangdaoMessenger.Instance.ChainRegister(typeof(LightViewModel), typeof(ElectViewModel), typeof(PressureViewModel));
2、
TangdaoMessenger.Instance.ChainRegister().Add(typeof(LightViewModel)).Add(typeof(ElectViewModel)).Add(typeof(PressureViewModel));
使用场景:
public class ElectViewModel : BaseViewModel, IMessageObserver
{
private string _tag;
public string Tag
{
get => _tag;
set => SetAndNotify(ref _tag, value);
}
public void UpdateNotice(NoticeContext context)
{
Tag = context.Tag;
}
}
一键通知:
MessengerContext Context = new MessengerContext();
Context.CurrentTime = DateTime.Now;
Context.Tag = "数据改变了";
TangdaoMessenger.Instance.NotifyAllObservers(Context);
单独通知:
MessengerContext Context = new MessengerContext();
Context.CurrentTime = DateTime.Now;
Context.Tag = "单独数据改变";
TangdaoMessenger.Instance.NotifyObserverByKey("LightViewModel", Context);
注销通知:
TangdaoMessenger.Instance.Unregister("Key");
注销所有通知:
TangdaoMessenger.Instance.Unregister();
21、委托表传输机制IActionTable
1、使用时需要保持单例唯一性,这里跟据你使用的IOC决定注册
container.RegisterdaoSingleton<IActionTable, ActionTable>();
2、带参数使用
VM1:
public void TestWindow()
{
_actionTable.Register("Open", x =>
{
if (x.Result == ActionStatus.Success)
{
MessageBox.Show("成功返回");
}
});
}
VM2:
public void Open()
{
ActionResult result = new ActionResult();
result.Result = ActionStatus.Success;
_handlerTable.GetResultHandler("Open").Invoke(result);
}
3、不带参数使用
VM1:
public void TestWindow()
{
_actionTable.Register("Open", () =>
{
});
}
VM2:
public void Open()
{
_handlerTable.GetHandler("Open").Invoke();
}
22、Window管道
public override void ConfigureWindowPipe(IWindowBuilder windowBuilder)
{
windowBuilder.UseGuard<LoginSignGuard>(rules =>
{
rules.UseLogin<LoginView>();
rules.SetCancel(true);
});
}
重大迁移通知
| 旧项 | 新项 |
|---|---|
DaoViewModelBase |
ViewModelBase |
IDaoEventAggregator |
IEventAggregator |
DaoEventBase |
EventBase |
IT.Tangdao.Framework.DaoExtension |
IT.Tangdao.Framework.Extensions |
IT.Tangdao.Framework.EventArs |
IT.Tangdao.Framework.Events |
IT.Tangdao.Core.DaoExtension |
IT.Tangdao.Core.Extensions |
IT.Tangdao.Core.EventArs |
IT.Tangdao.Core.Events |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net46 is compatible. net461 is compatible. net462 was computed. net463 was computed. net47 is compatible. net471 is compatible. net472 is compatible. net48 is compatible. net481 was computed. |
-
.NETFramework 4.6
- Microsoft.CSharp (>= 4.7.0)
- Newtonsoft.Json (>= 13.0.4)
- System.ValueTuple (>= 4.5.0)
-
.NETFramework 4.6.1
- Microsoft.CSharp (>= 4.7.0)
- Newtonsoft.Json (>= 13.0.4)
- System.ValueTuple (>= 4.5.0)
-
.NETFramework 4.7
- Microsoft.CSharp (>= 4.7.0)
- Newtonsoft.Json (>= 13.0.4)
- System.ValueTuple (>= 4.5.0)
-
.NETFramework 4.7.1
- Microsoft.CSharp (>= 4.7.0)
- Newtonsoft.Json (>= 13.0.4)
- System.ValueTuple (>= 4.5.0)
-
.NETFramework 4.7.2
- Microsoft.CSharp (>= 4.7.0)
- Newtonsoft.Json (>= 13.0.4)
- System.ValueTuple (>= 4.5.0)
-
.NETFramework 4.8
- Microsoft.CSharp (>= 4.7.0)
- Newtonsoft.Json (>= 13.0.4)
- System.ValueTuple (>= 4.5.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.1.0 | 79 | 9/28/2026 |
| 5.0.16 | 120 | 6/19/2026 |
| 5.0.15 | 111 | 6/3/2026 |
| 5.0.14 | 108 | 6/2/2026 |
| 5.0.13 | 102 | 5/31/2026 |
| 5.0.12 | 140 | 5/12/2026 |
| 5.0.10 | 108 | 5/8/2026 |
| 5.0.9 | 94 | 5/7/2026 |
| 5.0.8 | 96 | 5/5/2026 |
| 5.0.7 | 98 | 4/30/2026 |
| 5.0.6 | 111 | 4/16/2026 |
| 5.0.5 | 114 | 4/14/2026 |
| 5.0.4 | 109 | 4/11/2026 |
| 5.0.3 | 104 | 4/8/2026 |
| 5.0.2 | 103 | 4/7/2026 |
| 5.0.1 | 104 | 4/6/2026 |
| 5.0.0 | 113 | 3/31/2026 |
重构命名空间