{"id":17917426,"url":"https://github.com/pellared/mvvmutils","last_synced_at":"2025-09-09T05:41:41.997Z","repository":{"id":9984100,"uuid":"12012909","full_name":"pellared/MvvmUtils","owner":"pellared","description":"Utilities and samples for Model-View-ViewModel design. Only WPF supported.","archived":false,"fork":false,"pushed_at":"2021-02-08T16:14:03.000Z","size":31822,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-15T09:39:22.358Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Pellared.MvvmUtils","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pellared.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-08-09T23:55:45.000Z","updated_at":"2023-02-06T01:39:30.000Z","dependencies_parsed_at":"2022-09-22T06:20:32.059Z","dependency_job_id":null,"html_url":"https://github.com/pellared/MvvmUtils","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pellared/MvvmUtils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pellared%2FMvvmUtils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pellared%2FMvvmUtils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pellared%2FMvvmUtils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pellared%2FMvvmUtils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pellared","download_url":"https://codeload.github.com/pellared/MvvmUtils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pellared%2FMvvmUtils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274250247,"owners_count":25249395,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-09T02:00:10.223Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-28T20:07:11.307Z","updated_at":"2025-09-09T05:41:41.937Z","avatar_url":"https://github.com/pellared.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pellared.MvvmUtils\n\n\u003e MVVM Utils is a set of classes which extends existing MVVM Frameworks such as MVVM Light, Prism, Cinch. Only WPF supported.\n\n[![Nuget](https://img.shields.io/nuget/v/Pellared.MvvmUtils)](https://www.nuget.org/packages/Pellared.MvvmUtils)\n\n## Source code description\n\n* **MvvmLightSample** - a little WPF MvvmLight application which uses MvvmUtils main features\n* **MvvmUtils**\n  * **Pellared.Common** - small, but universal utlility library (you can check the code by your own)\n  * **MvvmUtils** - **MVVM Utils library**\n  * **SalaryBook** - a sample application where I test the MVVM Utils (uses MVVM Light, but with ViewModel first approach and FluentValidation)\n\n## Features\n\n* [**Validation**](#validation) - cutomizable error and validation management by using ErrorContainer, DataErrorInfoProvider and ValidationProvider\n* **DialogService** and [**WindowService**](#windowservice) - UI interaction code without any reference to the View layer\n* **UiDispatcher** - UI dispatcher using SynchronizationContext\n* **ConcurrentObservableCollection** - dispatched thread-safe observable collection\n* **PropetyObserver** - strongly typed property observer on INotifyPropertyChanged\n\n### Validation \n\nMVVM Utils provides a set of classes that helps implementing validation.\n\n* **ErrorContainer** stores property errors\n* **DataErrorInfoProvider** exposes errors taken from IErrorContainer via IDataErrorInfo and INotifyDataErrorInfo\nBy combining these classes you can easily create your own ValidatiableViewModel that can support your requirements.\n\nBelow there is a sample ValidatiableViewModel inherting MVVM Light's ViewModelBase, which fires validation after invoking RaisePropertyChanged. When the validation sets errors, then the ErrorContainer executes OnErrorsChanged, which also invokes RaisePropertyChanged for notifying the view about the errors.\n\n```csharp\npublic abstract class ValidatableViewModel : ViewModelBase, IDataErrorInfo\n{\n\tpublic const string ObjectErrorPropertyName = \"\";\n\n\tprivate readonly DataErrorInfoProvider dataErrorInfoProvider;\n\n\tprotected ValidatableViewModel(ArrayFormat errorFormat = ArrayFormat.First)\n\t{\n\t\tErrorsContainer = new ErrorsContainer();\n\t\tErrorsContainer.ErrorsChanged += OnErrorsChanged;\n\n\t\tdataErrorInfoProvider = new DataErrorInfoProvider(ErrorsContainer, errorFormat, ObjectErrorPropertyName);\n\t}\n\n\tprotected ValidatableViewModel(IMessenger messenger, ArrayFormat errorFormat = ArrayFormat.First)\n\t\t: base(messenger)\n\t{\n\t\tErrorsContainer = new ErrorsContainer();\n\t\tErrorsContainer.ErrorsChanged += OnErrorsChanged;\n\n\t\tdataErrorInfoProvider = new DataErrorInfoProvider(ErrorsContainer, errorFormat, ObjectErrorPropertyName);\n\t}\n\n\tprotected ValidatableViewModel(IErrorsContainer\u003cValidationError\u003e errorsContainer, ArrayFormat errorFormat = ArrayFormat.First)\n\t{\n\t\tErrorsContainer = errorsContainer;\n\t\tErrorsContainer.ErrorsChanged += OnErrorsChanged;\n\n\t\tdataErrorInfoProvider = new DataErrorInfoProvider(ErrorsContainer, errorFormat, ObjectErrorPropertyName);\n\t}\n\n\tpublic IErrorsContainer\u003cValidationError\u003e ErrorsContainer { get; private set; }\n\n\tpublic virtual bool HasErrors\n\t{\n\t\tget { return ErrorsContainer.HasErrors; }\n\t}\n\n\tpublic virtual string Error\n\t{\n\t\tget { return dataErrorInfoProvider.Error; }\n\t}\n\n\tpublic virtual string this[string columnName](string-columnName)\n\t{\n\t\tget {\treturn dataErrorInfoProvider[columnName](columnName); }\n\t}\n\n\tpublic void Validate()\n\t{\n\t\tIEnumerable\u003cValidationError\u003e errors = Validation();\n\t\tErrorsContainer.ClearAndSetErrors(errors);\n\t}\n\n\tprotected abstract IEnumerable\u003cValidationError\u003e Validation();\n\n\tprotected override void RaisePropertyChanged\u003cT\u003e(Expression\u003cFunc\u003cT\u003e\u003e propertyExpression)\n\t{\n\t\tbase.RaisePropertyChanged(propertyExpression);\n\t\tValidate();\n\t}\n\n\tprotected override void RaisePropertyChanged\u003cT\u003e(Expression\u003cFunc\u003cT\u003e\u003e propertyExpression, T oldValue, T newValue, bool broadcast)\n\t{\n\t\tbase.RaisePropertyChanged(propertyExpression, oldValue, newValue, broadcast);\n\t\tValidate();\n\t}\n\n\tprotected override void RaisePropertyChanged\u003cT\u003e(string propertyName, T oldValue, T newValue, bool broadcast)\n\t{\n\t\tbase.RaisePropertyChanged(propertyName, oldValue, newValue, broadcast);\n\t\tValidate();\n\t}\n\n\tprotected override void RaisePropertyChanged(string propertyName)\n\t{\n\t\tbase.RaisePropertyChanged(propertyName);\n\t\tValidate();\n\t}\n\n\tprivate void OnErrorsChanged(object sender, DataErrorsChangedEventArgs e)\n\t{\n\t\tOnErrorsChanged(e.PropertyName);\n\t}\n\n\tprivate void OnErrorsChanged(string propertyName)\n\t{\n\t\tif (propertyName == ObjectErrorPropertyName)\n\t\t{\n\t\t\t// object error\n\t\t\tpropertyName = \"Error\";\n\t\t}\n\n\t\t// notify for IDataErrorInfo\n\t\tbase.RaisePropertyChanged(propertyName);\n\t\tbase.RaisePropertyChanged(\"HasErrors\");\n\t}\n}\n```\n\nAnd here is a sample ViewModel that supports validation:\n\n```csharp\npublic sealed class PersonViewModel : ValidatableViewModel\n{\n\tpublic PersonViewModel()\n\t{\n\t\t// validate on creation\n\t\tValidate();\n\t}\n\tprivate string _name;\n\tpublic string Name\n\t{\n\t\tget { return _name; }\n\t\tset { Set(() =\u003e Name, ref _name, value); }\n\t}\n\tprotected override IEnumerable\u003cValidationError\u003e Validation()\n\t{\n\t\tif (string.IsNullOrWhiteSpace(Name))\n\t\t{\n\t\t\treturn new []() { ValidationError.Create(() =\u003e Name, \"Name cannot be empty\") };\n\t\t}\n\t\treturn null;\n\t}\n}\n```\n\n### WindowService\n\nTo use a custom dialog in a lously coupled way, only a few steps have to be done.\n\nCreate a ViewModel for the dialog implementing IWindowViewModel:\n\n```csharp\npublic class AddedViewModel : IWindowViewModel\n{\n\tpublic bool Closed { get; set; }\n\n\tpublic string Title\n\t{\n\t\tget { return \"Added!\"; }\n\t}\n\n\tpublic string Message\n\t{\n\t\tget { return \"A person has been added to the table.\"; }\n\t}\n}\n```\n\n**Remark**: Setting Closed property to true closes the dialog.\n\nCreate a View for the dialog as a UserControl:\n\n```xml\n\u003cUserControl x:Class=\"MvvmLightSample.AddedView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:vm=\"clr-namespace:MvvmLightSample.ViewModel\"\n             mc:Ignorable=\"d\"\n             d:DesignHeight=\"20\" d:DesignWidth=\"300\"\n             d:DataContext=\"{d:DesignInstance vm:AddedViewModel, IsDesignTimeCreatable=True}\"\u003e\n    \u003cGrid\u003e\n        \u003cTextBlock Text=\"{Binding Message}\" /\u003e\n    \u003c/Grid\u003e\n\u003c/UserControl\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpellared%2Fmvvmutils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpellared%2Fmvvmutils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpellared%2Fmvvmutils/lists"}