HansCnc.DataCollection
0.3.0-preview1
dotnet add package HansCnc.DataCollection --version 0.3.0-preview1
NuGet\Install-Package HansCnc.DataCollection -Version 0.3.0-preview1
<PackageReference Include="HansCnc.DataCollection" Version="0.3.0-preview1" />
<PackageVersion Include="HansCnc.DataCollection" Version="0.3.0-preview1" />
<PackageReference Include="HansCnc.DataCollection" />
paket add HansCnc.DataCollection --version 0.3.0-preview1
#r "nuget: HansCnc.DataCollection, 0.3.0-preview1"
#:package HansCnc.DataCollection@0.3.0-preview1
#addin nuget:?package=HansCnc.DataCollection&version=0.3.0-preview1&prerelease
#tool nuget:?package=HansCnc.DataCollection&version=0.3.0-preview1&prerelease
HansCnc.Mvvm
轻量级 WPF MVVM 框架:Roslyn 源生成器 + 对话框服务,基于 CommunityToolkit.Mvvm、Autofac、R3。
开发环境:Git 仓库根目录为
HansCnc.Mvvm/(本目录)。若 IDE 工作区打开的是外层文件夹,请在终端中cd到本目录再执行git/dotnet。详见 CONTRIBUTING.md 与 AGENTS.md。
项目结构
| 项目 | 说明 |
|---|---|
HansCnc.Core |
领域标记接口(IModel / IDto / IEntity 等),NuGet HansCnc.Core |
HansCnc.DataCollection |
数据采集运行时 + NotifyList<T>,NuGet HansCnc.DataCollection |
HansCnc.Mvvm |
MVVM 核心 + 内置 Roslyn 源生成器(含 DataCollection 节点/根生成器) |
HansCnc.Mvvm.WPF |
WPF 实现:DialogService、DialogViewModelBase、WhenActivated、MainThreadHelper |
HansCnc.Mvvm.SourceGenerators |
共享生成器源码(仓库内;已打包进 HansCnc.Mvvm NuGet) |
HansCnc.Mvvm.SourceGenerators.Package |
历史打包布局参考(不再单独发布) |
HansCnc.Mvvm.Samples |
示例 WPF 应用 |
HansCnc.Mvvm.*.Tests |
单元 / 集成测试(含 HansCnc.Mvvm.Integration.Tests) |
安装
NuGet(应用项目)
<PackageReference Include="HansCnc.Mvvm" Version="0.3.0-preview1" />
<PackageReference Include="HansCnc.Mvvm.WPF" Version="0.3.0-preview1" />
完整版说明(HTML,随发布更新):docs/DOCUMENTATION.html。全量变更记录:docs/CHANGELOG-HISTORY.md。
HansCnc.Mvvm 已包含 Roslyn 分析器(analyzers/dotnet/roslyn*/cs),无需再引用 HansCnc.Mvvm.SourceGenerators。
WPF 应用还需 UseWPF、Autofac、R3 等(见下方依赖)。从旧版三件套迁移见 MIGRATION.md。
本地仓库开发(示例 / 贡献者)
HansCnc.Mvvm.Samples 引用生成器项目以便调试:
<ProjectReference Include="..\HansCnc.Mvvm.SourceGenerators.Roslyn4120\HansCnc.Mvvm.SourceGenerators.Roslyn4120.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\HansCnc.Mvvm\HansCnc.Mvvm.csproj" />
<ProjectReference Include="..\HansCnc.Mvvm.WPF\HansCnc.Mvvm.WPF.csproj" />
消费方应用使用 NuGet 四包(HansCnc.Core、HansCnc.DataCollection、HansCnc.Mvvm、HansCnc.Mvvm.WPF;WPF 应用通常显式引用后两者,DataCollection 可由 HansCnc.Mvvm 传递依赖)。启用实时数据采集时导入 HansCnc.Mvvm.DataCollection.props(来自 HansCnc.DataCollection 包)。仅在修改生成器本身时使用 ProjectReference + OutputItemType="Analyzer"。
集合类型
| 类型 | 程序集 / 命名空间 | 通知 | 典型用途 |
|---|---|---|---|
NotifyList<T> |
HansCnc.DataCollection |
自定义 CollectionChanged(细粒度) |
ViewModel 内逻辑集合 |
ObservableDictionary<TKey, TValue> |
HansCnc.Mvvm / HansCnc.Collections |
BCL INotifyCollectionChanged + INotifyPropertyChanged |
WPF XAML 绑定 |
ObservableDictionary 与 NotifyList 不同,专为 WPF 绑定契约设计。在 UI 线程上修改;SyncRoot 提供线程安全,不替代 Dispatcher。
public ObservableDictionary<string, string> Items { get; } = new();
<ItemsControl ItemsSource="{Binding Items}" />
<TextBlock Text="{Binding Items[TitleKey]}" />
源生成器
[IViewFor<TViewModel>]
为 WPF 视图(partial 类)生成 IViewFor<TViewModel> 实现:
[IViewFor<HomeViewModel>]
public partial class HomeView : UserControl { }
生成内容:ViewModelProperty 依赖属性、ViewModel CLR 属性、BindingRoot 便捷属性。
| ID | 说明 |
|---|---|
| HMVVM0001 | [IViewFor] 标记的类必须为 partial(支持 Code Fix:添加 partial) |
| HMVVM0002 | [DialogViewModel] 不得显式声明基类 |
| HMVVM0003 | [DialogViewModel] 标记的类必须为 partial(支持 Code Fix) |
| HMVVM0004 | [IViewFor] 中的 ViewModel 类型无法解析 |
| HMVVM0005 | 同一类型上存在多个 [IViewFor](例如多个 partial 声明各带属性) |
[EditableModel] / [DirtyPart] / [DirtyCollectionPart]
为可编辑实体(partial + ObservableObject)生成 IEditableModel 脏跟踪成员:DirtyContext、DirtyCount、IsDirty、SetDirty、ClearDirty、RaiseDirtyChanged。嵌套图用 [DirtyPart](引用)与 [DirtyCollectionPart](IEnumerable<T>,T 须实现 IEditableModel 或标记 [EditableModel])。
[EditableModel]
public partial class Article : ObservableObject
{
[DirtyPart]
public Author Author { get; set; } = new();
[DirtyCollectionPart]
public List<Tag> Tags { get; set; } = [];
}
脏标记由业务代码显式调用 SetDirty(生成器不会挂钩属性 setter)。示例见 HansCnc.Mvvm.Samples/EditableModels/。
| ID | 说明 |
|---|---|
| HMVVM0006 | [EditableModel] 标记的类必须为 partial(支持 Code Fix) |
| HMVVM0007 | [EditableModel] 类型必须继承 ObservableObject |
| HMVVM0008 | [DirtyPart] 属性类型须实现 IEditableModel(或带 [EditableModel]) |
| HMVVM0009 | [DirtyCollectionPart] 须为 IEnumerable<T> 且 T 满足同上 |
设计时绑定
在 XAML 根元素绑定到 ViewModel(运行时由 DialogService 或代码赋值):
<UserControl ...
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=ViewModel}"
d:DataContext="{d:DesignInstance Type=vm:HomeViewModel, IsDesignTimeCreatable=True}">
示例见 HansCnc.Mvvm.Samples/Views/。
[DialogViewModel<TInput, TResult>]
为对话框 ViewModel(partial 类、无显式基类)注入基类 DialogViewModelBase<TInput, TResult>:
[DialogViewModel<string, string>]
public partial class MyDialogViewModel
{
public void Confirm()
{
ResultContext = MvvmDialogResult.Ok(UserInput);
Ok();
}
}
生成器只添加基类声明;属性、OkCommand / CancelCommand、生命周期与 IDialogViewModel 成员均由 DialogViewModelBase 提供。
| ID | 说明 |
|---|---|
| HMVVM0002 | 不得显式声明基类 |
| HMVVM0003 | [DialogViewModel] 标记的类必须为 partial |
对话框
注册(Autofac)
var builder = new ContainerBuilder();
builder.UseDialogService();
builder.RegisterDialog<MyDialogViewModel, MyDialogWindow>();
// 约定:FooDialogViewModel → FooDialogWindow(同一程序集)
builder.RegisterDialogsFromAssembly(typeof(MyDialogViewModel).Assembly);
使用
var result = dialogService.ShowDialog<MyDialogViewModel, string, string>("输入值");
if (result.Result)
Console.WriteLine(result.ResultValue);
DialogService 通过 IViewFor<TViewModel>.ViewModel 绑定 ViewModel(不设置 Window.DataContext)。对话框窗口须实现 IViewFor<TViewModel>(推荐 [IViewFor<T>] + partial)。
窗口行为约定
| 主题 | 说明 |
|---|---|
| Owner | 已 Show 的当前活动窗口,或已加载的 MainWindow |
| 居中 | 在 XAML 中设置 WindowStartupLocation="CenterOwner"(见 Samples) |
| ESC / 取消 | Cancel() 或 RequestClose();可配合 IsCancel="True" 按钮 |
| 生命周期 | OnDialogInitialized / Loaded / Closing / Closed;钩子内异常由服务记录日志,不中断关闭流程 |
DialogViewModelBase 提供 Input、ResultContext、Initialize、Ok() / Cancel() 及上述生命周期虚方法。确认前设置 ResultContext,再调用 Ok();取消调用 Cancel()。
视图激活(R3)
[IViewFor<HomeViewModel>]
public partial class HomeView : UserControl
{
public HomeView()
{
this.WhenActivated(disposables =>
{
// 视图激活时的订阅
});
}
}
当 ViewModel 实现 IActivatableViewModel 时,WhenActivated 会在首次激活/全部停用时调用 HandleActivation / HandleDeactivation。
UI 线程(MainThreadHelper)
MainThreadHelper.Invoke(() => { /* UI 线程同步 */ });
await MainThreadHelper.InvokeAsync(() => { /* UI 线程异步 */ });
MainThreadHelper.BeginInvoke(() => { /* 投递到队列,避免重入 */ });
MainThreadHelper.DoEvents(); // 长同步操作中偶尔泵送消息
MainThreadHelper.VerifyAccess(); // 断言当前在 UI 线程
属性 UiDispatcher 暴露缓存的 WPF 调度器(避免与 System.Windows.Threading.Dispatcher 类型同名冲突)。
R3 流与 UI 线程
应用启动时配置 R3 的 WPF 调度(Samples 使用 R3Extensions.WPF):
// App.xaml.cs
WpfProviderInitializer.SetDefaultObservableSystem(
ex => Log.Error(ex, ex.Message),
DispatcherPriority.Background,
Dispatcher);
命令式 UI 用 MainThreadHelper;Observable 流 用 ObserveOnUi / SubscribeOnUi:
this.WhenActivated(d =>
{
ViewModel!.WhenAnyValue(vm => vm.Counter)
.ObserveOnUi()
.Subscribe(c => { /* UI 线程回调 */ })
.DisposeWith(d);
});
// 或把流绑到 ViewModel 属性(无 OAPH):
ViewModel!.WhenAnyValue(vm => vm.Counter)
.SubscribeOnUi(c => viewModel.CounterDescription = $"已计 {c} 次");
WPF 控件事件 → Observable 请使用 MvvmAIO.R3.SourceGenerators(FromEvents / FromRoutedEvents)。属性观察见 WhenAnyMixin;ReactiveUI 迁移见 docs/MIGRATION-REACTIVEUI.md。
文档
| 文件 | 说明 |
|---|---|
docs/DOCUMENTATION.html |
当前发布版本的完整使用文档(单文件 HTML) |
docs/CHANGELOG-HISTORY.md |
全部历史版本变更记录 |
docs/README.md |
发布时如何同步更新上述两份文档 |
构建与测试
.\build.ps1
dotnet build HansCnc.Mvvm.slnx -c Release
dotnet test HansCnc.Mvvm.slnx -c Release --no-build
WPF 与集成测试需在 Windows 上运行。升级 MvvmAIO.R3.SourceGenerators 见 docs/DEPENDENCIES.md。
稳定 API(预览期)
在 0.1.0 之前仍可能调整,但以下面为对外承诺的主契约:IViewFor / [IViewFor]、[DialogViewModel]、DialogViewModelBase、IDialogService、DialogService、WhenActivated、MainThreadHelper、MvvmDialogResult<T>、诊断 HMVVM0001–0005。详见 CONTRIBUTING.md 与 docs/MIGRATION.md。
核心特性
- IViewFor 源生成器 — 消除
DependencyProperty样板代码 - DialogViewModel 源生成器 — 属性标记 +
DialogViewModelBase分工 - DialogService — 类型安全的模态/非模态对话框
- WhenActivated — 基于 R3 的视图生命周期
- MainThreadHelper — UI 线程封送与
DoEvents - 多 Roslyn 版本 — 4.0.1 / 4.3.1 / 4.12.0 / 5.0.0
依赖
- CommunityToolkit.Mvvm — MVVM 基元
- Autofac — IoC 容器
- R3 — 响应式扩展
- R3Extensions.WPF — WPF 调度(
HansCnc.Mvvm.WPF全 TFM 引用,最低 net472;应用启动建议WpfProviderInitializer) - MvvmAIO.R3.SourceGenerators — WPF 事件源生成(
HansCnc.Mvvm.WPF内部使用;应用侧同样可用) - Serilog — 日志(
HansCnc.Mvvm.WPF可选)
许可证
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- HansCnc.Core (>= 0.3.0-preview1)
- MessagePack (>= 3.1.6)
-
.NETStandard 2.1
- HansCnc.Core (>= 0.3.0-preview1)
- MessagePack (>= 3.1.6)
-
net10.0
- HansCnc.Core (>= 0.3.0-preview1)
- MessagePack (>= 3.1.6)
-
net8.0
- HansCnc.Core (>= 0.3.0-preview1)
- MessagePack (>= 3.1.6)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on HansCnc.DataCollection:
| Package | Downloads |
|---|---|
|
HansCnc.Mvvm
轻量级 WPF MVVM 框架核心库。提供 IViewFor<T>、IActivatableViewModel、IDialogViewModel、IEditableModel 抽象,并内置 Roslyn 源生成器(IViewFor / DialogViewModel / EditableModel,诊断 HMVVM0001–0009)。 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.3.0-preview1 | 60 | 6/4/2026 |