![]() |
VOOZH | about |
dotnet add package HansCnc.Mvvm --version 0.3.0-preview1
NuGet\Install-Package HansCnc.Mvvm -Version 0.3.0-preview1
<PackageReference Include="HansCnc.Mvvm" Version="0.3.0-preview1" />
<PackageVersion Include="HansCnc.Mvvm" Version="0.3.0-preview1" />Directory.Packages.props
<PackageReference Include="HansCnc.Mvvm" />Project file
paket add HansCnc.Mvvm --version 0.3.0-preview1
#r "nuget: HansCnc.Mvvm, 0.3.0-preview1"
#:package HansCnc.Mvvm@0.3.0-preview1
#addin nuget:?package=HansCnc.Mvvm&version=0.3.0-preview1&prereleaseInstall as a Cake Addin
#tool nuget:?package=HansCnc.Mvvm&version=0.3.0-preview1&prereleaseInstall as a Cake Tool
轻量级 WPF MVVM 框架:Roslyn 源生成器 + 对话框服务,基于 CommunityToolkit.Mvvm、Autofac、R3。
开发环境:Git 仓库根目录为
HansCnc.Mvvm/(本目录)。若 IDE 工作区打开的是外层文件夹,请在终端中cd到本目录再执行git/dotnet。详见 与 。
| 项目 | 说明 |
|---|---|
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) |
<PackageReference Include="HansCnc.Mvvm" Version="0.3.0-preview1" />
<PackageReference Include="HansCnc.Mvvm.WPF" Version="0.3.0-preview1" />
完整版说明(HTML,随发布更新):。全量变更记录:。
HansCnc.Mvvm 已包含 Roslyn 分析器(analyzers/dotnet/roslyn*/cs),无需再引用 HansCnc.Mvvm.SourceGenerators。
WPF 应用还需 UseWPF、Autofac、R3 等(见下方依赖)。从旧版三件套迁移见 。
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 |
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()。
[IViewFor<HomeViewModel>]
public partial class HomeView : UserControl
{
public HomeView()
{
this.WhenActivated(disposables =>
{
// 视图激活时的订阅
});
}
}
当 ViewModel 实现 IActivatableViewModel 时,WhenActivated 会在首次激活/全部停用时调用 HandleActivation / HandleDeactivation。
MainThreadHelper.Invoke(() => { /* UI 线程同步 */ });
await MainThreadHelper.InvokeAsync(() => { /* UI 线程异步 */ });
MainThreadHelper.BeginInvoke(() => { /* 投递到队列,避免重入 */ });
MainThreadHelper.DoEvents(); // 长同步操作中偶尔泵送消息
MainThreadHelper.VerifyAccess(); // 断言当前在 UI 线程
属性 UiDispatcher 暴露缓存的 WPF 调度器(避免与 System.Windows.Threading.Dispatcher 类型同名冲突)。
应用启动时配置 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 迁移见 。
| 文件 | 说明 |
|---|---|
| 当前发布版本的完整使用文档(单文件 HTML) | |
| 全部历史版本变更记录 | |
| 发布时如何同步更新上述两份文档 |
.\build.ps1
dotnet build HansCnc.Mvvm.slnx -c Release
dotnet test HansCnc.Mvvm.slnx -c Release --no-build
WPF 与集成测试需在 Windows 上运行。升级 MvvmAIO.R3.SourceGenerators 见 。
在 0.1.0 之前仍可能调整,但以下面为对外承诺的主契约:IViewFor / [IViewFor]、[DialogViewModel]、DialogViewModelBase、IDialogService、DialogService、WhenActivated、MainThreadHelper、MvvmDialogResult<T>、诊断 HMVVM0001–0005。详见 与 。
DependencyProperty 样板代码DialogViewModelBase 分工DoEventsHansCnc.Mvvm.WPF 全 TFM 引用,最低 net472;应用启动建议 WpfProviderInitializer)HansCnc.Mvvm.WPF 内部使用;应用侧同样可用)HansCnc.Mvvm.WPF 可选)| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 is compatible. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 1 NuGet packages that depend on HansCnc.Mvvm:
| Package | Downloads |
|---|---|
|
HansCnc.Mvvm.WPF
轻量级 WPF MVVM 框架 WPF 实现层。提供 DialogService、ContainerBuilderExtensions、ActivationMixins 和 DialogViewModelBase,基于 Autofac + R3 + CommunityToolkit.Mvvm。 |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.3.0-preview1 | 54 | 6/4/2026 |
| 0.2.3-preview1 | 54 | 6/3/2026 |
| 0.2.2-preview1 | 55 | 6/2/2026 |
| 0.2.1-preview1 | 67 | 5/28/2026 |
| 0.2.0-preview1 | 57 | 5/28/2026 |
| 0.1.0-preview4 | 62 | 5/27/2026 |
| 0.1.0-preview3 | 54 | 5/27/2026 |
| 0.1.0-preview2 | 56 | 5/27/2026 |
| 0.1.0-preview1 | 63 | 5/27/2026 |
| 0.0.2-preview1 | 57 | 5/26/2026 |
| 0.0.1-preview7 | 56 | 5/21/2026 |
| 0.0.1-preview6 | 57 | 5/18/2026 |
| 0.0.1-preview5 | 51 | 5/18/2026 |
| 0.0.1-preview4 | 55 | 5/17/2026 |
| 0.0.1-preview3 | 50 | 5/17/2026 |
| 0.0.1-preview2 | 51 | 5/17/2026 |
| 0.0.1-preview1 | 66 | 5/17/2026 |