From 1345e3d95adbd145956fedfec0d20f1bcb8bbc6d Mon Sep 17 00:00:00 2001 From: Brooks Vaughn <18422308+BrooksV@users.noreply.github.com> Date: Sun, 9 Feb 2025 22:59:22 -0500 Subject: [PATCH 1/5] Create branch and updated versions for repository only change --- .gitignore | 2 +- README.md | 3 ++- tests/TestDatabase1.mdf | Bin 3342336 -> 3342336 bytes tests/TestDatabase1_log.ldf | Bin 786432 -> 786432 bytes 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9e57944..6e520ff 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ run.ps1 # Root Level Files *.readinglist.md *.cheatsheet.md -*_cheatsheet.md +*.cheatsheet.md.ps1 reflog.md # Ignore build output directories diff --git a/README.md b/README.md index 0a85ffa..f22fbae 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,13 @@ The SqlQueryClass module provides a set of functions and cmdlets for working wit Name | Version | PS Compatibility | Project Uri (GitHub) ------------- | ------- | ---------------- | ------------------------------------------------------------------------------------ -SqlQueryClass | 0.1.2 | 5.1 | [https://github.com/BrooksV/SqlQueryClass](https://github.com/BrooksV/SqlQueryClass) +SqlQueryClass | 0.1.3 | 5.1 | [https://github.com/BrooksV/SqlQueryClass](https://github.com/BrooksV/SqlQueryClass) [PSGalleryLink]: https://www.powershellgallery.com/packages/SqlQueryClass/ [BadgeIOCount]: https://img.shields.io/powershellgallery/dt/SqlQueryClass.svg?label=downoads%20SqlQueryClass%40PSGallery [WorkFlowStatus]: https://img.shields.io/github/actions/workflow/status/BrooksV/SqlQueryClass/tests.yml?label=tests.yml%20build +![PowerShell Gallery Version](https://img.shields.io/powershellgallery/v/SqlQueryClass.svg) [![maintainer](https://img.shields.io/badge/maintainer-BrooksV-orange)](https://github.com/BrooksV) [![License](https://img.shields.io/github/license/BrooksV/SqlQueryClass)](https://github.com/BrooksV/SqlQueryClass/blob/main/LICENSE) [![contributors](https://img.shields.io/github/contributors/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/graphs/contributors/) diff --git a/tests/TestDatabase1.mdf b/tests/TestDatabase1.mdf index 5db30b6a1a4888d7739f7af1699f7ff7aeb7ff71..e0329d4e8fc148a0fc7e421ab6e2c90d4bc8b12b 100644 GIT binary patch delta 263 zcmXxaISztg6aZl!uDCBK?(pNTxSoU)Sa=dT4`5+uZEIp;O>8}b3B8AKD4gW$CJ5dV z#2(@~rmtz5;NSKBy`96C`#d^w5b-ae63d7$9l2Aa1UeE(B84A9*36%2AB1$Nu yf+}jL!$bp3SZJY*4!Y=}j{$5rP;fEC2xCkz#SC*Su!M&d*4SW+-7`{q`||^sWnnx3 diff --git a/tests/TestDatabase1_log.ldf b/tests/TestDatabase1_log.ldf index 0da2156e856dd747eaa41f78f440cefb5c1980f8..280d6d131e716832cc87b983ebe91e30d7d08227 100644 GIT binary patch delta 1374 zcmb`HPiPZC6vk&Z6O-M>WkE@lmL^iPRXhYeSrKdujix49111L#?LnxDrb&_FNmE+Z zo6Il*`d_ zIl4M5eF|11L+rip?|^x1Hu7TTCadoR&rzB*wLnNt5<(;hxgHJt_EMb>>h_=lgRfY< zsX%)FncYGrtFP}&Q+_koALgYkZK)G}+zC?%3BuFpgqc*R-Dg@L0cI3p>tp(Cbs}hO zUZ5(Kt%)=X9i9Gsi#~cu$TrNoFWD-^SY8%OB_dlNAln;<6x#S1Vtw zXm-^$yQ>!yV0Lna@7Y08>~XETXDnIFsTTbzh0N&eq`A60R@MED_B`sPY`xc>%zu;= zfl`y*<|xhalM>o^R|HC8sRUm+O1$wlN@-j5oj>M62^SzrijC4!7fRT52PJ1u++U!y zw--k#V|%H`OD>eU3%J)JacaSiq&P}*W@+<~wLHn)OzplM&KM{(nH4TjP2MDBVoPWx=eamw0eq&V-w$@w!A8(iz| o8Jj2OcHvZSw~kPw!i|cro@=6l4D$`Ja6IKY2zV zW&&d7?f>LinDW||v9v8?ZCl2+Y#F;1LJRF!K*xl%Jz#Hpz|r=Av+V)bvIpD>0Bulh Ae*gdg From 6cc608ca521679d08e4e97d3884afedf333d8be8 Mon Sep 17 00:00:00 2001 From: Brooks Vaughn <18422308+BrooksV@users.noreply.github.com> Date: Tue, 18 Feb 2025 11:38:40 -0500 Subject: [PATCH 2/5] Initial Commit --- README.md | 118 +-- project.json | 22 +- src/private/SqlQueryClass.ps1 | 920 ------------------ src/public/Add-ClickToMenuItem.ps1 | 25 + src/public/Dismount-Database.ps1 | 20 - src/public/Get-Database.ps1 | 16 - src/public/Get-DatabaseTable.ps1 | 14 - src/public/Get-FormVariable.ps1 | 9 + src/public/Invoke-DatabaseNonQuery.ps1 | 32 - src/public/Invoke-DatabaseQuery.ps1 | 49 - src/public/Mount-Database.ps1 | 20 - src/public/New-SqlQueryDataSet.ps1 | 105 -- src/public/New-XamlWindow.ps1 | 111 +++ ...yClass.help.txt => about_GuiMyPS.help.txt} | 0 tests/New-SqlQueryDataSets.tests.ps1 | 181 ---- tests/TestDatabase1.mdf | Bin 3342336 -> 3342336 bytes tests/TestDatabase1_log.ldf | Bin 786432 -> 786432 bytes 17 files changed, 216 insertions(+), 1426 deletions(-) delete mode 100644 src/private/SqlQueryClass.ps1 create mode 100644 src/public/Add-ClickToMenuItem.ps1 delete mode 100644 src/public/Dismount-Database.ps1 delete mode 100644 src/public/Get-Database.ps1 delete mode 100644 src/public/Get-DatabaseTable.ps1 create mode 100644 src/public/Get-FormVariable.ps1 delete mode 100644 src/public/Invoke-DatabaseNonQuery.ps1 delete mode 100644 src/public/Invoke-DatabaseQuery.ps1 delete mode 100644 src/public/Mount-Database.ps1 delete mode 100644 src/public/New-SqlQueryDataSet.ps1 create mode 100644 src/public/New-XamlWindow.ps1 rename src/resources/{about_SqlQueryClass.help.txt => about_GuiMyPS.help.txt} (100%) delete mode 100644 tests/New-SqlQueryDataSets.tests.ps1 diff --git a/README.md b/README.md index f22fbae..27c69bf 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,43 @@ -# SqlQueryClass +# GuiMyPS -The SqlQueryClass module provides a set of functions and cmdlets for working with SQL databases. It includes functionality for connecting, executing SQL queries, and managing output as DataTable, DataAdapter, DataSet, SqlReader, or NonQuery result objects. +Module helps in creating and running WPF GUI based PowerShell Applications. -## `SqlQueryClass` Module and Status Details +## `GuiMyPS` Module and Status Details Name | Version | PS Compatibility | Project Uri (GitHub) ------------- | ------- | ---------------- | ------------------------------------------------------------------------------------ -SqlQueryClass | 0.1.3 | 5.1 | [https://github.com/BrooksV/SqlQueryClass](https://github.com/BrooksV/SqlQueryClass) +GuiMyPS | 0.0.1 | 5.1 | [https://github.com/BrooksV/GuiMyPS](https://github.com/BrooksV/GuiMyPS) -[PSGalleryLink]: https://www.powershellgallery.com/packages/SqlQueryClass/ -[BadgeIOCount]: https://img.shields.io/powershellgallery/dt/SqlQueryClass.svg?label=downoads%20SqlQueryClass%40PSGallery -[WorkFlowStatus]: https://img.shields.io/github/actions/workflow/status/BrooksV/SqlQueryClass/tests.yml?label=tests.yml%20build +[PSGalleryLink]: https://www.powershellgallery.com/packages/GuiMyPS/ +[BadgeIOCount]: https://img.shields.io/powershellgallery/dt/GuiMyPS.svg?label=downoads%20GuiMyPS%40PSGallery +[WorkFlowStatus]: https://img.shields.io/github/actions/workflow/status/BrooksV/GuiMyPS/tests.yml?label=tests.yml%20build -![PowerShell Gallery Version](https://img.shields.io/powershellgallery/v/SqlQueryClass.svg) +![PowerShell Gallery Version](https://img.shields.io/powershellgallery/v/GuiMyPS.svg) [![maintainer](https://img.shields.io/badge/maintainer-BrooksV-orange)](https://github.com/BrooksV) -[![License](https://img.shields.io/github/license/BrooksV/SqlQueryClass)](https://github.com/BrooksV/SqlQueryClass/blob/main/LICENSE) -[![contributors](https://img.shields.io/github/contributors/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/graphs/contributors/) -[![last-commit](https://img.shields.io/github/last-commit/BroksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/commits/) -[![issues](https://img.shields.io/github/issues/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/issues/) -[![issues-closed](https://img.shields.io/github/issues-closed/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/issues?q=is%3Aissue+is%3Aclosed) -[![GitHub stars](https://img.shields.io/github/stars/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/network/members) -[![GitHub pull requests](https://img.shields.io/github/issues-pr/BrooksV/SqlQueryClass.svg)](https://github.com/BrooksV/SqlQueryClass/pulls) +[![License](https://img.shields.io/github/license/BrooksV/GuiMyPS)](https://github.com/BrooksV/GuiMyPS/blob/main/LICENSE) +[![contributors](https://img.shields.io/github/contributors/BrooksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/graphs/contributors/) +[![last-commit](https://img.shields.io/github/last-commit/BroksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/commits/) +[![issues](https://img.shields.io/github/issues/BrooksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/issues/) +[![issues-closed](https://img.shields.io/github/issues-closed/BrooksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/issues?q=is%3Aissue+is%3Aclosed) +[![GitHub stars](https://img.shields.io/github/stars/BrooksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/BrooksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/network/members) +[![GitHub pull requests](https://img.shields.io/github/issues-pr/BrooksV/GuiMyPS.svg)](https://github.com/BrooksV/GuiMyPS/pulls) ### Build and Release Statistics -[![SqlQueryClass@PowerShell Gallery][BadgeIOCount]][PSGalleryLink] +[![GuiMyPS@PowerShell Gallery][BadgeIOCount]][PSGalleryLink] ![WorkFlow Status][WorkFlowStatus] -![Build Status](https://img.shields.io/github/actions/workflow/status/BrooksV/SqlQueryClass/ci.yml?label=ci.yml%20build) +![Build Status](https://img.shields.io/github/actions/workflow/status/BrooksV/GuiMyPS/ci.yml?label=ci.yml%20build) -![Version](https://img.shields.io/github/v/release/BrooksV/SqlQueryClass.svg?label=version) -![GitHub All Releases](https://img.shields.io/github/downloads/BrooksV/SqlQueryClass/total.svg?label=release%20dl%20all%40GitHub) -![GitHub release (latest by date)](https://img.shields.io/github/downloads/BrooksV/SqlQueryClass/latest/total.svg?label=release%20dl%20by%20date%40GitHub) -![Downloads](https://img.shields.io/github/downloads/BrooksV/SqlQueryClass/total.svg?label=total%20release%20dl%40GitHub) +![Version](https://img.shields.io/github/v/release/BrooksV/GuiMyPS.svg?label=version) +![GitHub All Releases](https://img.shields.io/github/downloads/BrooksV/GuiMyPS/total.svg?label=release%20dl%20all%40GitHub) +![GitHub release (latest by date)](https://img.shields.io/github/downloads/BrooksV/GuiMyPS/latest/total.svg?label=release%20dl%20by%20date%40GitHub) +![Downloads](https://img.shields.io/github/downloads/BrooksV/GuiMyPS/total.svg?label=total%20release%20dl%40GitHub) ### Related Links -- [LicenseUri](https://github.com/BrooksV/SqlQueryClass/blob/main/LICENSE) -- [ProjectUri](https://github.com/BrooksV/SqlQueryClass) +- [LicenseUri](https://github.com/BrooksV/GuiMyPS/blob/main/LICENSE) +- [ProjectUri](https://github.com/BrooksV/GuiMyPS) ### Module Tags / Keywords @@ -46,20 +46,20 @@ SqlQueryClass | 0.1.3 | 5.1 | [https://github.com/BrooksV/SqlQuer ## Installation ```powershell -Install-Module -Name SqlQueryClass -Repository PSGallery -Scope CurrentUser +Install-Module -Name GuiMyPS -Repository PSGallery -Scope CurrentUser ``` To load a local build of the module, use Import-Module as follows: ```powershell -Import-Module -Name "C:\Git\SqlQueryClass\dist\SqlQueryClass\SqlQueryClass.psd1" -Force -verbose +Import-Module -Name "C:\Git\GuiMyPS\dist\GuiMyPS\GuiMyPS.psd1" -Force -verbose ``` ### Requirements - Tested with PowerShell 5.1 and 7.5x - No known dependencies for usage -- VS Code and clone [Brooks Vaughn's SqlQueryClass](https://github.com/BrooksV/SqlQueryClass) Repository +- VS Code and clone [Brooks Vaughn's GuiMyPS](https://github.com/BrooksV/GuiMyPS) Repository - Module build process uses [Manjunath Beli's](https://github.com/belibug) [ModuleTools](https://github.com/belibug) module. - Test scripts requires the Pester module and SQL Express - Includes sample SQL Express database file used in test scripts @@ -82,7 +82,7 @@ Import-Module -Name "C:\Git\SqlQueryClass\dist\SqlQueryClass\SqlQueryClass.psd1" ## Usage -The SqlQueryClass Module was developed to support data binding of WPF (Windows Presentation Framework) elements to DataTables and uses SQL Adapter features for CRUD operations. Having a single class object is very convenient since it allows for maintaining connectivity, queries, and results. +The GuiMyPS Module was developed to support data binding of WPF (Windows Presentation Framework) elements to DataTables and uses SQL Adapter features for CRUD operations. Having a single class object is very convenient since it allows for maintaining connectivity, queries, and results. It can be useful in any PS script that needs to read and write to SQL databases. For quick and simple, the Module's helper functions are also a consideration. When needing to use the Classes, use the `New-SqlQueryDataSet` function which calls the Parent Class [SqlQueryDataSet]::New() constructor and return an instance of the class. @@ -129,11 +129,11 @@ This includes details on: - [Setup](contributor.guide.md#setup) - [Source Files used in the Module](contributor.guide.md#source-files-used-in-the-module) - [Module Build Process](contributor.guide.md#module-build-process) -- [Publishing `SqlQueryClass` Module to GitHub](contributor.guide.md#publishing-sqlqueryclass-module-to-github) +- [Publishing `GuiMyPS` Module to GitHub](contributor.guide.md#publishing-GuiMyPS-module-to-github) - [Code Review and Feedback](contributor.guide.md#code-review-and-feedback) - [Merge the Pull Request](contributor.guide.md#merge-the-pull-request) - [Cleanup](contributor.guide.md#cleanup) -- [Publishing `SqlQueryClass` Module to PowerShell Gallery](contributor.guide.md#publishing-sqlqueryclass-module-to-powershell-gallery) +- [Publishing `GuiMyPS` Module to PowerShell Gallery](contributor.guide.md#publishing-GuiMyPS-module-to-powershell-gallery) ### ToDo @@ -147,7 +147,7 @@ This includes details on: ## Module Exported Functions ```powershell -Get-Command -Module "SqlQueryClass" -Syntax +Get-Command -Module "GuiMyPS" -Syntax - Dismount-Database [[-connectionString] ] [[-Database] ] [-Quiet] - Get-Database [[-connectionString] ] [[-query] ] [-Quiet] @@ -199,21 +199,27 @@ For additional technical information, see: ## Folder Structure and Build Management -The folder structure of the SqlQueryClass module is based on best practices for PowerShell module development and was initially created using [Manjunath Beli's](https://github.com/belibug) [ModuleTools](https://github.com/belibug) module. Check out his [Blog article](https://blog.belibug.com/post/ps-modulebuild) that explains the core concepts of ModuleTools. +The folder structure of the GuiMyPS module is based on best practices for PowerShell module development and was initially created using [Manjunath Beli's](https://github.com/belibug) [ModuleTools](https://github.com/belibug) module. Check out his [Blog article](https://blog.belibug.com/post/ps-modulebuild) that explains the core concepts of ModuleTools. + +Install [Manjunath Beli's ModuleTools](https://github.com/belibug/ModuleTools) module as the module build process uses ModuleTools + +```powershell +Find-Module -Name ModuleTools | Install-Module -Scope CurrentUser -Verbose +``` The the following ModuleTools CmdLets used in the build and maintenance process. They need to be executed from project root: -- Get-MTProjectInfo -- returns hashatble of project configuration which can be used in pester tests or for general troubleshooting -- Update-MTModuleVersion -- Increments SqlQueryClass module version by modifying the values in `project.json` or you can manually edit the json file. +- Get-MTProjectInfo -- returns HashTable of project configuration which can be used in pester tests or for general troubleshooting +- Update-MTModuleVersion -- Increments GuiMyPS module version by modifying the values in `project.json` or you can manually edit the json file. - Invoke-MTBuild -- Run `Invoke-MTBuild -Verbose` to build the module. The output will be saved in the `dist` folder, ready for distribution. - Invoke-MTTest -- Executes pester configuration (*.text.ps1) files in the `tests` folder - To skip a test, add `-skip` in describe block of the Pester *.test.ps1 file to skip. ### Folder and Files - + ```powershell -.\SQLQUERYCLASS +.\GuiMyPS | .gitignore | api.guide.md | CODE_OF_CONDUCT.md @@ -230,14 +236,14 @@ The the following ModuleTools CmdLets used in the build and maintenance process. | +---archive +---dist -| \---SqlQueryClass -| about_SqlQueryClass.help.txt -| SqlQueryClass.psd1 -| SqlQueryClass.psm1 +| \---GuiMyPS +| about_GuiMyPS.help.txt +| GuiMyPS.psd1 +| GuiMyPS.psm1 | +---src | +---private -| | SqlQueryClass.ps1 +| | GuiMyPS.ps1 | | | +---public | | Dismount-Database.ps1 @@ -249,7 +255,7 @@ The the following ModuleTools CmdLets used in the build and maintenance process. | | New-SqlQueryDataSet.ps1 | | | \---resources -| about_SqlQueryClass.help.txt +| about_GuiMyPS.help.txt | \---tests Module.Tests.ps1 @@ -263,11 +269,11 @@ The the following ModuleTools CmdLets used in the build and maintenance process. All files and folders in the `src` folder, will be published Module. -All other folder and files in the `.\SqlQueryClass` folder will resides in the [GitHub SqlQueryClass Repository](https://github.com/BrooksV/SqlQueryClass) except those excluded by inclusion in the `.\SqlQueryClass\.gitignore` file. +All other folder and files in the `.\GuiMyPS` folder will resides in the [GitHub GuiMyPS Repository](https://github.com/BrooksV/GuiMyPS) except those excluded by inclusion in the `.\GuiMyPS\.gitignore` file. ### Project JSON File -The `project.json` file contains all the important details about your module, is used during the module build process, and helps to generate the SqlQueryClass.psd1 manifest. +The `project.json` file contains all the important details about your module, is used during the module build process, and helps to generate the GuiMyPS.psd1 manifest. ### Root Level and Other Files @@ -275,24 +281,24 @@ The `project.json` file contains all the important details about your module, is - CODE_OF_CONDUCT.md -- Standard GitHub code of conduct and standards - GitHub_Action_Docs.md -- How to add GitHub Action WorkFlows to automate CI/CD (Continuous Integration/Continuous Deployment) - LICENSE -- MIT License notice and copyright -- project.json -- ModuleTools project configuration file used to build the `SqlQueryClass` module -- README.md -- Documentation (this) file for the `SqlQueryClass` module -- .vscode\settings.json -- VS Code settings used during `SqlQueryClass` module development +- project.json -- ModuleTools project configuration file used to build the `GuiMyPS` module +- README.md -- Documentation (this) file for the `GuiMyPS` module +- .vscode\settings.json -- VS Code settings used during `GuiMyPS` module development - *.guide.md -- various guides such as api, contributor, wpf ### archive Folder -`.\SqlQueryClass\archive` is not used in this project. Its a temporary place / BitBucket to hold code snippets and files during development and is not part of the build. +`.\GuiMyPS\archive` is not used in this project. Its a temporary place / BitBucket to hold code snippets and files during development and is not part of the build. ### Dist (build output) Folder -Generated module is stored in `dist\SqlQueryClass` folder, you can easily import it or publish it to PowerShell Gallery or repository. +Generated module is stored in `dist\GuiMyPS` folder, you can easily import it or publish it to PowerShell Gallery or repository. ### Src Folder - - All functions in the `public` folder are exported during the module build. - - All functions in the `private` folder are accessible internally within the module but are not exposed outside the module. - - All files and folder contained in the `resources` folder will be published to the `dist\SqlQueryClass` folder. +- All functions in the `public` folder are exported during the module build. +- All functions in the `private` folder are accessible internally within the module but are not exposed outside the module. +- All files and folder contained in the `resources` folder will be published to the `dist\GuiMyPS` folder. ### Tests Folder @@ -310,7 +316,7 @@ Run `Invoke-MTTest` to execute the tests. ## Join the Conversation -We encourage you to participate in our [Discussions](https://github.com/BrooksV/SqlQueryClass/discussions) section! Whether you have questions, ideas, or just want to chat with other users, Discussions is the place to be. Your feedback and contributions are valuable to us! +We encourage you to participate in our [Discussions](https://github.com/BrooksV/GuiMyPS/discussions) section! Whether you have questions, ideas, or just want to chat with other users, Discussions is the place to be. Your feedback and contributions are valuable to us! ## License @@ -319,11 +325,11 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file ## Acknowledgements - [Manjunath Beli](https://github.com/belibug) for the [ModuleTools](https://github.com/belibug/ModuleTools) module used in the build process. -- [Brooks Vaughn](https://github.com/BrooksV) for maintaining the SqlQueryClass module. +- [Brooks Vaughn](https://github.com/BrooksV) for maintaining the GuiMyPS module. ## Contact For support, inquiries, or feedback, contact Brooks Vaughn at [BrooksV](https://github.com/BrooksV) through one of the following methods: -- **GitHub Issues**: [Open an issue](https://github.com/BrooksV/SqlQueryClass/issues) -- **GitHub Discussions**: [Start a discussion](https://github.com/BrooksV/SqlQueryClass/discussions) +- **GitHub Issues**: [Open an issue](https://github.com/BrooksV/GuiMyPS/issues) +- **GitHub Discussions**: [Start a discussion](https://github.com/BrooksV/GuiMyPS/discussions) diff --git a/project.json b/project.json index 4380059..e763ad1 100644 --- a/project.json +++ b/project.json @@ -1,23 +1,19 @@ { - "ProjectName": "SqlQueryClass", - "Description": "The SqlQueryClass module provides a set of functions and cmdlets for working with SQL databases. It includes functionality for connecting, executing SQL queries, and managing output as DataTable, DataAdapter, DataSet, SqlReader, or NonQuery result objects.", - "Version": "0.1.2", + "ProjectName": "GuiMyPS", + "Description": "Module helps in creating and running WPF GUI based PowerShell Applications.", + "Version": "0.0.1", "Manifest": { "Author": "Brooks Vaughn", "PowerShellHostVersion": "5.1", - "GUID": "8375edbe-fb0f-4cb6-acb0-9964b45725c0", + "GUID": "b9f7d4c6-e924-4a7b-a117-e169dc71f010", "Tags": [ "PowerShell", - "Database", - "SQL", - "SQLServer", - "SQLQuery", - "DataAdapter", - "DataSet", - "DataTable" + "WPF", + "XAML", + "GUI" ], - "ProjectUri": "https://github.com/BrooksV/SqlQueryClass", - "LicenseUri": "https://github.com/BrooksV/SqlQueryClass/blob/main/LICENSE" + "ProjectUri": "https://github.com/BrooksV/GuiMyPS", + "LicenseUri": "https://github.com/BrooksV/GuiMyPS/blob/main/LICENSE" }, "Pester": { "TestResult": { diff --git a/src/private/SqlQueryClass.ps1 b/src/private/SqlQueryClass.ps1 deleted file mode 100644 index 1e580ca..0000000 --- a/src/private/SqlQueryClass.ps1 +++ /dev/null @@ -1,920 +0,0 @@ -<# -.SYNOPSIS -. 'C:\Git\SqlQueryEditor\scripts\SqlQueryClass.ps1' - - SqlQueryClass.ps1 -- Dot Source file of SQL Query Class definitions for classes [SqlQueryDataSet] and [SqlQueryTable] - -.DESCRIPTION - This script defines two PowerShell classes [SqlQueryDataSet] and [SqlQueryTable] which are used to execute SQL Queries and return the results in a DataTable, DataAdapter, DataSet, DataRows ([Array]DataRow) or NonQuery. - - The parent class, [SqlQueryDataSet], is designed to manage SQL Server connections, execute queries, and methods to manage data. - The child class, [SqlQueryTable], is designed to manage a table SQL query's configuration and query results. - - [SqlQueryDataSet] class property, [Tables], is a collection of child class [SqlQueryTable] objects. - When a Query is added to the [SqlQueryDataSet] object, a new [SqlQueryTable] object is created and added to the [Tables] collection. - - The original design was a single class of [SqlQueryDataSet] with a single Query and Result. The SQL query could have several statements separated by semi-colons resulting in a DataSet result. - The DataSet approach worked out well for binding with WPF components provided you know which tables[index] was associated with which query. - When it came to how to edit and save changes for a specific DataTable in the DataSet, there was no easy way to only update a single table without looping through all the tables in the DataSet and checking for changes. - Another issue was with the default values on columns like Id, CreatedOn, which were updated in the database but could not get the WPF Components to reflect the changes without executing a new query and setting the ItemsSources. - - The next approach was resorting to ObservableCollection and DataView objects. This involved creating Model-Views for each table, a corresponding class, and functions to Add, Update, Delete, and Save. This was a lot of hard coding and not very flexible and also suffered from the default column values issue. - - A working solution was not to use multiple queries in a single DataSet but to have separate instances of [SqlQueryDataSet] for each table query executed as a SQLDataAdapter. - Using this approach, I was able to easily edit any table using the same DataGrid component and the application does not need to know the table schema of how to Select, Insert, Update, or Delete. - The [SqlQueryDataSet] class handles all of that dynamically utilizing the features [System.Data.SqlClient.SqlConnection], [System.Data.SqlClient.SqlCommand], [System.Data.SqlClient.SqlDataAdapter], [System.Data.SqlClient.SqlCommandBuilder], and [System.Data.DataViewRowState] objects. - - This class was built for a document scanning application that has several comboboxes and DataGrids populated from different table queries. It used a collection of [SqlQueryDataSet] objects in the main program and an enum of the tableNames to assist with accessing the correct DataSet results. -I was uncomfortable with the idea of hardcoding the table names and having separate [SqlQueryDataSet] objects with the same repeating Database configurations and connections. -This lead to having the [SqlQueryDataSet] maintain a collection of table queries ([SqlQueryTable]) which is where we are at now. - -Usage Documentation: - -An instance of the [SqlQueryDataSet] can be created using any one of the following constructors: - $TestQuery = [SqlQueryDataSet]::new() - $TestQuery = [SqlQueryDataSet]::new($SQLServer) - $TestQuery = [SqlQueryDataSet]::new($SQLServer, $Database) - $TestQuery = [SqlQueryDataSet]::new($SQLServer, $Database, $Query) - -When a Query is added to the [SqlQueryDataSet] object, a new [SqlQueryTable] object is created and added to [SqlQueryDataSet].Tables collection. The TableName and it's Index in the Tables collection is added a the TableNames [HashTable]. - -A Query can be added to the [SqlQueryDataSet] object in the following ways: - $TestQuery = [SqlQueryDataSet]::new($SQLServer, $Database, $Query) - $TestQuery.ExecuteQuery($Query) - $TestQuery.ExecuteAsDataTable($Query) - $TestQuery.ExecuteAsDataAdapter($Query) - $TestQuery.ExecuteAsDataSet($Query) - $TestQuery.ExecuteAsDataRows($Query) - $TestQuery.AddQuery($Query) - $TestQuery.LoadQueryFromFile($PathToSQLFile) - -using the AddQuery() method or LoadQueryFromFile() method. - $TestQuery.AddQuery($Query) - $TestQuery.LoadQueryFromFile($PathToSQLFile) - - - -It's only been test using SQL Express - -Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) - May 27 2016 15:33:17 - Copyright (c) Microsoft Corporation - Express Edition (64-bit) on Windows NT 6.3 (Build 19045: ) (Hypervisor) - -Example that produced the above - -. "C:\Git\SqlQueryEditor\scripts\SqlQueryClass.ps1" - -$SqlServer = '(localdb)\MSSQLLocalDB' -$DatabaseName = 'F:\DATA\BILLS\PSSCRIPTS\SCANMYBILLS\DATABASE1.MDF' -$ConnectionString = "Data Source={0};AttachDbFilename={1};Integrated Security=True" -f $SqlServer, $DatabaseName - -$TestQuery = [SqlQueryDataSet]::new() -$TestQuery.Database = $DatabaseName -$TestQuery.ConnectionString = $ConnectionString -$TestQuery.DisplayResults = $true - -$TestQuery.AddQuery('Version', "SELECT @@VERSION FROM INFORMATION_SCHEMA.TABLES") -$TestQuery.Tables[0].Query = "SELECT TABLE_NAME, @@VERSION FROM INFORMATION_SCHEMA.TABLES" -$TestQuery.Execute() - -$TestQuery.ExecuteQuery('SELECT @@VERSION FROM INFORMATION_SCHEMA.TABLES') -$TestQuery.Tables[0].Result - -$TestQuery.ExecuteAsDataSet('SELECT @@VERSION FROM INFORMATION_SCHEMA.TABLES') - -$TestQuery.AddQuery("SELECT * FROM [dbo].[SqlQuery] ORDER BY Id DESC") - -Setting the TableIndex controls which table query will be selected when performing Executions and Saves. These are a few ways to set the TableIndex: -$TestQuery.TableIndex = 2 -$TestQuery.TableIndex = $TestQuery.Tables.Where({$_.TableName -eq 'SqlQuery'})[0].TableIndex -$TestQuery.TableIndex = $TestQuery.TableNames['SqlQuery'] - - -Helper Methods -$TestQuery.BuildOleDbConnectionString() -- Builds a connection string for OleDb -$TestQuery.BuildConnectionString() -- [Hidden] Builds a connection string for SqlClient used as a fallback when $TestQuery.ConnectionString is missing or fails -$TestQuery.Clear() -- Closes the connection and clears all the properties and collections of the [SqlQueryDataSet] object -$TestQuery.CloseConnection() -- Closes the SQL Connection -$TestQuery.GetCreateBasicDLL($TableName) -- Returns a DataTable with the basic structure of a table with the following columns: Id, CreatedOn, CreatedBy, UpdatedOn, UpdatedBy -$TestQuery -$TestQuery -$TestQuery -$TestQuery -$TestQuery - - -AddQuery Method int AddQuery(string Query), int AddQuery(string TableName, string Query) -BuildOleDbConnectionString Method string BuildOleDbConnectionString() -Clear Method void Clear() -CloseConnection Method void CloseConnection() -Equals Method bool Equals(System.Object obj) -Execute Method System.Object Execute(), System.Object Execute(SqlQueryTable table), System.Object Execute(int TableIndex), System.Object Execute(stri… -ExecuteAsDataAdapter Method System.Object ExecuteAsDataAdapter(string SqlQuery) -ExecuteAsDataSet Method System.Object ExecuteAsDataSet(string SqlQuery) -ExecuteAsDataTable Method System.Object ExecuteAsDataTable(string SqlQuery) -ExecuteAsDataRows Method System.Object ExecuteAsDataRows(string SqlQuery) -ExecuteNonQuery Method System.Object ExecuteNonQuery(string SqlQuery) -ExecuteQuery Method System.Object ExecuteQuery(string SqlQuery), System.Object ExecuteQuery(string TableName, string SqlQuery) -GetCreateBasicDLL Method System.Object GetCreateBasicDLL(string TableName) -GetCreateDDL Method System.Object GetCreateDDL(string TableName) -GetDBTableIndexes Method System.Object GetDBTableIndexes(string TableName) -GetDBTableIndexesV17 Method System.Object GetDBTableIndexesV17(string TableName) -GetDBTableSchema Method System.Object GetDBTableSchema(string TableName) -GetHashCode Method int GetHashCode() -GetTableFromQuery Method System.Object GetTableFromQuery(string Query) -GetTableFromTableName Method System.Object GetTableFromTableName(string TableName) -GetType Method type GetType() -LoadQueryFromFile Method void LoadQueryFromFile(string Path) -OpenConnection Method void OpenConnection() -ParseSQLQuery Method System.Object ParseSQLQuery(string Query) -SaveChanges Method System.Object SaveChanges() -ToString Method string ToString() -CommandTimeout Property int CommandTimeout {get;set;} -ConnectionString Property string ConnectionString {get;set;} -ConnectionTimeout Property int ConnectionTimeout {get;set;} -Database Property string Database {get;set;} -DisplayResults Property bool DisplayResults {get;set;} -KeepAlive Property bool KeepAlive {get;set;} -SQLConnection Property System.Object SQLConnection {get;set;} -SQLServer Property string SQLServer {get;set;} -TableIndex Property int TableIndex {get;set;} -TableNames Property hashtable TableNames {get;set;} -Tables Property System.Collections.Generic.List[SqlQueryTable] Tables {get;set;} - -$TestQuery.Tables | GM - - - -Helpful commands to check and manage SQL Express: - -# Check environment PATH for Sql Related Paths -ForEach ($path in (([environment]::GetEnvironmentVariables()).Path -split ';').Where({$_ -like '*SQL*'})) {(Get-ChildItem -Path $path -Filter *.exe -Recurse).FullName} - -# Check environment PATH for Sql Express Paths -ForEach ($path in (([environment]::GetEnvironmentVariables()).Path -split ';').Where({$_ -like '*SQL*'})) {(Get-ChildItem -Path $path -Filter sqllocaldb.exe -Recurse).FullName} -C:\Program Files\Microsoft SQL Server\110\Tools\Binn\SqlLocalDB.exe -C:\Program Files\Microsoft SQL Server\120\Tools\Binn\SqlLocalDB.exe -C:\Program Files\Microsoft SQL Server\130\Tools\Binn\SqlLocalDB.exe - -# Display Help -SqlLocalDB.exe -? - -# Show Version -SqlLocalDB.exe v -Microsoft SQL Server 2012 (11.0.3000.0) -Microsoft SQL Server 2014 (12.0.4459.0) -Microsoft SQL Server 2016 (13.0.7037.1) - -# List Databases -SqlLocalDB.exe info -MSSQLLocalDB -ProjectsV13 -v11.0 - -# Info on specific Database -SqlLocalDB.exe info MSSQLLocalDB -Name: MSSQLLocalDB -Version: 12.0.4459.0 -Shared name: -Owner: Desktop\Brooks -Auto-create: Yes -State: Running -Last start time: 1/31/2025 9:51:16 AM -Instance pipe name: - -# Get current Database Status -$status = ((SqlLocalDB.exe info MSSQLLocalDB) -split [Environment]::NewLine).Where({$_}).Where({$_.StartsWith('State:')})[0].Split(' ')[-1] -$status - -# Start the Database -SqlLocalDB.exe Start MSSQLLocalDB - - -.AUTHOR - Brooks Vaughn - -.COMPONENT - VERSION 1.0.0 - -.NOTES -When using the debugger, I always get this error and not sure why or how to correct it as I have no idea where it is coming from. -ParserError: -Line | - 1 | [System.Diagnostics.DebuggerHidden()]param() ,eval $stdout.sync=true - | ~ - | Missing expression after unary operator ','. -it was suggested to use this at top of my scripts: -[System.Diagnostics.DebuggerHidden()] -param() -It doesn't seem to help. Strange think is it happens every other execution - -.EXAMPLE - -# Configure Database settings for connection -$SqlServer = '(localdb)\MSSQLLocalDB' -$DatabaseName = 'F:\DATA\BILLS\PSSCRIPTS\SCANMYBILLS\DATABASE1.MDF' -$ConnectionString = "Data Source={0};AttachDbFilename={1};Integrated Security=True" -f $SqlServer, $DatabaseName - - -# Create a new instance of SqlQueryDataSet -$TestQuery = New-SqlQueryDataSet -SQLServer $SqlServer -Database $DatabaseName -ConnectionString $ConnectionString -DisplayResults $true -# There are at least 12 different overloaded Execute methods used execute queries and return results as different object types. - -# Example usage of the class -$TestQuery.ExecuteQuery('DBTables', "SELECT TABLE_NAME, @@VERSION FROM INFORMATION_SCHEMA.TABLES") - -# Displaying the Results of the Query -$TestQuery.Tables[0].Result - -# Changing an existing Table Query and then executing it. -$TestQuery.Tables[0].Query = "SELECT * FROM INFORMATION_SCHEMA.TABLES" -$TestQuery.Execute($TestQuery.Tables[0]) - -$TestQuery = New-SqlQueryDataSet -SQLServer $SqlServer -Database $DatabaseName -ConnectionString $ConnectionString -DisplayResults $true -TableName 'Category' -Query 'SELECT * FROM [dbo].Category;' -$TestQuery = New-SqlQueryDataSet -SQLServer $SqlServer -Database $DatabaseName -ConnectionString $ConnectionString -DisplayResults $false -$TestQuery.ExecuteQuery('Category', 'SELECT * FROM [dbo].Category;') -$TestQuery.Tables[0].Result[0] -$TestQuery.DisplayResults = $false -$TestQuery.Execute($TestQuery.Tables[0]) - -.NOTES -C:\Git\SqlQueryEditor> $TestQuery = [SqlQueryDataSet]::new() -PS C:\Git\SqlQueryEditor> $TestQuery | GM - - TypeName: SqlQueryDataSet - -Name MemberType Definition ----- ---------- ---------- -AddQuery Method int AddQuery(string Query), int AddQuery(string TableName, string Query) -BuildOleDbConnectionString Method string BuildOleDbConnectionString() -Clear Method void Clear() -CloseConnection Method void CloseConnection() -Equals Method bool Equals(System.Object obj) -Execute Method System.Object Execute(), System.Object Execute(SqlQueryTable table), System.Object Execute(int TableIndex), System.Object Execute(stri… -ExecuteAsDataAdapter Method System.Object ExecuteAsDataAdapter(string SqlQuery) -ExecuteAsDataSet Method System.Object ExecuteAsDataSet(string SqlQuery) -ExecuteAsDataTable Method System.Object ExecuteAsDataTable(string SqlQuery) -ExecuteAsDataRows Method System.Object ExecuteAsDataRows(string SqlQuery) -ExecuteNonQuery Method System.Object ExecuteNonQuery(string SqlQuery) -ExecuteQuery Method System.Object ExecuteQuery(string SqlQuery), System.Object ExecuteQuery(string TableName, string SqlQuery) -GetCreateBasicDLL Method System.Object GetCreateBasicDLL(string TableName) -GetCreateDDL Method System.Object GetCreateDDL(string TableName) -GetDBTableIndexes Method System.Object GetDBTableIndexes(string TableName) -GetDBTableIndexesV17 Method System.Object GetDBTableIndexesV17(string TableName) -GetDBTableSchema Method System.Object GetDBTableSchema(string TableName) -GetHashCode Method int GetHashCode() -GetTableFromQuery Method System.Object GetTableFromQuery(string Query) -GetTableFromTableName Method System.Object GetTableFromTableName(string TableName) -GetType Method type GetType() -LoadQueryFromFile Method void LoadQueryFromFile(string Path) -OpenConnection Method void OpenConnection() -ParseSQLQuery Method System.Object ParseSQLQuery(string Query) -SaveChanges Method System.Object SaveChanges() -ToString Method string ToString() -CommandTimeout Property int CommandTimeout {get;set;} -ConnectionString Property string ConnectionString {get;set;} -ConnectionTimeout Property int ConnectionTimeout {get;set;} -Database Property string Database {get;set;} -DisplayResults Property bool DisplayResults {get;set;} -KeepAlive Property bool KeepAlive {get;set;} -SQLConnection Property System.Object SQLConnection {get;set;} -SQLServer Property string SQLServer {get;set;} -TableIndex Property int TableIndex {get;set;} -TableNames Property hashtable TableNames {get;set;} -Tables Property System.Collections.Generic.List[SqlQueryTable] Tables {get;set;} - -PS C:\Git\SqlQueryEditor> $TestQuery.AddQuery("SELECT TABLE_NAME, @@VERSION FROM INFORMATION_SCHEMA.TABLES") - -dataBase schemaTable Schema TableName --------- ----------- ------ --------- - INFORMATION_SCHEMA.TABLES INFORMATION_SCHEMA TABLES - -GetTableFromQuery(): $schemaTable.tableName=TABLES, TableName=TABLES, TableIndex=0 - -PS C:\Git\SqlQueryEditor> $TestQuery.Tables | GM - - TypeName: SqlQueryTable - -Name MemberType Definition ----- ---------- ---------- -Equals Method bool Equals(System.Object obj) -GetHashCode Method int GetHashCode() -GetType Method type GetType() -ToString Method string ToString() -isDirty Property bool isDirty {get;set;} -Parent Property SqlQueryDataSet Parent {get;set;} -Query Property string Query {get;set;} -QueryFile Property string QueryFile {get;set;} -Result Property System.Object Result {get;set;} -ResultType Property ResultType ResultType {get;set;} -SQLCommand Property System.Object SQLCommand {get;set;} -SqlDataAdapter Property System.Object SqlDataAdapter {get;set;} -TableIndex Property int TableIndex {get;set;} -TableName Property string TableName {get;set;} - -#> - -enum ResultType { DataTable; DataRows; DataAdapter; DataSet; NonQuery; } - -class SqlQueryTable { - [int]$TableIndex = 0 - [string]$TableName = [string]::Empty - [string]$Query = [string]::Empty - [object]$SQLCommand = $null - [object]$SqlDataAdapter = $null - [ResultType]$ResultType = [ResultType]::DataTable - [object]$Result = $null - [bool]$isDirty = $false - [string]$QueryFile = [string]::Empty - [SqlQueryDataSet]$Parent = $null - - # Constructor -empty object - SqlQueryTable () - { - Return - } -} - -class SqlQueryDataSet { - [string]$SQLServer - [string]$Database - [int]$ConnectionTimeout = 5 - [int]$CommandTimeout = 600 - # Connection string keywords: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring(v=vs.110).aspx - [string]$ConnectionString - [object]$SQLConnection - [int]$TableIndex = 0 - [System.Collections.Generic.List[SqlQueryTable]]$Tables - [System.Collections.Hashtable]$TableNames = @{} - [bool]$DisplayResults = $True - [bool]$KeepAlive = $False - - # Constructor -empty object - SqlQueryDataSet () { Return } - - # Constructor - sql server and database - SqlQueryDataSet ([String]$SQLServer, [String]$Database) { - $This.SQLServer = $SQLServer - $This.Database = $Database - } - - # Constructor - sql server, database and query - SqlQueryDataSet ([String]$SQLServer, [String]$Database, [string]$Query) { - $This.SQLServer = $SQLServer - $This.Database = $Database - $This.TableIndex = $This.AddQuery($Query) - } - - # Methods to Add New Table for Query and Results - [int] AddQuery([String]$Query) { - Return $This.AddQuery([String]::Empty, $Query) - } - [int] AddQuery([String]$TableName, [String]$Query) { - If (-not $this.Tables) { - $this.Tables = [System.Collections.Generic.List[SqlQueryTable]]::new() - } - $table = $null - $schemaTable = $This.ParseSQLQuery($Query) - If ([string]::IsNullOrEmpty($TableName)) { - $table = $This.GetTableFromQuery([String]$Query) - Write-Host ($schemaTable | Out-String) - $tableName = $schemaTable.tableName - } Else { - $table = $This.GetTableFromTableName([String]$TableName) - } - If ([String]::IsNullOrEmpty($table)) { - $index = $This.TableNames[$TableName] - If ($index) { - $table = $This.Tables[$index] - } - If ([String]::IsNullOrEmpty($table)) { - $table = [SqlQueryTable]::new() - $table.Parent = $This - $table.Query = $Query - $table.TableIndex = $This.Tables.Count - $table.TableName = $TableName - $This.TableNames.Add($TableName,$table.TableIndex) - [void]$This.Tables.Add($table) - } Else { - $This.TableIndex = $table.TableIndex - } - } Else { - $This.TableIndex = $table.TableIndex - If ($schemaTable.tableName -ne $table.TableName) { - Write-Warning "GetTableFromQuery() TableName from Query is different then TableName in Tables: `$schemaTable.tableName=$($schemaTable.tableName), TableName=$($table.TableName), TableIndex=$($table.TableIndex)" - } - } - Write-Host "GetTableFromQuery(): `$schemaTable.tableName=$($schemaTable.tableName), TableName=$($table.TableName), TableIndex=$($table.TableIndex)" -ForegroundColor Green - Return ($table.TableIndex) - } - - # Method to Find existing Table based on if there is an existing SQL Query - [Object] GetTableFromQuery([String]$Query) { - $table = $null - If ($this.Tables) { - $table = $This.Tables.Where({$_.Query -eq $Query}) - If ($table) { - Return ($table[0]) - } - } - Return ($table) - } - - # Method to Find existing Table based on TableName - [Object] GetTableFromTableName([String]$TableName) { - $table = $null - If ($this.Tables) { - $table = $This.Tables.Where({$_.TableName -eq $TableName}) - If ($table) { - Return ($table[0]) - } - } - Return ($table) - } - - # Method - hidden [String]BuildConnectionString() { - Return ("Server=$($This.SQLServer);Database=$($This.Database);Integrated Security=SSPI;Connection Timeout=$($This.ConnectionTimeout)") - # Return ("Data Source=$($This.SQLServer);Initial Catalog=$($This.Database);Integrated Security=True;Connect Timeout=$($This.ConnectionTimeout);Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False") - } - - [String]BuildOleDbConnectionString() { - # Return ("Provider=MSOLEDBSQL;Data Source=$($This.SQLServer);Initial Catalog=$($This.Database);Integrated Security=SSPI;Connection Timeout=$($This.ConnectionTimeout)") - # Return ("Provider=MSOLEDBSQL;Server=$($This.SQLServer);Initial Catalog=$($This.Database);Integrated Security=SSPI;Connection Timeout=$($This.ConnectionTimeout)") - # Return ("Provider=MSOLEDBSQL;Server=$($This.SQLServer);Database=$($This.Database);Trusted_Connection=yes") - # Return ("Provider=MSOLEDBSQL;DataTypeCompatibility=80;Server=$($This.SQLServer);Database=$($This.Database);Trusted_Connection=yes") - # Return ("Provider=MSOLEDBSQL;Driver={ODBC Driver 17 for SQL Server};Server=$($This.SQLServer);Database=$($This.Database);Trusted_Connection=yes;Connection Timeout=$($This.ConnectionTimeout)") - Return ("Provider=MSOLEDBSQL;Server=$($This.SQLServer);Database=$($This.Database);Trusted_Connection=yes;Connection Timeout=$($This.ConnectionTimeout)") - <# - All the above: "Could not find server 'System' in sys.servers. - Verify that the correct server name was specified. - If necessary, execute the stored procedure sp_addlinkedserver to add the server to sys.servers." - #> - } - - # Method - LoadQueryFromFile([String]$Path) { - If (Test-Path $Path) { - If ([IO.Path]::GetExtension($Path) -ne ".sql") { - throw [System.IO.FileFormatException] "'$Path' does not have an '.sql' extension'" - } Else { - Try { - [String]$QueryStatement = Get-Content -Path $Path -Raw -ErrorAction Stop - $This.TableIndex = $This.AddQuery($QueryStatement) - $This.Tables[$This.TableIndex].QueryFile = $Path - } Catch { - Write-host ($_ | Out-String) -ForegroundColor Red - } - } - } Else { - throw [System.IO.FileNotFoundException] "'$Path' not found" - } - } - - # Method - [void] OpenConnection() { - #If localDB, Get Instance Name - $localdb = ($This.SQLConnection.DataSource -split '\\')[-1] - If (-not $localdb) { - $localdb = ($This.ConnectionString -split ';').Where({$_.StartsWith('Data Source')}).ForEach({$_ -split '\\'})[-1] - } - $status = ((SqlLocalDB.exe info "$localdb") -split [environment]::NewLine).Where({$_}) - Switch ($status.Where({$_.StartsWith('State:')}).ForEach({($_ -split ' ')[-1]})) { - 'Stopped' {SqlLocalDB.exe Start "$localdb" ; break} - } - If ($This.SQLConnection -and $This.SQLConnection.State -eq 'Open' -and $This.KeepAlive) { - Return - } ElseIf ($This.SQLConnection -and $This.SQLConnection.State -ne 'Open') { - $This.SQLConnection.Open() - } Else { - If ($This.SQLConnection -and -not $This.KeepAlive) { - If ($This.SQLConnection.State -eq 'Open') { - $This.SQLConnection.Close() - } - $This.SQLConnection.Dispose() - } - - If (-not $This.ConnectionString) { - $This.ConnectionString = $This.BuildConnectionString() - } - - If ($This.SQLConnection.State -ne 'Open') { - $This.SQLConnection = [System.Data.SqlClient.SqlConnection]::new() - $This.SQLConnection.ConnectionString = $This.ConnectionString - Try { - $This.SQLConnection.Open() - } Catch { - $This.CloseConnection() - Write-host ($_ | Out-String) -ForegroundColor Red - } - } - } - } - - # Method - [void] CloseConnection() { - If ($This.SQLConnection) { - $This.SQLConnection.Close() - $This.SQLConnection.Dispose() - $This.SQLConnection = $null - } - } - - # Method - [System.Data.SqlClient.SqlCommand] GetSqlCommand([string]$query) { - $This.OpenConnection() - $SQLCommand = $This.SQLConnection.CreateCommand() - $SQLCommand.CommandText = $query - $SQLCommand.CommandTimeout = $This.CommandTimeout - $SQLCommand.Connection = $This.SQLConnection - Return $SQLCommand - } - - [void] Clear() { - $This.CloseConnection() - $This.SQLServer = $null - $This.Database = $null - $This.ConnectionTimeout = 5 - $This.CommandTimeout = 600 - $This.ConnectionString = $null - $This.SQLConnection = $null - $This.TableIndex = 0 - $This.Tables.Clear() - $This.TableNames.Clear() - $This.DisplayResults = $True - $This.KeepAlive = $False - } - - # Method - [Object] Execute() { - $table = $This.Tables[$This.TableIndex] - Return $This.Execute($table) - } - # Method - [Object] Execute([SqlQueryTable]$table) { - $SQLReader = $null - $table.SQLCommand = $This.GetSqlCommand($table.Query) - Try { - If ($table.ResultType -in @([ResultType]::DataTable, [ResultType]::DataRows)) { - $SQLReader = $table.SQLCommand.ExecuteReader() - If ($SQLReader) { - $table.Result = [System.Data.DataTable]::new() - $table.Result.Load($SQLReader) - If ($This.DisplayResults) { - If ($table.ResultType -eq [ResultType]::DataTable) { - Return ,$table.Result - } Else { - Return $table.Result - } - } - } - } ElseIf ($table.ResultType -eq [ResultType]::DataAdapter) { - $table.Result = [System.Data.DataSet]::new() - $table.SqlDataAdapter = [System.Data.SqlClient.SqlDataAdapter]::new($table.SQLCommand) - [void]$table.SqlDataAdapter.Fill($table.Result) - - # $SqlCommandBuilder = [System.Data.SqlClient.SqlCommandBuilder]::new($table.SqlDataAdapter) - # $SqlCommandBuilder.DataAdapter = $table.SqlDataAdapter - # $SqlCommandBuilder.QuotePrefix = "[" - # $SqlCommandBuilder.QuoteSuffix = "]" - # Try { $table.SqlDataAdapter.DeleteCommand = $SqlCommandBuilder.GetDeleteCommand() - # } Catch { - # Write-Warning "Failed to get DeleteCommand: $_" - # } - # Try { $table.SqlDataAdapter.UpdateCommand = $SqlCommandBuilder.GetUpdateCommand() - # } Catch { - # Write-Warning "Failed to get UpdateCommand: $_" - # } - # Try { $table.SqlDataAdapter.InsertCommand = $SqlCommandBuilder.GetInsertCommand() - # } Catch { - # Write-Warning "Failed to get InsertCommand: $_" - # } - - If ($This.DisplayResults) { - Return $table.Result.Tables[0] - } - } ElseIf ($table.ResultType -eq [ResultType]::DataSet) { - $table.Result = [System.Data.DataSet]::new() - $table.SqlDataAdapter = [System.Data.SqlClient.SqlDataAdapter]::new($table.SQLCommand) - [void]$table.SqlDataAdapter.Fill($table.Result) - If ($This.DisplayResults) { - Return $table.Result.Tables[0] - } - } ElseIf ($table.ResultType -eq [ResultType]::NonQuery) { - $table.Result = $table.SQLCommand.ExecuteNonQuery() - Return $table.Result - } - } Catch { - $This.SQLConnection.Close() - Return $(Write-host ($_ | Out-String) -ForegroundColor Red) - } Finally { - If (-not $this.KeepAlive) { - $This.CloseConnection() - } - If ($SQLReader -and -not $SQLReader.IsClosed) { - $SQLReader.Close() - $SQLReader.Dispose() - } - } - Return $null - } - - # Method - [Object] Execute([Int]$TableIndex) { - If ($TableIndex -ge 0) { - $This.TableIndex = $TableIndex - } - $table = $This.Tables[$TableIndex] - Return ($This.Execute($table)) - } - - # Method - [Object] Execute([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - Return ($This.Execute($table)) - } - - # Method - [Object] Execute([ResultType]$ResultType) { - $table = $This.Tables[$This.TableIndex] - $table.ResultType = $ResultType - Return ($This.Execute($table)) - } - - # Method - [Object] ExecuteNonQuery([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::NonQuery - Return ($This.Execute($table)) - } - - # Method - [Object] ExecuteQuery([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::DataTable - Return ($This.Execute($table)) - } - - [Object] ExecuteQuery([String]$TableName, [String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($TableName, $SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::DataTable - Return ($This.Execute($table)) - } - - # Method - [Object] ExecuteAsDataTable([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - $table = $This.Tables[$This.TableIndex] - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::DataTable - Return ($This.Execute($table)) - } - - # Method - [Object] ExecuteAsDataAdapter([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::DataAdapter - Return ($This.Execute($table)) - } - - # Method - [Object] ExecuteAsDataSet([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::DataSet - Return ($This.Execute($table)) - } - - # Method - [Object] ExecuteAsDataRows([String]$SqlQuery) { - If ($SqlQuery) { - $This.TableIndex = $This.AddQuery($SqlQuery) - } - $table = $This.Tables[$This.TableIndex] - $table.ResultType = [ResultType]::DataRows - Return ($This.Execute($table)) - } - - # Method - [Object] SaveChanges() { - $table = $This.Tables[$This.TableIndex] - If ($This.ConnectionString) { - $SaveChangesConnectionString = $This.ConnectionString - } Else { - $SaveChangesConnectionString = $This.BuildConnectionString() - } - - $SaveChangesConnection = [System.Data.SqlClient.SqlConnection]::new() - $SaveChangesConnection.ConnectionString = $SaveChangesConnectionString - - Try { - $This.KeepAlive = $false - $This.OpenConnection() - - #-------------------------------------------------- - # Create a DataView to examine the changes - #-------------------------------------------------- - # $dataView = [System.Data.DataView]::new($This.Tables[0].Result.Tables[0]) - # # Set the RowStateFilter to display only added and modified rows. - # $dataView.RowStateFilter = ([System.Data.DataViewRowState]::Deleted -bor [System.Data.DataViewRowState]::Added -bor [System.Data.DataViewRowState]::ModifiedCurrent) - # ForEach ($row in $dataView) {Write-Host ($row | FT -AutoSize | Out-String).Trim()} - # Write-Host ($dataView | FT -AutoSize | Out-String).Trim() - - $commandBuilder = [System.Data.SqlClient.SqlCommandBuilder]::new($table.SqlDataAdapter) - $table.SqlDataAdapter.UpdateCommand = $commandBuilder.GetUpdateCommand() - $table.SqlDataAdapter.InsertCommand = $commandBuilder.GetInsertCommand() - $table.SqlDataAdapter.DeleteCommand = $commandBuilder.GetDeleteCommand() - - If ($table.ResultType -in @([ResultType]::DataAdapter,[ResultType]::DataSet)) { - $table.SqlDataAdapter.Update($table.Result.Tables[0]) - } ElseIf ($table.ResultType -eq [ResultType]::DataTable) { - $table.SqlDataAdapter.Update($table.Result) - } - - If (-not [String]::IsNullOrEmpty($table.SqlDataAdapter.DeleteCommand)) { - $table.SqlDataAdapter.DeleteCommand.Connection = $This.SQLConnection - } - If (-not [String]::IsNullOrEmpty($table.SqlDataAdapter.UpdateCommand)) { - $table.SqlDataAdapter.UpdateCommand.Connection = $This.SQLConnection - } - If (-not [String]::IsNullOrEmpty($table.SqlDataAdapter.InsertCommand)) { - $table.SqlDataAdapter.InsertCommand.Connection = $This.SQLConnection - } - - Try { # First process deletes. - $table.SqlDataAdapter.Update($table.Result.Tables[0].Select($null, $null, [System.Data.DataViewRowState]::Deleted)) - } Catch { - Write-Warning "Handled an exception in the delete process: $($_.Exception.Message)" - } - - Try { # Next process updates. - $table.SqlDataAdapter.Update($table.Result.Tables[0].Select($null, $null, [System.Data.DataViewRowState]::ModifiedCurrent)) - } Catch { - Write-Warning "Handled an exception in the delete process: $($_.Exception.Message)" - } - - Try { # Finally, process inserts. - $table.SqlDataAdapter.Update($table.Result.Tables[0].Select($null, $null, [System.Data.DataViewRowState]::Added)) - $table.Result.Tables[0].AcceptChanges() - } Catch { - Write-Warning "Handled an exception in the delete process: $($_.Exception.Message)" - } - } Catch { - return $(Write-host ($_ | Out-String) -ForegroundColor Red) - } Finally { - $table.Result.AcceptChanges() - $This.CloseConnection() - } - - If ($This.DisplayResults) { - Return $table.Result.Tables[0] - } Else { - Return $null - } - } - - # Method to Retrieve Table Schema - [Object] GetDBTableSchema([String]$TableName) { - $SqlQuery = "SELECT * FROM [$($This.Database)].INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$TableName';" - # $SqlQuery = "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE FROM [$($This.Database)].INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$TableName';" - Return ($This.ExecuteQuery($SqlQuery)) - } - - # Method to Retrieve Table Indexes from SQL v17 or higher using STRING_AGG - [Object] GetDBTableIndexesV17([String]$TableName) { - $query = "SELECT i.name AS INDEX_NAME, - STRING_AGG(c.name, ', ') AS COLUMN_NAMES, - i.is_unique AS IS_UNIQUE - FROM sys.indexes AS i - JOIN sys.index_columns AS ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id - JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id - WHERE i.object_id = OBJECT_ID('dbo.$TableName') - GROUP BY i.name, i.is_unique" - $adapter = New-Object System.Data.SqlClient.SqlDataAdapter($query, $This.ConnectionString) - $indexTable = New-Object System.Data.DataTable - $adapter.Fill($indexTable) - return $indexTable - } - - # Method to Retrieve Table Indexes Without STRING_AGG - [Object] GetDBTableIndexes([String]$TableName) { - $SqlQuery = "SELECT i.name AS INDEX_NAME, - STUFF((SELECT ', ' + c.name - FROM [$($This.Database)].sys.index_columns AS ic - JOIN [$($This.Database)].sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id - WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id - FOR XML PATH('')), 1, 2, '') AS COLUMN_NAMES, - i.is_unique AS IS_UNIQUE - FROM [$($This.Database)].sys.indexes AS i - WHERE i.object_id = OBJECT_ID('dbo.$TableName') - GROUP BY i.name, i.is_unique, i.object_id, i.index_id" - Return ($This.ExecuteQuery($SqlQuery)) - } - - # Method to Generate the CREATE TABLE statement - [Object] GetCreateBasicDLL([String]$TableName) { - $schemaTable = $This.GetDBTableSchema($TableName) - $createTableStatement = "USE [$($This.Database)]" + [Environment]::NewLine + - "GO" + [Environment]::NewLine + [Environment]::NewLine + - "CREATE TABLE [$TableName] (" + [Environment]::NewLine - foreach ($column in $schemaTable.Rows) { - $columnName = $column.COLUMN_NAME - $dataType = $column.DATA_TYPE - $maxLength = if ($column.CHARACTER_MAXIMUM_LENGTH -ne -1) { "($($column.CHARACTER_MAXIMUM_LENGTH))" } else { "" } - $nullable = if ($column.IS_NULLABLE -eq "YES") { "NULL" } else { "NOT NULL" } - $createTableStatement += " [$columnName] $dataType$maxLength $nullable," + [Environment]::NewLine - } - # Remove the last comma and add closing parenthesis - $createTableStatement = $createTableStatement.TrimEnd(",`r`n".ToCharArray()) + [Environment]::NewLine + ");" - Return ($createTableStatement) - } - - # Method to Generate the CREATE TABLE statement - [Object] GetCreateDDL([String]$TableName) { - $schemaTable = $This.GetDBTableSchema($TableName) - $indexTable = $This.GetDBTableIndexes($TableName) - - $createTableStatement = "USE [$($This.Database)]" + [Environment]::NewLine + - "GO" + [Environment]::NewLine + [Environment]::NewLine + - "/****** Object: Table [dbo].[$TableName] Script Date: $(Get-Date -Format 'MM/dd/yyyy hh:mm:ss tt') ******/" + [Environment]::NewLine + - "SET ANSI_NULLS ON" + [Environment]::NewLine + - "GO" + [Environment]::NewLine + [Environment]::NewLine + - "SET QUOTED_IDENTIFIER ON" + [Environment]::NewLine + - "GO" + [Environment]::NewLine + [Environment]::NewLine + - "CREATE TABLE [dbo].[$TableName] (" + [Environment]::NewLine - - foreach ($column in $schemaTable.Rows) { - $columnName = $column.COLUMN_NAME - $dataType = $column.DATA_TYPE - $maxLength = if ($column.CHARACTER_MAXIMUM_LENGTH -ne -1) { "($($column.CHARACTER_MAXIMUM_LENGTH))" } else { "" } - $nullable = if ($column.IS_NULLABLE -eq "YES") { "NULL" } else { "NOT NULL" } - $identity = if ($column.COLUMN_NAME -eq "Id") { "IDENTITY (1, 1)" } else { "" } - $createTableStatement += " [$columnName] $dataType$maxLength $identity $nullable," + [Environment]::NewLine - } - - # Remove the last comma and add closing parenthesis - $createTableStatement = $createTableStatement.TrimEnd(",`r`n".ToCharArray()) + [Environment]::NewLine + ");" + [Environment]::NewLine + [Environment]::NewLine - # $createTableStatement = $createTableStatement.TrimEnd(",`r`n".ToCharArray()) + [Environment]::NewLine + "), PRIMARY KEY CLUSTERED ([Id] ASC);" + [Environment]::NewLine + [Environment]::NewLine - - # Add index creation statements, excluding the primary key index - foreach ($index in $indexTable.Rows) { - if ($index.INDEX_NAME -notlike 'PK__*') { - $indexName = $index.INDEX_NAME - $columnNames = $index.COLUMN_NAMES - $unique = if ($index.IS_UNIQUE -eq 1) { "UNIQUE " } else { "" } - $createTableStatement += "GO" + [Environment]::NewLine + - "CREATE $($unique)NONCLUSTERED INDEX [$indexName]" + [Environment]::NewLine + - " ON [dbo].[$TableName]([$columnNames] ASC);" + [Environment]::NewLine - } - } - return $createTableStatement - } - - # Method to Parse SQL Statements to extract the Schema and TableName. Support Multi-line Select From statements - [Object] ParseSQLQuery([String]$Query) { - $queryPattern = '([\s\t]*FROM[\s\t\r\n](?[A-Za-z0-9_.\[\]]+).*$)|((?s)[\s\t]*FROM[\s\t\r\n]+(?[A-Za-z0-9_. \[\] ]+).*$)' - - If ($Query -like '*\*' -and $Query -like '*.MDF*') { - $queryPattern = '([\s\t]*FROM[\s\t\r\n](?[\[]?[A-Za-z0-9_\:\\]+[\.MDF]{4}[\]]?)[.]{1}(?[A-Za-z0-9_.\[\]]+).*$)' - } Else { - $queryPattern = '([\s\t]*FROM[\s\t\r\n](?[A-Za-z0-9_.\[\]]+).*$)|((?s)[\s\t]*FROM[\s\t\r\n]+(?[A-Za-z0-9_. \[\] ]+).*$)' - } - $schemaTablePattern = '([\[]?(?[A-Za-z0-9_]+)[\.\[\] ]?$)|([\[]?(?[A-Za-z0-9_]+)[\.\[\]]*(?[A-Za-z0-9_]+)[\.\[\] ]?$)' - $retResult = ([PSCustomObject]@{ - dataBase = [string]::Empty - schemaTable = [string]::Empty - Schema = [string]::Empty - TableName = [string]::Empty - }) - If ($Query -match $queryPattern) { - $retResult.schemaTable = $Matches.schemaTable - $retResult.dataBase = $Matches.databaseName - If ($retResult.schemaTable -match $schemaTablePattern) { - $retResult.schema = $Matches.schema - $retResult.TableName = $Matches.tableName - } - } - Return ($retResult) - } - -} diff --git a/src/public/Add-ClickToMenuItem.ps1 b/src/public/Add-ClickToMenuItem.ps1 new file mode 100644 index 0000000..85e6f2f --- /dev/null +++ b/src/public/Add-ClickToMenuItem.ps1 @@ -0,0 +1,25 @@ +Function Add-ClickToMenuItem() { + Param ( + $MenuObj, + [ScriptBlock]$Handler + ) + If ([String]::IsNullOrEmpty($Handler)) { + Write-Warning "Parameter -Handler `$Handler cannot be blank" + Return + } + If ([String]::IsNullOrEmpty($MenuObj)) { + Write-Warning "Parameter -MenuObj `$MenuObj cannot be blank" + Return + } + Write-Verbose ("Menu Object: [{0}] - {1}" -f $MenuObj.Name, $MenuObj.GetType().ToString()) + ForEach ($child in $MenuObj.Item) { + Write-Verbose (" {0} - [{1}]" -f $child.Header, $child.GetType().ToString()) + If ($child.HasItems) { + Add-ClickToMenuItems -MenuObj $child -Handler:$handler + } Else { + If ($child -is 'System.Windows.Controls.MenuItem') { + $child.Add_Click($handler) + } + } + } +} diff --git a/src/public/Dismount-Database.ps1 b/src/public/Dismount-Database.ps1 deleted file mode 100644 index 539ad4f..0000000 --- a/src/public/Dismount-Database.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -Function Dismount-Database { - Param ( - $connectionString = "Data Source=(localdb)\MSSQLLocalDB;Integrated Security=True", - $Database = 'TestDatabase1', - [Switch]$Quiet - ) - Write-Warning "`nDetaching Database: ($Database)" - $NonQuery = "EXEC sp_detach_db '$Database'" - $Splat = @{connectionString = $connectionString; NonQuery = $NonQuery} - # Remove any null value parameters to use called function's default value - $Splat.Keys.Where({-not $Splat[$_]}).ForEach({$Splat.Remove($_)}) - Invoke-DatabaseNonQuery @Splat -Quiet:$Quiet -} -<# Usage Example: # > -$Error.Clear() -# Dismount-Database -# Dismount-Database -Database:$null -Dismount-Database -Database 'TestDataBase1' -Get-Database -#> diff --git a/src/public/Get-Database.ps1 b/src/public/Get-Database.ps1 deleted file mode 100644 index 6c1bf3c..0000000 --- a/src/public/Get-Database.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -Function Get-Database { - Param ( - $connectionString = "Data Source=(localdb)\MSSQLLocalDB;Integrated Security=True", - $query = "SELECT name FROM sys.databases;", - [Switch]$Quiet - ) - $Splat = @{connectionString = $connectionString; query = $query} - # Remove any null value parameters to use called function's default value - $Splat.Keys.Where({-not $Splat[$_]}).ForEach({$Splat.Remove($_)}) - Invoke-DatabaseQuery @Splat -Quiet:$Quiet -} -<# Usage Examples # > -Get-Database -Get-Database -Quiet -Get-Database -connectionString:$null -#> \ No newline at end of file diff --git a/src/public/Get-DatabaseTable.ps1 b/src/public/Get-DatabaseTable.ps1 deleted file mode 100644 index bd2f433..0000000 --- a/src/public/Get-DatabaseTable.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -Function Get-DatabaseTable { - Param ( - $connectionString, - $query = "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_SCHEMA, TABLE_NAME;", - [Switch]$Quiet - ) - $Splat = @{ connectionString = $connectionString; query = $query } - # Remove any null value parameters to use called function's default value - $Splat.Keys.Where({-not $Splat[$_]}).ForEach({$Splat.Remove($_)}) - Invoke-DatabaseQuery @Splat -Quiet:$Quiet -} -<# Usage Examples: # > -Get-DatabaseTable -#> diff --git a/src/public/Get-FormVariable.ps1 b/src/public/Get-FormVariable.ps1 new file mode 100644 index 0000000..6175c11 --- /dev/null +++ b/src/public/Get-FormVariable.ps1 @@ -0,0 +1,9 @@ +# Function to display form variables +Function Get-FormVariable { + if ($null -eq $ReadmeDisplay -or $ReadmeDisplay -eq $false) { + Write-Host "If you need to reference this display again, run Get-FormVariables" + } + Write-Host ("`n$("-" * 65)`nFound the following intractable elements from our form`n$("-" * 65)") + Get-Variable WPF* + Write-Host ("$("-" * 65)`n") +} diff --git a/src/public/Invoke-DatabaseNonQuery.ps1 b/src/public/Invoke-DatabaseNonQuery.ps1 deleted file mode 100644 index 198602a..0000000 --- a/src/public/Invoke-DatabaseNonQuery.ps1 +++ /dev/null @@ -1,32 +0,0 @@ -Function Invoke-DatabaseNonQuery { - Param ( - $connectionString = "Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=C:\Git\SqlQueryClass\tests\TestDatabase1.mdf;Integrated Security=True", - $NonQuery, - [Switch]$Quiet - ) - If ([String]::IsNullOrWhiteSpace($NonQuery)) { - Throw "Parameter -NonQuery cannot be null or empty" - } - If (-Not $Quiet) { - Write-Host "`nUsing ConnectionString: ($connectionString)" - Write-Host ("Executing Database NonQuery: $($NonQuery | Out-String)").Trim() - } - Try { - # Create and open the connection - $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString) - $connection.Open() - # Create a command to attach the database - $attachCommand = $connection.CreateCommand() - $attachCommand.CommandText = $NonQuery - $attachCommand.ExecuteNonQuery() - } Catch { - Write-host ($_ | Out-String) -ForegroundColor Red - } Finally { - # Close connection - $connection.Close() - } -} -<# Usage Example: # > -$Error.Clear() -Invoke-DatabaseNonQuery -NonQuery "EXEC sp_detach_db 'NORTHWIND'" -#> diff --git a/src/public/Invoke-DatabaseQuery.ps1 b/src/public/Invoke-DatabaseQuery.ps1 deleted file mode 100644 index 4c9dbbc..0000000 --- a/src/public/Invoke-DatabaseQuery.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -Function Invoke-DatabaseQuery { - Param ( - $connectionString = "Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=C:\Git\SqlQueryClass\tests\TestDatabase1.mdf;Integrated Security=True", - $query, - [Switch]$Quiet - ) - If ([String]::IsNullOrWhiteSpace($Query)) { - Throw "Parameter -Query cannot be null or empty" - } - If (-Not $Quiet) { - Write-Host "`nUsing ConnectionString: ($connectionString)" - Write-Host ("Executing SQL Query: $($Query | Out-String)").Trim() - } - $SQLReader = $null - $Result = $null - Try { - # Create and open the connection - $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString) - $connection.Open() - - # Create the command - $command = $connection.CreateCommand() - $command.CommandText = $query - $command.CommandTimeout = 600 - - # Execute the command and read the results - $SQLReader = $command.ExecuteReader() - If ($SQLReader) { - $Result = [System.Data.DataTable]::new() - $Result.Load($SQLReader) - } - # Write-Host ($Result.Rows | Out-String) - Return ,$Result - } Catch { - Write-host ($_ | Out-String) -ForegroundColor Red - } Finally { - # Close the reader and the connection - If ($SQLReader) { - $SQLReader.Close() - } - $connection.Close() - } -} -<# Usage Example: # > -Invoke-DatabaseQuery -connectionString 'Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=C:\Git\SqlQueryClass\tests\TestDatabase1.mdf;Integrated Security=True' -query 'SELECT database_id, name, compatibility_level FROM sys.databases;' -Invoke-DatabaseQuery -query "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_SCHEMA, TABLE_NAME;" -Invoke-DatabaseQuery -query "SELECT * FROM SqlQuery;" -Invoke-DatabaseQuery -query "SELECT * FROM SqlQueryParms;" -#> diff --git a/src/public/Mount-Database.ps1 b/src/public/Mount-Database.ps1 deleted file mode 100644 index ab7f595..0000000 --- a/src/public/Mount-Database.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -Function Mount-Database { - Param ( - $connectionString = "Data Source=(localdb)\MSSQLLocalDB;Integrated Security=True", - $Database = 'TestDatabase1', - $DatabaseFilePath = 'C:\Git\SqlQueryClass\tests\TestDatabase1.mdf', - [Switch]$Quiet - ) - Write-Warning "`nAttaching Database: ($Database) with Database File ($DatabaseFilePath)" - $Query = "CREATE DATABASE $Database ON (FILENAME = '$DatabaseFilePath') FOR ATTACH" - $Splat = @{ connectionString = $connectionString; query = $query } - # Remove any null value parameters to use called function's default value - $Splat.Keys.Where({-not $Splat[$_]}).ForEach({$Splat.Remove($_)}) - Invoke-DatabaseQuery @Splat -Quiet:$Quiet -} -<# Usage Example: # > -$Error.Clear() -Mount-Database -Get-Database -# Mount-Database -Database 'TestDataBase1' -DatabaseFilePath 'C:\Git\SqlQueryClass\tests\TestDatabase1.mdf' -#> diff --git a/src/public/New-SqlQueryDataSet.ps1 b/src/public/New-SqlQueryDataSet.ps1 deleted file mode 100644 index 90da3cb..0000000 --- a/src/public/New-SqlQueryDataSet.ps1 +++ /dev/null @@ -1,105 +0,0 @@ -<# -.SYNOPSIS - New-SqlQueryDataSet -- Creates and returns an Object instance of the [SqlQueryDataSet] class configured with or without the specified parameters. -.DESCRIPTION - This function initializes a new instance of the [SqlQueryDataSet] class and the resulting object is configured is based which parameters were specified. - - All parameters are optional as the can be configured later using the [SqlQueryDataSet]$object returned when calling $object = New-SqlQueryDataSet - When using $SQLServer and $Database, both must be specified together. The [SqlQueryDataSet] class will auto generate a SQL ConnectionString. - Specifying $ConnectionString overrides auto generation even when $SQLServer and $Database are also specified. - - Based on which parameters are passed, this CmdLet will use one of the overloaded class constructors and configure instance settings with the other parameters: - - [SqlQueryDataSet]::new() - - [SqlQueryDataSet]::new(string SQLServer, string Database) - - [SqlQueryDataSet]::new(string SQLServer, string Database, string Query) - - Explanation of Parameter Sets: - - **`ServerDatabase`**: This parameter set allows the user to specify the SQL Server and Database separately without needing a full connection string. - - **`ServerDatabaseWithConnectionString`**: This parameter set allows the user to provide both the SQL Server and Database separately, or use a connection string. - - **`ConnectionString`**: This parameter set allows the user to provide a connection string directly. -.PARAMETER SQLServer - The name or address of the SQL Server to connect to. This parameter is optional, but when used, must be specified with $Database. -.PARAMETER Database - The name of the database to connect to on the specified SQL Server. This parameter is also optional, but when used, must be specified with $SQLServer. -.PARAMETER ConnectionString - The full connection string to use for connecting to the SQL Server. This parameter is optional and provides an alternative to specifying $SQLServer and $Database separately. -.PARAMETER Query - The SQL query to execute against the database. This parameter is optional, only configures the query settings, and does not trigger execution. Best when used with $TableName. -.PARAMETER TableName - Unique identifier that names the configuration of this Query. This parameter is optional and a $TableName will be parsed from only simple queries which is why its best to specify with $Query. -.PARAMETER DisplayResults - Boolean flag indicating whether to display the query results. The default value is $true. - The [SqlQueryDataSet] class uses this flag to output content to standard out when executing content generating methods such as Execute(). -.FUNCTIONALITY - Creates and initializes an Instance of [SqlQueryDataSet] class -.OUTPUTS - Object of [SqlQueryDataSet] class -.EXAMPLE - $result = New-SqlQueryDataSet -SQLServer "myServer" -Database "myDB" -Query "SELECT * FROM myTable" -.EXAMPLE - $result = New-SqlQueryDataSet -ConnectionString "Server=myServer;Database=myDB;User Id=myUser;Password=myPass;" -Query "SELECT * FROM myTable" -TableName myTable -DisplayResults $false -.NOTES - Author: Brooks Vaughn - Date: 2025-02-01 - Version: 0.1.0 -#> - -function New-SqlQueryDataSet { - [CmdletBinding(DefaultParameterSetName = 'ServerDatabase')] - # Suppress PSScriptAnalyzer rule about ShouldProcess - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] - - param ( - [Parameter(Mandatory = $false)] - [string]$SQLServer, - - [Parameter(Mandatory = $false)] - [ValidateScript({ if ($SQLServer -and -not $_) { throw "Database must be provided if SQLServer is specified." } else { $true } })] - [string]$Database, - - [Parameter(Mandatory = $false)] - [string]$ConnectionString, - - [Parameter(Mandatory = $false)] - [ValidateScript({ if ($TableName -and -not $_) { throw "Query must be provided if TableName is specified." } else { $true } })] - [string]$Query, - - [Parameter(Mandatory = $false)] - [string]$TableName, - - [Parameter(Mandatory = $false)] - [bool]$DisplayResults = $true - ) - - # Function logic here - if ($SQLServer -and -not $Database) { - Write-Warning "Database must be provided if SQLServer is specified." - } - - if ($Database -and -not $SQLServer) { - Write-Warning "SQLServer must be provided if Database is specified." - } - - if ($TableName -and -not $Query) { - Write-Warning "Query must be provided if TableName is specified." - } - - $instance = $null - if (-not [string]::IsNullOrEmpty($SQLServer) -and -not [string]::IsNullOrEmpty($Database)) { - $instance = [SqlQueryDataSet]::new($SQLServer, $Database) - } else { - $instance = [SqlQueryDataSet]::new() - } - if (-not [string]::IsNullOrEmpty($Query)) { - if (-not [string]::IsNullOrEmpty($TableName)) { - [void]$instance.AddQuery($TableName, $Query) - } else { - [void]$instance.AddQuery($Query) - } - } - if (-not [string]::IsNullOrEmpty($ConnectionString)) { $instance.ConnectionString = $ConnectionString } - if (-not [string]::IsNullOrEmpty($SQLServer)) { $instance.SQLServer = $SQLServer } - if (-not [string]::IsNullOrEmpty($Database)) { $instance.Database = $Database } - $instance.DisplayResults = $DisplayResults - return $instance -} diff --git a/src/public/New-XamlWindow.ps1 b/src/public/New-XamlWindow.ps1 new file mode 100644 index 0000000..cbff238 --- /dev/null +++ b/src/public/New-XamlWindow.ps1 @@ -0,0 +1,111 @@ +<# +PsWpfHelper +WpfHelperForPS +PsWpfUtils +WpfUtilsForPS +PsWpfLib +PsXaml +PsXamlHelper +GuiMyPS - Collections of XAML & WPF Helper Functions to simplify creation of GUI based PowerShell Applications +GuiMyPS - Module helps in creating and running WPF GUI based PowerShell Applications. + +#> +Function New-XamlWindow { + Param ( + $xaml, + $NoXRemoval + ) + # Load the necessary assemblies + Add-Type -AssemblyName PresentationFramework + + <# + XAML Elements might use either x:Name="" or Name="" + The x: refers to the namespace xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" which is not automatically defined in PowerShell + To use XPath queries are used during preprocessing of the XAML Text, you have to create a NamespaceManager and Add the missing NameSpaces + For x:Name, the XPath query would be $xaml.SelectNodes("//*[@x:Name]", $nsManager) + For Name, the XPath query would be $xaml.SelectNodes("//*[@Name]", $nsManager) + By Removing x:Name from the XAML string before converting to [System.Xml.XmlDocument], + there is not need for the NamespaceManager and XPath needs only$xaml.SelectNodes("//*[@Name]") + #> + Function Remove-XName { + [CmdletBinding(SupportsShouldProcess=$true)] + Param ( + $xamlString, + $NoXRemoval + ) + If ($NoXRemoval) { + $xamlString + } Else { + ((($xamlString -replace 'mc:Ignorable="d"','') -replace "x:Na",'Na') -replace '^7TN4v&V(S`A*n%tYPW y1y$5ghlvK7u+Tyq9dyw{9|PEMpx|PN5yqHciW%luU8}b3B8AKD4gW$CJ5dV z#2(@~rmtz5;NSKBy`96C`#d^w5b-ae63d7$9l2Aa1UeE(B84^mG`e^WnMysc3@o@J)0p4pxjH-%W#+U{>JQ6i->m!quKXRTROF~*q zu)GhH{&IQ_OCK3~u#(e@6&-fY73%s6bsV+^yUZ!n>3TYw&?CkU$j@Pqf0|sCXjNJ#jx@ zW?kRJ2H-yo(BLmr4McKtA#^zEZ@y>kYwIBM+#PxP)G&!g7T zRwak4m0}lUEtKENglqZzO!GYZe2X5RA$c1n-zAgJ={dC(+q3)ypKt#e(aZoPvad$8 z+E^vk!(?5I#aRr(y6GI)6}(xovaJ5&2kNQ&4P$0msNTApSmor8=?&c24HhitMbE?S zplC(TmfP!g>5KS|*%dpEVtb_6=;k*qAhv9P&WuGD&phaz8A}#6)8ktIX2^tZxN8`xZy+!K+yV5yW+9F;ny(wem|6_Px6Qc5wm4(vt;k@UqQ$@5i; zXv5U|Wa?n_j}jbph^rO93npvwbL791EL$=(HH{-f9Z8yd zXF?$f1CXS!BbWig`Z zpcHHWP_wx>m9S`)Cil$PJ7JrNraqak;lRJEv0e0xGX+g6dM{ArH-g}#EK|@VLN#WJ zOQ6O}nht?C(lmAK8fINDo^cp}G?|j7>k3U6?b76)sS))BO{KMnro&R})us$;$!DAw zKw2qAx!@MDBYi~E4f{k3JgGfLUloK>LT0{oF7!lE_UVhws{#>NTTG?e=DLptmL>^V+; XC|Dgz=Yr{6c{*2-&Q)H|g%*DUryStB delta 392 zcmZo@FlcBnm|(+ljQtz;WJO1XjR|vE8NDV8@Q8DIF)%PN0Ws%h1D+i$o3C+MGBO5$ zWdfiwTtJzRtk?xH&6v!xeL)vXJR^jhY~UE){71h1k31s~GXXL4_CNA0ENyMqSlX_! jwq0Xec8#42S;z2TxBtD*4#XTl%n8I?+yCC@W>x?Ie2a#{ From 7c36e557b40fdcd7befa0f35862bae03a1ebdef8 Mon Sep 17 00:00:00 2001 From: Brooks Vaughn <18422308+BrooksV@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:05:19 -0500 Subject: [PATCH 3/5] Added Event Handler Code Generator --- src/public/Add-ClickToEveryButton.ps1 | 60 ++++++++ src/public/Add-ClickToEveryMenuItem.ps1 | 62 ++++++++ src/public/Add-ClickToMenuItem.ps1 | 25 ---- src/public/Add-EventsToEveryCheckBox.ps1 | 126 ++++++++++++++++ src/public/Build-HandlerCode.ps1 | 64 ++++++++ src/public/Find-EveryControl.ps1 | 74 ++++++++++ src/public/Format-XML.ps1 | 69 +++++++++ src/public/Get-ControlContent.ps1 | 12 ++ src/public/Get-ObjectPropertyDetail.ps1 | 52 +++++++ src/public/New-XamlWindow.ps1 | 36 ++++- tests/ScriptAnalyzer.Tests.ps1 | 2 +- tests/Test-Usage-01.ps1 | 180 +++++++++++++++++++++++ 12 files changed, 728 insertions(+), 34 deletions(-) create mode 100644 src/public/Add-ClickToEveryButton.ps1 create mode 100644 src/public/Add-ClickToEveryMenuItem.ps1 delete mode 100644 src/public/Add-ClickToMenuItem.ps1 create mode 100644 src/public/Add-EventsToEveryCheckBox.ps1 create mode 100644 src/public/Build-HandlerCode.ps1 create mode 100644 src/public/Find-EveryControl.ps1 create mode 100644 src/public/Format-XML.ps1 create mode 100644 src/public/Get-ControlContent.ps1 create mode 100644 src/public/Get-ObjectPropertyDetail.ps1 create mode 100644 tests/Test-Usage-01.ps1 diff --git a/src/public/Add-ClickToEveryButton.ps1 b/src/public/Add-ClickToEveryButton.ps1 new file mode 100644 index 0000000..06a938e --- /dev/null +++ b/src/public/Add-ClickToEveryButton.ps1 @@ -0,0 +1,60 @@ +<# +.SYNOPSIS + Adds a Click event handler to every button within a given element. + +.DESCRIPTION + The Add-ClickToEveryButton function traverses the visual tree of a given element and adds a Click event handler to every button found. Specific buttons can be ignored based on their names. + +.PARAMETER Element + The root element to start the search. + +.PARAMETER ClickHandler + The Click event handler to be added to each button. + +.EXAMPLE + Add-ClickToEveryButton -Element $window -ClickHandler $ClickHandler + + Traverses the visual tree of the $window element and adds the $ClickHandler Click event handler to every button found. + +.NOTES + Author: Brooks Vaughn + Date: 2025-02-18 14:22:52 +#> +Function Add-ClickToEveryButton { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + $Element, + [Parameter(Mandatory = $false)] + [System.Windows.RoutedEventHandler]$ClickHandler + ) + + # Dump Parameter Values + # $PSBoundParameters.GetEnumerator() | ForEach-Object { + # Write-Verbose "$($_.Key) = $($_.Value)" + # } + + If ($Element -is 'System.Windows.Controls.Button') { + Write-Verbose ("Add-ClickToEveryButton() Button Object: [{0}] - {1}" -f $Element.GetType().ToString(), $Element.Name) + If (-not [String]::IsNullOrEmpty($ClickHandler)) { + Write-Verbose (" Adding Click Event For: {0}" -f $Element.Content) + $Element.Add_Click($ClickHandler) + } + } Else { + Write-Verbose ("Add-ClickToEveryButton() Object: [{0}] - {1}" -f $Element.GetType().ToString(), $Element.Name) + } + + If ($Element.HasItems -or $Element.HasContent -or $Element.Child.Count -gt 0 -or $Element.Children.Count -gt 0 -or $Element.Items.Count -gt 0) { + # The logical tree can contain any type of object, not just + # instances of DependencyObject subclasses. LogicalTreeHelper + # only works with DependencyObject subclasses, so we must be + # sure that we do not pass it an object of the wrong type. + $depObj = $Element + If ($null -ne $depObj) { + ForEach ($logicalChild in ([System.Windows.LogicalTreeHelper]::GetChildren($depObj))) { + Add-ClickToEveryButton -Element $logicalChild -ClickHandler:$ClickHandler + } + } + } +} + diff --git a/src/public/Add-ClickToEveryMenuItem.ps1 b/src/public/Add-ClickToEveryMenuItem.ps1 new file mode 100644 index 0000000..37898a7 --- /dev/null +++ b/src/public/Add-ClickToEveryMenuItem.ps1 @@ -0,0 +1,62 @@ +<# +.SYNOPSIS + Adds a Click event handler to every menu item within a given menu object. + +.DESCRIPTION + The Add-ClickToEveryMenuItem function traverses the visual tree of a given menu object and adds a Click event handler to every menu item found. Specific menu items can be ignored based on their properties. + +.PARAMETER MenuObj + The root menu object to start the search. + +.PARAMETER Handler + The Click event handler to be added to each menu item. + +.EXAMPLE + Add-ClickToEveryMenuItem -MenuObj $menu -Handler $handler + + Traverses the visual tree of the $menu object and adds the $handler Click event handler to every menu item found. + +.NOTES + Author: Brooks Vaughn + Date: 2025-02-18 14:22:52 +#> +Function Add-ClickToEveryMenuItem { + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + $MenuObj, + [Parameter(Mandatory = $true)] + [ScriptBlock]$Handler + ) + + # Dump Parameter Values + $PSBoundParameters.GetEnumerator() | ForEach-Object { + Write-Verbose "$($_.Key) = $($_.Value)" + } + + If ([String]::IsNullOrEmpty($Handler)) { + Write-Warning "Parameter -Handler `$Handler cannot be blank" + Return + } + If ([String]::IsNullOrEmpty($MenuObj)) { + Write-Warning "Parameter -MenuObj `$MenuObj cannot be blank" + Return + } + + Write-Verbose ("Menu Object: [{0}] - {1}" -f $MenuObj.Name, $MenuObj.GetType().ToString()) + ForEach ($child in $MenuObj.Items) { + Write-Verbose (" {0} - [{1}]" -f $child.Header, $child.GetType().ToString()) + If ($child.HasItems) { + Add-ClickToEveryMenuItem -MenuObj $child -Handler:$Handler + } Else { + If ($child -is 'System.Windows.Controls.MenuItem') { + If (-not [String]::IsNullOrEmpty($Handler)) { + Write-Verbose (" Adding Click Event For: {0}" -f $child.Content) + $child.Add_Click($Handler) + } Else { + $child.Add_Click($null) + } + } + } + } +} diff --git a/src/public/Add-ClickToMenuItem.ps1 b/src/public/Add-ClickToMenuItem.ps1 deleted file mode 100644 index 85e6f2f..0000000 --- a/src/public/Add-ClickToMenuItem.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -Function Add-ClickToMenuItem() { - Param ( - $MenuObj, - [ScriptBlock]$Handler - ) - If ([String]::IsNullOrEmpty($Handler)) { - Write-Warning "Parameter -Handler `$Handler cannot be blank" - Return - } - If ([String]::IsNullOrEmpty($MenuObj)) { - Write-Warning "Parameter -MenuObj `$MenuObj cannot be blank" - Return - } - Write-Verbose ("Menu Object: [{0}] - {1}" -f $MenuObj.Name, $MenuObj.GetType().ToString()) - ForEach ($child in $MenuObj.Item) { - Write-Verbose (" {0} - [{1}]" -f $child.Header, $child.GetType().ToString()) - If ($child.HasItems) { - Add-ClickToMenuItems -MenuObj $child -Handler:$handler - } Else { - If ($child -is 'System.Windows.Controls.MenuItem') { - $child.Add_Click($handler) - } - } - } -} diff --git a/src/public/Add-EventsToEveryCheckBox.ps1 b/src/public/Add-EventsToEveryCheckBox.ps1 new file mode 100644 index 0000000..0006364 --- /dev/null +++ b/src/public/Add-EventsToEveryCheckBox.ps1 @@ -0,0 +1,126 @@ +<# +.SYNOPSIS + Adds event handlers to every checkbox within a given element. + +.DESCRIPTION + The Add-EventsToEveryCheckBox function traverses the visual tree of a given element and adds event handlers to every checkbox found. Specific event handlers can be added based on the properties of the checkboxes. + +.PARAMETER Element + The root element to start the search. + +.PARAMETER ClickHandler + The Click event handler to be added to each checkbox. + +.PARAMETER CheckedHandler + The Checked event handler to be added to each checkbox. + +.PARAMETER UncheckedHandler + The Unchecked event handler to be added to each checkbox. + +.PARAMETER PreviewMouseUpHandler + The PreviewMouseUp event handler to be added to each TreeViewItem. + +.PARAMETER PreventSelectionScrolling + A switch to prevent horizontal content scrolling when an item is clicked. + +.EXAMPLE + Add-EventsToEveryCheckBox -Element $window -ClickHandler $clickHandler + + Traverses the visual tree of the $window element and adds the $clickHandler Click event handler to every checkbox found. + +.NOTES + Author: Brooks Vaughn + Date: 2025-02-18 14:22:52 +#> +Function Add-EventsToEveryCheckBox { + [CmdletBinding()] + Param( + [Parameter(Mandatory = $true)] + $Element, + [Parameter(Mandatory = $false)] + [System.Windows.RoutedEventHandler]$ClickHandler = $null, + [Parameter(Mandatory = $false)] + [System.Windows.RoutedEventHandler]$CheckedHandler = $null, + [Parameter(Mandatory = $false)] + [System.Windows.RoutedEventHandler]$UncheckedHandler = $null, + [Parameter(Mandatory = $false)] + [System.Windows.Input.MouseButtonEventHandler]$PreviewMouseUpHandler = $null, + [Switch]$PreventSelectionScrolling + ) + + # Dump Parameter Values + # $PSBoundParameters.GetEnumerator() | ForEach-Object { + # Write-Verbose "$($_.Key) = $($_.Value)" + # } + + Write-Verbose ("Add-EventsToEveryCheckBox(): [{0}] : ({1})" -f $($Element.GetType().ToString()), $($Element.Name)) + + If ($Element -is 'System.Windows.Controls.TreeViewItem') { + $Element.IsExpanded = $true # "True" + If ($null -ne $PreviewMouseUpHandler) { + $Element.Add_PreviewMouseUp($PreviewMouseUpHandler) + } + If ($PreventSelectionScrolling) { + # This prevents horizontal content scrolling when an item is clicked + # Suppress PSScriptAnalyzer rule for assignment to automatic variable + $Element.Add_RequestBringIntoView({ + # PSScriptAnalyzer rule suppression for $sender + # [Diagnostics.CodeAnalysis.SuppressMessage("PSAvoidAssignmentToAutomaticVariable", "AvoidAssignmentToAutomaticVariable", Justification="The `$sender variable is required for event handlers.")] + # [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidAssignmentToAutomaticVariable", "")] + # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'sender', Justification='Assignment is by design.')] + # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'sender', Justification='Assignment is by design.')] + # [Diagnostics.CodeAnalysis.SuppressMessage('sender','PSAvoidAssignmentToAutomaticVariable',Justification='Assignment is by design.')] + # [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidAssignmentToAutomaticVariable','',Scope='Namespace')] + # [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidAssignmentToAutomaticVariable','sender',Justification='Assignment is by design.')] + # Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable','sender',Justification='Assignment is by design.')] + # Get-ScriptAnalyzerRule -Name 'PSAvoidAssignmentToAutomaticVariable' | Format-List + # [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable','$sender',Justification='Assignment is by design.')] + # [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidAssignmentToAutomaticVariable", "")] + # [CmdletBinding()] + Param( + # [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidAssignmentToAutomaticVariable','',Justification='Assignment is by design.')] + # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'Assignment is by design.')] + # [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable','$sender',Justification='Assignment is by design.')] + [object]$theSender, + [System.Windows.RequestBringIntoViewEventArgs]$e + ) + Write-Verbose ("`$Element.Add_RequestBringIntoView {0}: {1}({2})" -f $theSender.Name, $e.Source.Name, $e.ToString()) + # Mark the event as handled + $e.Handled = $true + }) + } + <# + $Count = [System.Windows.Media.VisualTreeHelper]::GetChildrenCount($Element) + For ($i=0; $i -lt $Count; $I++) { + $current = [System.Windows.Media.VisualTreeHelper]::GetChild($Element, $i); + Add-EventsToEveryCheckBox -Element $current -ClickHandler:$ClickHandler -CheckedHandler:$CheckedHandler -UncheckedHandler:$UncheckedHandler + } + #> + Write-Verbose (" Header Name: {0}, Type: {1}" -f $Element.Header.Type, $Element.Header.Name) + <# + If ($Element.Header.Type -eq 'Server') { + $current = [System.Windows.Media.VisualTreeHelper]::GetChild($Element, 0); + Add-EventsToEveryCheckBox -Element $current -ClickHandler:$ClickHandler -CheckedHandler:$CheckedHandler -UncheckedHandler:$UncheckedHandler -PreviewMouseUpHandler:$PreviewMouseUpHandler + } + #> + } ElseIf ($Element -is 'System.Windows.Controls.TextBlock') { + Write-Verbose (" TextBlock: {0}" -f $($Element.Inlines.Text | Out-String)) + } ElseIf ($Element -is 'System.Windows.Controls.CheckBox') { + Write-Verbose (" Adding CheckBox Event Handlers For: " + $Element.Content.Inlines.text -join "") + If (-not [Sting]::IsNullOrEmpty($ClickHandler)) { + $Element.Add_Click($ClickHandler) + } + If (-not [Sting]::IsNullOrEmpty($CheckedHandler)) { + $Element.Add_Checked($CheckedHandler) + } + If (-not [Sting]::IsNullOrEmpty($UncheckedHandler)) { + $Element.Add_Unchecked($UncheckedHandler) + } + } + + $Count = [System.Windows.Media.VisualTreeHelper]::GetChildrenCount($Element) + For ($i=0; $i -lt $Count; $i++) { + $current = [System.Windows.Media.VisualTreeHelper]::GetChild($Element, $i) + Add-EventsToEveryCheckBox -Element $current -ClickHandler:$ClickHandler -CheckedHandler:$CheckedHandler -UncheckedHandler:$UncheckedHandler -PreviewMouseUpHandler:$PreviewMouseUpHandler + } +} diff --git a/src/public/Build-HandlerCode.ps1 b/src/public/Build-HandlerCode.ps1 new file mode 100644 index 0000000..1d35b60 --- /dev/null +++ b/src/public/Build-HandlerCode.ps1 @@ -0,0 +1,64 @@ + +Function Build-HandlerCode { + Param ( + [System.Object]$Element, + [String]$ControlType + ) + If ([String]::IsNullOrEmpty($Element)) { + Write-Warning "Parameter -Element is empty or missing" + Return + } + If ([String]::IsNullOrEmpty($ControlType)) { + Write-Host "Parameter -ControlType is empty or missing" -ForegroundColor Red + Return + } + + $eol = [System.Environment]::NewLine + $sb = [System.Text.StringBuilder]::new() + + $handlerName = "`$handler_$($ControlType.Split('.')[-1])_Click" + + # Starting Code + [void]$sb.AppendLine(@" +$handlerName = { + Param ([object]`$theSender, [System.EventArgs]`$e) + Write-Host ("``$handlerName() Item clicked: {0}" -f `$theSender.Name) + Switch -Regex (`$theSender.Name) { +"@ + $eol) + + # Body Code consists of Regex patterns of the Item Element Name it finds of matching ControlTypes + # $elements = Find-EveryMatchingElement -Element:$Element -ControlType:$ControlType -Verbose + # Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.MenuItem' + # Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.RadioButton' + # Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Combobox' + # Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Button' + # Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.MenuItem' + # Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Primitives.ToggleButton' + + $elements = Find-EveryControl -Element $form -ControlType $ControlType + ForEach ($element in $elements) { + If (-not [String]::IsNullOrEmpty($element)) { + [void]$sb.AppendLine(@" + '^$($element.Name)$' { + Break + } +"@) + } + } + + # Ending Code + [void]$sb.AppendLine(@" + default { + Write-Host ("{0}: {1}({2})" -f `$theSender.Name, `$e.OriginalSource.Name, `$e.OriginalSource.ToString()) + } + } +} +"@) + + $sb.ToString() + +} + + +# Build-HandlerCode -Element $form -ControlType System.Windows.Controls.MenuItem +# Build-HandlerCode -Element $form -ControlType 'System.Windows.Controls.Primitives.ToggleButton' diff --git a/src/public/Find-EveryControl.ps1 b/src/public/Find-EveryControl.ps1 new file mode 100644 index 0000000..63336c9 --- /dev/null +++ b/src/public/Find-EveryControl.ps1 @@ -0,0 +1,74 @@ + +Function Find-EveryControl () { + [CmdletBinding()] + Param( + $ControlType, + $Element, + [Switch]$UseVisualTreeHelper, + [Switch]$IncludeAll + ) + If (-not [String]::IsNullOrEmpty($IncludeAll) -and $IncludeAll) { + [PSCustomObject]@{ + Name = $Element.Name + Type = $Element.GetType().ToString() + Text = (Get-ControlContent -Element $Element) + Element = $Element + } + } Else { + If ($Element -is $ControlType) { + Return ([PSCustomObject]@{ + Name = $Element.Name + Type = $Element.GetType().ToString() + Text = (Get-ControlContent -Element $Element) + Element = $Element + }) + } + } + If ($UseVisualTreeHelper) { + $Count = [System.Windows.Media.VisualTreeHelper]::GetChildrenCount($Element) + If ($Count -gt 0) { + For ($i=0; $i -lt $Count; $i++) { + $current = [System.Windows.Media.VisualTreeHelper]::GetChild($Element, $i) + # If (-not [String]::IsNullOrEmpty($IncludeAll) -and $IncludeAll) { + # [PSCustomObject]@{ + # Name = $Element.Name + # Type = $Element.GetType().ToString() + # Text = $Element.Content + # Element = $Element + # } + # } + Find-EveryControl -Element:$current -ControlType:$ControlType -IncludeAll:$IncludeAll + } + } + } Else { + If ($Element.HasContent) { + ForEach ($item in $Element.Content) { + Find-EveryControl -Element:$item -ControlType:$ControlType -IncludeAll:$IncludeAll + } + } + If ($Element.Children) { + ForEach ($child in $Element.Children) { + Find-EveryControl -Element:$child -ControlType:$ControlType -IncludeAll:$IncludeAll + } + } + If ($Element.HasItems) { + ForEach ($item in $Element.Items) { + Find-EveryControl -Element:$item -ControlType:$ControlType -IncludeAll:$IncludeAll + } + } + } +} + +# Find-EveryControl -Element $WPF_menuMasterDataGrid -ControlType 'System.Windows.Controls.MenuItem' +# Find-EveryControl -Element $WPF_menuDetailDataGrid -ControlType 'System.Windows.Controls.MenuItem' +# Find-EveryControl -Element $WPF_tabControl -ControlType 'System.Windows.Controls.Button' +# Find-EveryControl -Element $WPF_gridSqlQueryEditor -ControlType 'System.Windows.Controls.Primitives.ToggleButton' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.MenuItem' +# Find-EveryControl -Element $WPF_tabControl -ControlType 'System.Windows.Controls.Primitives.ToggleButton' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Primitives.ToggleButton' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.MenuItem' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.RadioButton' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Combobox' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Button' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.MenuItem' +# Find-EveryControl -Element $form -ControlType 'System.Windows.Controls.Primitives.ToggleButton' diff --git a/src/public/Format-XML.ps1 b/src/public/Format-XML.ps1 new file mode 100644 index 0000000..517a0ae --- /dev/null +++ b/src/public/Format-XML.ps1 @@ -0,0 +1,69 @@ +<# +.SYNOPSIS + Formats an XML string with indentation and optional attribute formatting and XML declaration. + +.DESCRIPTION + The Format-XML function formats an XML string with a specified indentation level. It provides options to format attributes with new lines and to include or omit the XML declaration. + +.PARAMETER xml + The XML string to be formatted. + +.PARAMETER indent + The number of spaces to use for indentation. Default is 4. + +.PARAMETER FormatAttributes + A switch to format attributes with new lines. + +.PARAMETER IncludeXmlDeclaration + A switch to include the XML declaration. + +.EXAMPLE + Format-XML -xml $inputXML -indent 4 + + Formats the XML string $inputXML with an indentation level of 4 spaces. + +.EXAMPLE + Format-XML -xml $inputXML -indent 4 -FormatAttributes + + Formats the XML string $inputXML with an indentation level of 4 spaces and formats attributes with new lines. + +.EXAMPLE + Format-XML -xml $inputXML -indent 4 -IncludeXmlDeclaration + + Formats the XML string $inputXML with an indentation level of 4 spaces and includes the XML declaration. + +.NOTES + Author: Brooks Vaughn + Date: 2025-02-18 14:22:52 +#> +function Format-XML { + [CmdletBinding()] + Param ( + [parameter(Mandatory=$true,ValueFromPipeLine=$true)] + [xml]$xml, + [parameter(Mandatory=$false,ValueFromPipeLine=$false)] + $indent = 4, + [switch]$FormatAttributes, + [switch]$IncludeXmlDeclaration + ) + Process { + $StringWriter = New-Object System.IO.StringWriter + $XmlWriterSettings = New-Object System.Xml.XmlWriterSettings + $XmlWriterSettings.Indent = $true + $XmlWriterSettings.IndentChars = " " * $indent + If (-not [Sting]::IsNullOrEmpty($IncludeXmlDeclaration)) { + $XmlWriterSettings.OmitXmlDeclaration = $true + } + If (-not [Sting]::IsNullOrEmpty($FormatAttributes)) { + $XmlWriterSettings.NewLineOnAttributes = $true + } + $XmlWriter = [System.Xml.XmlWriter]::Create($StringWriter, $XmlWriterSettings) + $xml.WriteTo($XmlWriter) + $XmlWriter.Flush() + $StringWriter.Flush() + $XmlWriter.Close() + $Result = $StringWriter.ToString() + $StringWriter.Dispose() + $Result + } +} diff --git a/src/public/Get-ControlContent.ps1 b/src/public/Get-ControlContent.ps1 new file mode 100644 index 0000000..92a9848 --- /dev/null +++ b/src/public/Get-ControlContent.ps1 @@ -0,0 +1,12 @@ +Function Get-ControlContent { + Param ( + $Element + ) + Switch ($Element) { + {$_.Content} {Return $_.Content} + {$_.Header} {Return $_.Header} + {$_.Text} {Return $_.Text} + {$_.SelectedItem} {Return $_.SelectedItem} + Default {Return $_.Name} + } +} diff --git a/src/public/Get-ObjectPropertyDetail.ps1 b/src/public/Get-ObjectPropertyDetail.ps1 new file mode 100644 index 0000000..a225ed1 --- /dev/null +++ b/src/public/Get-ObjectPropertyDetail.ps1 @@ -0,0 +1,52 @@ +<# +.SYNOPSIS + Returns a PSObject containing details about an object's properties. + +.DESCRIPTION + The Get-ObjectPropertyDetail function returns a PSObject that lists the properties of the input object along with their names, values, and types. + +.PARAMETER InputObject + The object whose properties are to be detailed. + +.EXAMPLE + $details = Get-ObjectPropertyDetail -InputObject $form1 + $details | Format-Table -AutoSize + $details | Format-Table -AutoSize -Force -Wrap -Property Property, Value, Type + + Retrieves the properties of the object $form1 and displays them in a formatted table. + +.NOTES + Author: Brooks Vaughn + Date: 2025-02-18 15:41:05 +#> +Function Get-ObjectPropertyDetail { + [CmdletBinding()] + Param ( + [parameter(Mandatory = $true, ValueFromPipeline = $true)] + [Object]$InputObject + ) + Begin { + $ReturnObject = [System.Collections.Generic.List[PSCustomObject]]::new() + } + Process { + ForEach ($property in $InputObject.PSObject.Properties) { + $typeName = [Microsoft.PowerShell.ToStringCodeMethods]::Type([type]$property.TypeNameOfValue) + $value = $property.Value + + If ($typeName -like '*IScriptExtent*') { + $file = if ($null -eq $value.File) { "" } else { Split-Path -Leaf $value.File } + $value = "{0} ({1},{2})-({3},{4})" -f $file, $value.StartLineNumber, $value.StartColumnNumber, $value.EndLineNumber, $value.EndColumnNumber + } + + [void]$ReturnObject.Add([PSCustomObject]@{ + Property = $property.Name + Value = $value + Type = $typeName + }) + } + } + End { + $ReturnObject + } +} + diff --git a/src/public/New-XamlWindow.ps1 b/src/public/New-XamlWindow.ps1 index cbff238..e8de7e6 100644 --- a/src/public/New-XamlWindow.ps1 +++ b/src/public/New-XamlWindow.ps1 @@ -11,9 +11,14 @@ GuiMyPS - Module helps in creating and running WPF GUI based PowerShell Applicat #> Function New-XamlWindow { + [CmdletBinding(SupportsShouldProcess=$true)] Param ( - $xaml, - $NoXRemoval + [Parameter(Mandatory=$true)] + [Alias("InputObject")] + [System.Object]$xaml, + + [Parameter(Mandatory=$false)] + [switch]$NoXRemoval ) # Load the necessary assemblies Add-Type -AssemblyName PresentationFramework @@ -30,15 +35,24 @@ Function New-XamlWindow { Function Remove-XName { [CmdletBinding(SupportsShouldProcess=$true)] Param ( - $xamlString, - $NoXRemoval + [Parameter(Mandatory=$true)] + [string]$xamlString, + + [Parameter(Mandatory=$false)] + [switch]$NoXRemoval ) + If ($NoXRemoval) { $xamlString } Else { - ((($xamlString -replace 'mc:Ignorable="d"','') -replace "x:Na",'Na') -replace '^' -ForEach $files { $null = [System.Management.Automation.PSParser]::Tokenize($psFile, [ref]$errors) $errors.Count | Should -Be 0 } - It 'passess ScriptAnalyzer' { + It 'passes ScriptAnalyzer' { $saResults = Invoke-ScriptAnalyzer -Path $_ -Settings $ScriptAnalyzerSettings $saResults | Should -BeNullOrEmpty -Because $($saResults.Message -join ';') } diff --git a/tests/Test-Usage-01.ps1 b/tests/Test-Usage-01.ps1 new file mode 100644 index 0000000..6f9608d --- /dev/null +++ b/tests/Test-Usage-01.ps1 @@ -0,0 +1,180 @@ + + +Import-Module C:\Git\GuiMyPS\dist\GuiMyPS\GuiMyPS.psd1 -Verbose -Force +# Remove-Module -Name GuiMyPS -Force -Verbose +# Import-Module -Name GuiMyPS -Force -Verbose + +# . "C:\Git\GuiMyPS\src\public\New-XamlWindow.ps1" + +$Error.Clear() + +<# Example with Events defined # > +$inputXML = @" + + + +