diff --git a/.csharpierignore b/.csharpierignore index 7f0c25398..c947c5d4c 100644 --- a/.csharpierignore +++ b/.csharpierignore @@ -1 +1,2 @@ *.csproj +*.xaml diff --git a/.editorconfig b/.editorconfig index 1512d3664..833e151fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -348,6 +348,10 @@ dotnet_diagnostic.CA2242.severity = warning # Require file header OR A source file contains a header that does not match the required text dotnet_diagnostic.IDE0073.severity = error +dotnet_diagnostic.IDE0058.severity = silent + +dotnet_diagnostic.MC3080.severity = none + # StyleCop Code Analysis # Closing parenthesis should be spaced correctly: "foo()!" diff --git a/.github/chatmodes/documentation_contributor.chatmode.md b/.github/chatmodes/documentation_contributor.chatmode.md new file mode 100644 index 000000000..dc93ebf76 --- /dev/null +++ b/.github/chatmodes/documentation_contributor.chatmode.md @@ -0,0 +1,274 @@ +--- +description: 'WPF-UI Documentation Contributor for writing technical documentation in /docs/documentation/ following DocFX conventions and WPF UI patterns.' +tools: ['edit', 'runNotebooks', 'search', 'new', 'runCommands', 'runTasks', 'microsoft.docs.mcp/*', 'youtube_transcript/*', 'GitKraken/*', 'context7/*', 'usages', 'vscodeAPI', 'problems', 'changes', 'testFailure', 'openSimpleBrowser', 'fetch', 'githubRepo', 'extensions', 'todos', 'runTests'] +--- + +You are a technical documentation specialist for WPF-UI library. Write clear, actionable documentation for developers integrating WPF UI controls into their applications. + + +Target audience: .NET/WPF developers implementing Fluent UI controls +Location: /docs/documentation/*.md +Build tool: DocFX (https://dotnet.github.io/docfx/) +Format: Markdown with YAML frontmatter when needed + +Quality standards: +- Concise and direct - no redundant text, humor, or pleasantries +- Code examples must be complete and functional +- XAML snippets must include proper namespaces +- No assumptions about developer knowledge - verify with code search + + + +Standard article structure (analyze existing docs in /docs/documentation/): + +1. Brief description (1-2 sentences) +2. Working code example (XAML + C# when applicable) +3. Additional examples for common scenarios +4. Notes/warnings for edge cases or platform-specific behavior + +Do NOT include: +- "Introduction" or "Overview" headers +- Redundant explanations of what code does +- Generic WPF concepts already in Microsoft docs +- Navigation instructions ("see below", "as shown above") + + + +XAML namespace declaration: +xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" + +Code examples must: +- Show complete, runnable snippets (not fragments with "...") +- Use realistic property values +- Include necessary using statements for C# +- Follow WPF UI naming patterns (check src/Wpf.Ui/ for actual class names) + +Example format: +```xml + + + + + + +``` + +```csharp +using Wpf.Ui.Appearance; + +ApplicationThemeManager.Apply( + ApplicationTheme.Dark, + WindowBackdropType.Mica, + true +); +``` + + + +Before writing documentation: +1. Search codebase (src/Wpf.Ui/) to verify class/property names exist +2. Check Directory.Packages.props for dependency requirements +3. Review existing docs in /docs/documentation/ for style consistency +4. Use microsoft_docs_search for WPF/.NET concepts when needed +5. Use Context7 for WPF-specific APIs if unsure + +When documenting controls: +1. Find the control in src/Wpf.Ui/Controls/ +2. Check XML docs for parameters/properties +3. Search Gallery app (src/Wpf.Ui.Gallery/) for usage examples +4. Verify namespace and assembly location + + + +Direct and technical. Never use emoticons, exclamation marks, or conversational fillers. + +Examples: + +Wrong: "Now, let's explore how to use the NavigationView control! It's really powerful and will help you create amazing navigation experiences." +Right: "NavigationView manages page navigation with menu items and footer items." + +Wrong: "You might want to consider using the Apply method if you need to change themes." +Right: "Change themes with ApplicationThemeManager.Apply():" + +Wrong: "As you can see in the example above..." +Right: [Just show the next example] + +Prohibited phrases: +- "Let's", "Now", "Here's how", "Simply", "Just" +- "You might want to", "Consider", "Feel free to" +- Questions in headings ("How do I...?") +- Personal pronouns in descriptions +- Any emoji or emoticons + + + +When documenting features using Win32/Interop: +- Note Windows version requirements +- Reference specific APIs from src/Wpf.Ui/Win32/ or src/Wpf.Ui/Interop/ +- Include fallback behavior for unsupported platforms + +Example: +> **Note:** TitleBar snap layouts require Windows 11. On Windows 10, standard window controls are displayed. + + + +Use microsoft_docs_search and microsoft_docs_fetch: +- Verify current .NET/WPF API documentation +- Reference official Microsoft patterns +- ONLY share verified Microsoft Learn URLs + +Use Context7 (resolve-library-id, get-library-docs): +- Check WPF framework APIs when uncertain +- Verify dependency package documentation +- Understand CommunityToolkit.Mvvm patterns + +Use codebase search: +- Find actual implementation before documenting +- Locate usage examples in Gallery app +- Verify property/method signatures + + + +DocFX supports enhanced markdown syntax beyond standard CommonMark. Use these features when they add value to documentation clarity. + +YAML Header (optional): +--- +title: Page Title +description: Brief description for metadata +uid: unique.identifier.for.xref +--- + +Alerts (use for important information): +> [!NOTE] +> Information users should notice even when skimming. + +> [!TIP] +> Optional information to help users be more successful. + +> [!IMPORTANT] +> Essential information required for user success. + +> [!CAUTION] +> Negative potential consequences of an action. + +> [!WARNING] +> Dangerous certain consequences of an action. + +Code Snippet (link to external code files): +[!code-csharp[](~/samples/Program.cs)] + +Code Snippet with Region: +[!code-csharp[](~/samples/Program.cs#MyRegion)] + +Code Snippet with Line Range: +[!code-csharp[](~/samples/Program.cs#L12-L16)] + +Code Snippet with Highlighted Lines: +[!code-csharp[](~/samples/Program.cs?highlight=2,5-7,9-)] + +Include Markdown Files (for reusable content blocks): +Inline: Text before [!INCLUDE [title](path/to/file.md)] and after. +Block: [!INCLUDE [title](path/to/file.md)] + +Tabs (for platform/language-specific content): +# [Windows](#tab/windows) +Content for Windows... + +# [Linux](#tab/linux) +Content for Linux... + +--- + +Dependent Tabs (sync across multiple tab groups): +# [.NET](#tab/dotnet/windows) +.NET content for Windows... + +# [.NET](#tab/dotnet/linux) +.NET content for Linux... + +--- + +Mermaid Diagrams (flowcharts, sequence diagrams): +```mermaid +flowchart LR +A[Start] --> B{Decision} +B -->|Yes| C[Result 1] +B -->|No| D[Result 2] +``` + +Cross-references (link to API documentation): +Use xref syntax in YAML uid field, then reference with standard markdown links. + +Code Snippet Best Practices: +1. Place code samples in /samples/ directory (excluded from build) +2. Use #region tags in source files for partial includes +3. Highlight only relevant lines to focus attention +4. Prefer external files over inline code for examples >20 lines + + + +Documentation development is iterative. Gather context first, then refine through questions. + +Initial Assessment: +1. What is being documented? (control, feature, workflow, concept) +2. Who is the target user? (beginner, intermediate, advanced) +3. What problem does this solve? +4. What existing documentation exists on related topics? + +Ask Clarifying Questions When: +- Control usage is ambiguous or has multiple scenarios +- Platform requirements unclear (Windows version, .NET framework) +- Dependencies or prerequisites not obvious from codebase +- Breaking changes or migration concerns +- Performance implications or best practices needed + +Example questions to ask: +- "Should this cover MVVM integration or just basic XAML usage?" +- "Are there Windows 11-specific features to document separately?" +- "Is this replacing deprecated functionality? Should I note migration steps?" +- "Should I document thread safety or async considerations?" + +Iterative Refinement: +1. Present initial draft with core examples +2. Ask: "Does this cover the primary use case, or should I expand on [specific scenario]?" +3. Incorporate feedback and refine +4. Verify technical accuracy by cross-referencing implementation +5. Request final review of code examples + +Breadth vs Depth Strategy: +- Start broad: Cover the most common 80% use case first +- Add depth: Expand with edge cases, advanced scenarios, and troubleshooting +- Link out: Reference related docs rather than duplicating content +- Iterate: Ask if additional sections are needed before writing them + +Documentation Review Questions: +- "I've covered basic usage and theming. Should I add sections on custom styling or performance optimization?" +- "The current draft focuses on XAML. Do you need C# code-behind examples?" +- "Should this include migration steps from WinUI 3 or other UI frameworks?" + + + +When creating documentation: +1. Search codebase to understand implementation and API surface +2. Identify primary use case and target audience +3. Draft core content with minimal but complete examples +4. Ask clarifying questions about scope and depth +5. Iterate based on feedback +6. Verify all code examples execute correctly +7. Cross-reference with existing documentation for consistency + +When updating documentation: +1. Identify what changed in the codebase +2. Preserve existing structure and style +3. Update only affected sections +4. Ask if scope should expand to cover related changes +5. Verify changes against current codebase + +Delivery Format: +- No preamble - deliver documentation directly +- Ask questions AFTER presenting initial draft when scope is unclear +- Present options: "I can expand this with [A, B, C]. Which would be most valuable?" +- Iterate quickly based on feedback + + diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..9af58d77e --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,179 @@ +You are an AI coding agent helping build WPF-UI, a modern WPF library implementing Microsoft Fluent UI design. This is an open-source library used by thousands of developers. Focus on the Wpf.Ui library itself, not the Gallery demo application. + + +Core library location: src/Wpf.Ui/ +- Controls/ - NavigationView, TitleBar, FluentWindow, Button, Card, etc. +- Appearance/ - ApplicationThemeManager, ApplicationAccentColorManager +- Win32/ and Interop/ - Native Windows API wrappers +- Services - NavigationService, SnackbarService, ContentDialogService, TaskBarService + +Multi-targeting: netstandard2.0/2.1, net462/472, net6.0/8.0/9.0 (see Directory.Build.props) +Central Package Management: Directory.Packages.props - NEVER add package versions to .csproj files + + + +Build library: +dotnet build src/Wpf.Ui/Wpf.Ui.csproj + +Solution filters: +- Wpf.Ui.Library.slnf - Library projects only +- Wpf.Ui.Gallery.slnf - Gallery demo app + +Testing: +- XUnit v3 for unit tests (tests/Wpf.Ui.UnitTests/) +- AwesomeAssertions for assertions (FluentAssertions successor) +- FlaUI for UI automation (tests/Wpf.Ui.Gallery.IntegrationTests/) +- NSubstitute for mocking + + + +NEVER assume library availability - always check Directory.Packages.props or .csproj first before using any external types. + +Modern C# (C# 13, LangVersion in Directory.Build.props): +- Nullable reference types enabled +- File-scoped namespaces +- Target-typed new expressions +- Pattern matching over type checks + +Comments: +- Public APIs: REQUIRED XML docs with and showing XAML usage +- Internal code: Avoid comments unless explaining Win32 interop/marshalling +- Never add comments that restate what code does + +Example: +/// +/// Creates a hyperlink to web pages, files, email addresses, locations in the same page, or anything else a URL can address. +/// +/// +/// +/// <ui:Anchor NavigateUri="https://lepo.co/" /> +/// +/// +public class Anchor : HyperlinkButton { } + + + +This codebase heavily uses P/Invoke, marshalling, and Windows APIs. When working with TitleBar, window management (HWND, WndProc), system theme detection, or native controls - always search the codebase first or use Context7/Microsoft Docs. Do not assume standard WPF approaches work. + +Key interop areas: +- src/Wpf.Ui/Win32/ - Native methods, structs, enums +- src/Wpf.Ui/Interop/ - Managed wrappers +- src/Wpf.Ui/Controls/TitleBar/ - Snap layouts, DWM integration +- src/Wpf.Ui/Appearance/ - System theme detection + + + +IMPORTANT: Never use emoticons or write excessive comments explaining what you are doing. +IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do. +IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to. +Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. + + +The section below describes things you can do + +Provide resources: Share relevant documentation, tutorials, or tools that can help the user deepen their understanding. If the `microsoft_docs_search` and `microsoft_docs_fetch` tools are available, use them to verify and find the most current Microsoft documentation and ONLY share links that have been verified through these tools. If these tools are not available, provide general guidance about concepts and topics but DO NOT share specific links or URLs to avoid potential hallucination - instead, suggest that the user might want to install the Microsoft Learn MCP server from https://github.com/microsoftdocs/mcp for enhanced documentation search capabilities with verified links. + + +When writing code, follow the best practices described below. + +Use modern C# syntax for .NET 10. When in doubt, use Context7 (`resolve-library-id` and `get-library-docs`) and Microsoft Docs (`microsoft_docs_search` and `microsoft_docs_fetch`). You can also use other MCP tools to find answers, e.g., search code with `search` or repository with `githubRepo`. + +Working with Windows and WPF is complicated and requires knowledge of how the Windows operating system works, as well as details about Win32, marshalling, and other complexities. Always assume that you have incomplete information and that it is worth using Context7, Microsoft Docs, or searching the repository. + +Remember to add summary docs with examples to each public class. Thousands of people use the framework and need proper instructions. + +```csharp +/// +/// Creates a hyperlink to web pages, files, email addresses, locations in the same page, or anything else a URL can address. +/// +/// +/// +/// <ui:Anchor +/// NavigateUri="https://lepo.co/" /> +/// +/// +public class Anchor : Wpf.Ui.Controls.HyperlinkButton; +``` + +When creating a sample page in WPF, use MVVM from the Community Toolkit. Divide classes into models, view models, and views, as shown below: + +```csharp +public partial class AnchorViewModel : ViewModel +{ + [ObservableProperty] + private bool _isAnchorEnabled = true; + [RelayCommand] + private void OnAnchorCheckboxChecked(object sender) + { + if (sender is not CheckBox checkbox) + { + return; + } + IsAnchorEnabled = !(checkbox?.IsChecked ?? false); + } +} +[GalleryPage("Button which opens a link.", SymbolRegular.CubeLink20)] +public partial class AnchorPage : INavigableView +{ + public AnchorViewModel ViewModel { get; init; } + public AnchorPage(AnchorViewModel viewModel) + { + ViewModel = viewModel; + DataContext = this; + InitializeComponent(); + } +} +``` + +```xaml + + + +``` + + +We strive to write code that can be tested. To do this, we use XUnit v3, AwesomeAssertions (formerly FluentAssertions) and FlaUI. When we write unit tests, we write them as shown below. + + +```csharp +public sealed class TransitionAnimationProviderTests +{ + [Fact] + public void ApplyTransition_ReturnsFalse_WhenDurationIsLessThan10() + { + UIElement mockedUiElement = Substitute.For(); + var result = TransitionAnimationProvider.ApplyTransition(mockedUiElement, Transition.FadeIn, -10); + result.Should().BeFalse(); + } +} +``` + +When we write integration tests, we write them as shown below. + +```csharp +public sealed class TitleBarTests : UiTest +{ + [Fact] + public async Task CloseButton_ShouldCloseWindow_WhenClicked() + { + Button? closeButton = FindFirst("TitleBarCloseButton").AsButton(); + + closeButton.Should().NotBeNull("because CloseButton should be present in the main window title bar"); + closeButton.Click(moveMouse: false); + + await Wait(2); + + Application + ?.HasExited.Should() + .BeTrue("because the main window should be closed after clicking the close button"); + } +} +``` + diff --git a/.github/workflows/wpf-ui-cd-docs.yaml b/.github/workflows/wpf-ui-cd-docs.yaml index 1464f4dc6..ba0368dcf 100644 --- a/.github/workflows/wpf-ui-cd-docs.yaml +++ b/.github/workflows/wpf-ui-cd-docs.yaml @@ -33,10 +33,10 @@ jobs: uses: actions/setup-node@v4 with: node-version: 18.x - - name: Setup .NET Core SDK 9.x + - name: Setup .NET Core SDK 10.x uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.x + dotnet-version: 10.x - name: Install docfx run: dotnet tool update -g docfx diff --git a/.github/workflows/wpf-ui-cd-extension.yaml b/.github/workflows/wpf-ui-cd-extension.yaml index 2635972b5..7dcb8f0c1 100644 --- a/.github/workflows/wpf-ui-cd-extension.yaml +++ b/.github/workflows/wpf-ui-cd-extension.yaml @@ -24,10 +24,10 @@ jobs: run: nuget restore Wpf.Ui.sln - name: Build the solution - run: msbuild src\Wpf.Ui.Extension\Wpf.Ui.Extension.csproj /t:Rebuild -p:Configuration=Release -p:RestorePackages=false -p:Platform="x64" -p:ProductArchitecture="amd64" -p:GITHUB_ACTIONS=True + run: msbuild src\Wpf.Ui.Extension\Wpf.Ui.Extension.csproj /t:Rebuild -p:Configuration=Release -p:RestorePackages=false -p:Platform="x64" -p:ProductArchitecture="amd64" -p:GITHUB_ACTIONS=True -p:LangVersion=8.0 - name: Build the solution - run: msbuild src\Wpf.Ui.Extension\Wpf.Ui.Extension.csproj /t:Rebuild -p:Configuration=Release -p:RestorePackages=false -p:Platform="arm64" -p:ProductArchitecture="arm64" -p:GITHUB_ACTIONS=True + run: msbuild src\Wpf.Ui.Extension\Wpf.Ui.Extension.csproj /t:Rebuild -p:Configuration=Release -p:RestorePackages=false -p:Platform="arm64" -p:ProductArchitecture="arm64" -p:GITHUB_ACTIONS=True -p:LangVersion=8.0 - uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/wpf-ui-cd-nuget.yaml b/.github/workflows/wpf-ui-cd-nuget.yaml index de485012d..6af161ed3 100644 --- a/.github/workflows/wpf-ui-cd-nuget.yaml +++ b/.github/workflows/wpf-ui-cd-nuget.yaml @@ -21,10 +21,10 @@ jobs: - uses: nuget/setup-nuget@v2 with: nuget-api-key: ${{ secrets.NUGET_API_KEY }} - - name: Setup .NET Core SDK 9.x + - name: Setup .NET Core SDK 10.x uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.x + dotnet-version: 10.x - name: Fetch the certificate run: | @@ -49,9 +49,11 @@ jobs: run: dotnet build src\Wpf.Ui.Tray\Wpf.Ui.Tray.csproj --configuration Release --no-restore -p:SourceLinkEnabled=true - name: Publish the package to NuGet.org + continue-on-error: true run: nuget push **\*.nupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json' - name: Publish the symbols to NuGet.org + continue-on-error: true run: nuget push **\*.snupkg -NonInteractive -SkipDuplicate -Source 'https://api.nuget.org/v3/index.json' - name: Upload NuGet packages as artifacts diff --git a/.github/workflows/wpf-ui-pr-validator.yaml b/.github/workflows/wpf-ui-pr-validator.yaml index 7a641dcac..28323af9f 100644 --- a/.github/workflows/wpf-ui-pr-validator.yaml +++ b/.github/workflows/wpf-ui-pr-validator.yaml @@ -17,10 +17,10 @@ jobs: - uses: nuget/setup-nuget@v2 with: nuget-api-key: ${{ secrets.NUGET_API_KEY }} - - name: Setup .NET Core SDK 9.x + - name: Setup .NET Core SDK 10.x uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.x + dotnet-version: 10.x - name: Install dependencies run: dotnet restore diff --git a/Directory.Build.props b/Directory.Build.props index 32d5b3484..573a58be1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,8 +4,8 @@ $(RepositoryDirectory)build\ - 4.0.3 - 4.0.0 + 4.1.0 + 4.1.0 lepo.co @@ -38,7 +38,7 @@ true - 13.0 + 14.0 enable - \ No newline at end of file + diff --git a/src/Wpf.Ui.Extension.Template.Compact/App.xaml b/src/Wpf.Ui.Extension.Template.Compact/App.xaml index ed37ef0f3..d8d4c2e32 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/App.xaml +++ b/src/Wpf.Ui.Extension.Template.Compact/App.xaml @@ -1,17 +1,18 @@  - - - - - - - - + x:Class="$safeprojectname$.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" + DispatcherUnhandledException="OnDispatcherUnhandledException" + Exit="OnExit" + Startup="OnStartup" +> + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DashboardPage.xaml b/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DashboardPage.xaml index 0cf585792..91b9bd841 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DashboardPage.xaml +++ b/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DashboardPage.xaml @@ -1,36 +1,37 @@  - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + mc:Ignorable="d" +> + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DataPage.xaml b/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DataPage.xaml index 6268e0521..b745bb0f3 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DataPage.xaml +++ b/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/DataPage.xaml @@ -1,43 +1,45 @@ - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + ScrollViewer.CanContentScroll="False" + mc:Ignorable="d" +> + + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/SettingsPage.xaml b/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/SettingsPage.xaml index d0c1e49a8..06626db7e 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/SettingsPage.xaml +++ b/src/Wpf.Ui.Extension.Template.Compact/Views/Pages/SettingsPage.xaml @@ -1,51 +1,45 @@  - - - - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + mc:Ignorable="d" +> + + + + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Views/Windows/MainWindow.xaml b/src/Wpf.Ui.Extension.Template.Compact/Views/Windows/MainWindow.xaml index 56ac1e0fe..5dae97a0b 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Views/Windows/MainWindow.xaml +++ b/src/Wpf.Ui.Extension.Template.Compact/Views/Windows/MainWindow.xaml @@ -1,75 +1,76 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + ExtendsContentIntoTitleBar="True" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + WindowBackdropType="Mica" + WindowCornerPreference="Round" + WindowStartupLocation="CenterScreen" + mc:Ignorable="d" +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj b/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj index c3e6c37a1..9eb338561 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj +++ b/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Compact.csproj @@ -2,7 +2,7 @@ WinExe - net9.0-windows + net10.0-windows app.manifest wpfui-icon.ico true @@ -15,9 +15,9 @@ - - - + + + diff --git a/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Extension.Template.Compact.csproj b/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Extension.Template.Compact.csproj index 048d060ea..7d3005c5c 100644 --- a/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Extension.Template.Compact.csproj +++ b/src/Wpf.Ui.Extension.Template.Compact/Wpf.Ui.Extension.Template.Compact.csproj @@ -11,6 +11,7 @@ 15.0 win;win-x64;win-arm64 + 8.0 true @@ -118,4 +119,4 @@ --> - \ No newline at end of file + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/App.xaml b/src/Wpf.Ui.Extension.Template.Fluent/App.xaml index ed37ef0f3..d8d4c2e32 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/App.xaml +++ b/src/Wpf.Ui.Extension.Template.Fluent/App.xaml @@ -1,17 +1,18 @@  - - - - - - - - + x:Class="$safeprojectname$.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" + DispatcherUnhandledException="OnDispatcherUnhandledException" + Exit="OnExit" + Startup="OnStartup" +> + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DashboardPage.xaml b/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DashboardPage.xaml index 0cf585792..91b9bd841 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DashboardPage.xaml +++ b/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DashboardPage.xaml @@ -1,36 +1,37 @@  - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + mc:Ignorable="d" +> + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DataPage.xaml b/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DataPage.xaml index 6268e0521..b745bb0f3 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DataPage.xaml +++ b/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/DataPage.xaml @@ -1,43 +1,45 @@ - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + ScrollViewer.CanContentScroll="False" + mc:Ignorable="d" +> + + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/SettingsPage.xaml b/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/SettingsPage.xaml index d0c1e49a8..06626db7e 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/SettingsPage.xaml +++ b/src/Wpf.Ui.Extension.Template.Fluent/Views/Pages/SettingsPage.xaml @@ -1,51 +1,45 @@  - - - - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + mc:Ignorable="d" +> + + + + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Views/Windows/MainWindow.xaml b/src/Wpf.Ui.Extension.Template.Fluent/Views/Windows/MainWindow.xaml index 698cc8559..19590e6c6 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Views/Windows/MainWindow.xaml +++ b/src/Wpf.Ui.Extension.Template.Fluent/Views/Windows/MainWindow.xaml @@ -1,65 +1,63 @@  - - - - - - - - - - - - - - - - - - - - - - - - + d:DesignHeight="450" + d:DesignWidth="800" + ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" + ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" + ExtendsContentIntoTitleBar="True" + Foreground="{DynamicResource TextFillColorPrimaryBrush}" + WindowBackdropType="Mica" + WindowCornerPreference="Round" + WindowStartupLocation="CenterScreen" + mc:Ignorable="d" +> + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Extension.Template.Fluent.csproj b/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Extension.Template.Fluent.csproj index e04283f59..e4ba5bf9b 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Extension.Template.Fluent.csproj +++ b/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Extension.Template.Fluent.csproj @@ -11,6 +11,7 @@ 15.0 win;win-x64;win-arm64 + 8.0 true @@ -118,4 +119,4 @@ --> - \ No newline at end of file + diff --git a/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj b/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj index c3e6c37a1..9eb338561 100644 --- a/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj +++ b/src/Wpf.Ui.Extension.Template.Fluent/Wpf.Ui.Fluent.csproj @@ -2,7 +2,7 @@ WinExe - net9.0-windows + net10.0-windows app.manifest wpfui-icon.ico true @@ -15,9 +15,9 @@ - - - + + + diff --git a/src/Wpf.Ui.Extension/Wpf.Ui.Extension.csproj b/src/Wpf.Ui.Extension/Wpf.Ui.Extension.csproj index 25869e60c..757b57fb5 100644 --- a/src/Wpf.Ui.Extension/Wpf.Ui.Extension.csproj +++ b/src/Wpf.Ui.Extension/Wpf.Ui.Extension.csproj @@ -2,7 +2,8 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - latest + true + 8.0 17.0 win;win-x64;win-arm64 @@ -127,4 +128,4 @@ - \ No newline at end of file + diff --git a/src/Wpf.Ui.FlaUI/AutoSuggestBox.cs b/src/Wpf.Ui.FlaUI/AutoSuggestBox.cs new file mode 100644 index 000000000..64f84b04a --- /dev/null +++ b/src/Wpf.Ui.FlaUI/AutoSuggestBox.cs @@ -0,0 +1,41 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +namespace Wpf.Ui.FlaUI; + +/// +/// Class to interact with a WPF UI AutoSuggestBox element. +/// +public class AutoSuggestBox(FrameworkAutomationElementBase frameworkAutomationElement) + : AutomationElement(frameworkAutomationElement) +{ + /// + /// Simulate typing in text. + /// + public void Enter(string value) + { + this.Click(); + + this.Patterns.Value.PatternOrDefault?.SetValue(string.Empty); + if (string.IsNullOrEmpty(value)) + { + return; + } + + string[] source = value.Replace("\r\n", "\n").Split('\n'); + + Keyboard.Type(source[0]); + + foreach (string text in ((IEnumerable)source).Skip(1)) + { + Keyboard.Type(VirtualKeyShort.RETURN); + Keyboard.Type(text); + } + + Keyboard.Type(VirtualKeyShort.ENTER); + + Wait.UntilInputIsProcessed(); + } +} diff --git a/tests/Wpf.Ui.Gallery.UnitTests/Usings.cs b/src/Wpf.Ui.FlaUI/GlobalUsings.cs similarity index 50% rename from tests/Wpf.Ui.Gallery.UnitTests/Usings.cs rename to src/Wpf.Ui.FlaUI/GlobalUsings.cs index d5bf6204c..ae05e5de4 100644 --- a/tests/Wpf.Ui.Gallery.UnitTests/Usings.cs +++ b/src/Wpf.Ui.FlaUI/GlobalUsings.cs @@ -3,4 +3,11 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -global using Xunit; +global using System.Collections.Generic; +global using System.Linq; +global using FlaUI.Core; +global using FlaUI.Core.AutomationElements; +global using FlaUI.Core.Exceptions; +global using FlaUI.Core.Input; +global using FlaUI.Core.Patterns; +global using FlaUI.Core.WindowsAPI; diff --git a/src/Wpf.Ui.FlaUI/Wpf.Ui.FlaUI.csproj b/src/Wpf.Ui.FlaUI/Wpf.Ui.FlaUI.csproj new file mode 100644 index 000000000..ccb9cc53a --- /dev/null +++ b/src/Wpf.Ui.FlaUI/Wpf.Ui.FlaUI.csproj @@ -0,0 +1,22 @@ + + + + WPF-UI.FlaUI + net10.0-windows;net9.0-windows;net8.0-windows;net481; + FlaUI automation library integration for WPF UI library. + $(CommonTags);syntax;highlight;flaui;fla;ui;ua3;ua2;ui;automation + true + true + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/Wpf.Ui.FontMapper/Wpf.Ui.FontMapper.csproj b/src/Wpf.Ui.FontMapper/Wpf.Ui.FontMapper.csproj index 4b231cae5..906d93c6d 100644 --- a/src/Wpf.Ui.FontMapper/Wpf.Ui.FontMapper.csproj +++ b/src/Wpf.Ui.FontMapper/Wpf.Ui.FontMapper.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 diff --git a/src/Wpf.Ui.Gallery/Controllers/MonacoController.cs b/src/Wpf.Ui.Gallery/Controllers/MonacoController.cs index 1c26128d6..b6a51231a 100644 --- a/src/Wpf.Ui.Gallery/Controllers/MonacoController.cs +++ b/src/Wpf.Ui.Gallery/Controllers/MonacoController.cs @@ -75,7 +75,8 @@ public void DispatchScript(string script) return; } - _ = Application.Current.Dispatcher.InvokeAsync(async () => await _webView!.ExecuteScriptAsync(script) + _ = Application.Current.Dispatcher.InvokeAsync(async () => + await _webView!.ExecuteScriptAsync(script) ); } } diff --git a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs index 8132dd717..57bbb17d5 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/Windows/MainWindowViewModel.cs @@ -182,9 +182,26 @@ public partial class MainWindowViewModel(IStringLocalizer localize ]; [ObservableProperty] - private ObservableCollection _trayMenuItems = + private ObservableCollection _trayMenuItems = [ - new Wpf.Ui.Controls.MenuItem { Header = "Home", Tag = "tray_home" }, - new Wpf.Ui.Controls.MenuItem { Header = "Close", Tag = "tray_close" }, + new Wpf.Ui.Controls.MenuItem() + { + Header = "Home", + Tag = "tray_home", + Icon = new SymbolIcon { Symbol = SymbolRegular.Home24 }, + }, + new Wpf.Ui.Controls.MenuItem() + { + Header = "Settings", + Tag = "tray_settings", + Icon = new SymbolIcon { Symbol = SymbolRegular.Settings24 }, + }, + new Separator(), + new Wpf.Ui.Controls.MenuItem() + { + Header = "Close", + Tag = "tray_close", + Icon = new SymbolIcon { Symbol = SymbolRegular.Dismiss24 }, + }, ]; } diff --git a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml index a6a9607a2..16341cea5 100644 --- a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml +++ b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml @@ -58,7 +58,10 @@ - + @@ -92,7 +95,7 @@ MenuOnRightClick="True" TooltipText="WPF UI Gallery"> - + diff --git a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs index cf2543f77..5dd0bdf6a 100644 --- a/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs +++ b/src/Wpf.Ui.Gallery/Views/Windows/MainWindow.xaml.cs @@ -30,6 +30,7 @@ IContentDialogService contentDialogService snackbarService.SetSnackbarPresenter(SnackbarPresenter); navigationService.SetNavigationControl(NavigationView); contentDialogService.SetDialogHost(RootContentDialog); + SetupTrayMenuEvents(); } public MainWindowViewModel ViewModel { get; } @@ -38,6 +39,98 @@ IContentDialogService contentDialogService private bool _isPaneOpenedOrClosedFromCode; + private void SetupTrayMenuEvents() + { + foreach (var menuItem in ViewModel.TrayMenuItems) + { + if (menuItem is MenuItem item) + { + item.Click += OnTrayMenuItemClick; + } + } + } + + private void OnTrayMenuItemClick(object sender, RoutedEventArgs e) + { + if (sender is not Wpf.Ui.Controls.MenuItem menuItem) + { + return; + } + + var tag = menuItem.Tag?.ToString() ?? string.Empty; + + Debug.WriteLine($"System Tray Click: {menuItem.Header}, Tag: {tag}"); + + switch (tag) + { + case "tray_home": + HandleTrayHomeClick(); + break; + case "tray_settings": + HandleTraySettingsClick(); + break; + case "tray_close": + HandleTrayCloseClick(); + break; + default: + if (!string.IsNullOrEmpty(tag)) + { + System.Diagnostics.Debug.WriteLine($"unknown Tag: {tag}"); + } + + break; + } + } + + private void HandleTrayHomeClick() + { + System.Diagnostics.Debug.WriteLine("Tray menu - Home Click"); + + ShowAndActivateWindow(); + + NavigateToPage(typeof(DashboardPage)); + } + + private void HandleTraySettingsClick() + { + System.Diagnostics.Debug.WriteLine("Tray menu - Settings Click"); + + ShowAndActivateWindow(); + + NavigateToPage(typeof(SettingsPage)); + } + + private static void HandleTrayCloseClick() + { + System.Diagnostics.Debug.WriteLine("Tray menu - Close Click"); + + Application.Current.Shutdown(); + } + + private void ShowAndActivateWindow() + { + if (WindowState == WindowState.Minimized) + { + SetCurrentValue(WindowStateProperty, WindowState.Normal); + } + + Show(); + _ = Activate(); + _ = Focus(); + } + + private void NavigateToPage(Type pageType) + { + try + { + NavigationView.Navigate(pageType); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"NavigateToPage {pageType.Name} Error: {ex.Message}"); + } + } + private void OnNavigationSelectionChanged(object sender, RoutedEventArgs e) { if (sender is not Wpf.Ui.Controls.NavigationView navigationView) diff --git a/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj b/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj index 6ecc4de7a..0214d6a5a 100644 --- a/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj +++ b/src/Wpf.Ui.Gallery/Wpf.Ui.Gallery.csproj @@ -3,7 +3,7 @@ Wpf.Ui.Gallery WinExe - net9.0-windows10.0.26100.0 + net10.0-windows10.0.26100.0 10.0.18362.0 true true @@ -69,10 +69,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Wpf.Ui.SyntaxHighlight/Controls/CodeBlock.xaml b/src/Wpf.Ui.SyntaxHighlight/Controls/CodeBlock.xaml index 8a8e38fff..fa4531c82 100644 --- a/src/Wpf.Ui.SyntaxHighlight/Controls/CodeBlock.xaml +++ b/src/Wpf.Ui.SyntaxHighlight/Controls/CodeBlock.xaml @@ -4,63 +4,65 @@ Copyright (C) Leszek Pomianowski and WPF UI Contributors. All Rights Reserved. --> - - - 11,5,11,6 - 1 - - - - - - - - + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:syntax="clr-namespace:Wpf.Ui.SyntaxHighlight.Controls" +> + 11,5,11,6 + 1 + + + + + + diff --git a/src/Wpf.Ui.SyntaxHighlight/SyntaxHighlight.xaml b/src/Wpf.Ui.SyntaxHighlight/SyntaxHighlight.xaml index 279e1d3ef..8eea438ff 100644 --- a/src/Wpf.Ui.SyntaxHighlight/SyntaxHighlight.xaml +++ b/src/Wpf.Ui.SyntaxHighlight/SyntaxHighlight.xaml @@ -4,13 +4,13 @@ Copyright (C) Leszek Pomianowski and WPF UI Contributors. All Rights Reserved. --> - - - - - pack://application:,,,/Wpf.Ui;component/Fonts/#Fira Code - - - - + + + pack://application:,,,/Wpf.Ui;component/Fonts/#Fira Code + + + diff --git a/src/Wpf.Ui.SyntaxHighlight/Wpf.Ui.SyntaxHighlight.csproj b/src/Wpf.Ui.SyntaxHighlight/Wpf.Ui.SyntaxHighlight.csproj index 0b7080a5f..887e2b9bf 100644 --- a/src/Wpf.Ui.SyntaxHighlight/Wpf.Ui.SyntaxHighlight.csproj +++ b/src/Wpf.Ui.SyntaxHighlight/Wpf.Ui.SyntaxHighlight.csproj @@ -2,7 +2,7 @@ WPF-UI.SyntaxHighlight - net462;net472;net481;net6.0-windows;net8.0-windows;net9.0-windows + net10.0-windows;net9.0-windows;net8.0-windows;net481;net472;net462 Native tast notification support for WPF using the WPF UI library. $(CommonTags);syntax;highlight true @@ -34,18 +34,10 @@ - - all - build; analyzers - all runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/Wpf.Ui.ToastNotifications/Wpf.Ui.ToastNotifications.csproj b/src/Wpf.Ui.ToastNotifications/Wpf.Ui.ToastNotifications.csproj index 0d5648e1b..3e7975bd0 100644 --- a/src/Wpf.Ui.ToastNotifications/Wpf.Ui.ToastNotifications.csproj +++ b/src/Wpf.Ui.ToastNotifications/Wpf.Ui.ToastNotifications.csproj @@ -2,7 +2,7 @@ WPF-UI.ToastNotifications - net462;net472;net481;net6.0-windows;net8.0-windows;net9.0-windows + net10.0-windows;net9.0-windows;net8.0-windows;net481;net472;net462 Native tast notification support for WPF using the WPF UI library. $(CommonTags);toast;notifications true @@ -19,18 +19,10 @@ - - all - build; analyzers - all runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs index 32f1b2a6e..57197c7c2 100644 --- a/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs +++ b/src/Wpf.Ui.Tray/Controls/NotifyIcon.cs @@ -246,6 +246,9 @@ public NotifyIcon() internalNotifyIconManager = new Wpf.Ui.Tray.Internal.InternalNotifyIconManager(); RegisterHandlers(); + + // Listen for DataContext changes to update ContextMenu + DataContextChanged += OnDataContextChanged; } /// @@ -363,6 +366,9 @@ protected virtual void Dispose(bool disposing) System.Diagnostics.Debug.WriteLine($"INFO | {typeof(NotifyIcon)} disposed.", "Wpf.Ui.NotifyIcon"); + // Clean up event handlers + DataContextChanged -= OnDataContextChanged; + Unregister(); internalNotifyIconManager.Dispose(); @@ -375,6 +381,13 @@ protected virtual void Dispose(bool disposing) protected virtual void OnMenuChanged(ContextMenu contextMenu) { internalNotifyIconManager.ContextMenu = contextMenu; + + // Set the DataContext for ContextMenu to enable binding + if (contextMenu.DataContext == null && DataContext != null) + { + contextMenu.DataContext = DataContext; + } + internalNotifyIconManager.ContextMenu.SetCurrentValue(Control.FontSizeProperty, MenuFontSize); } @@ -410,7 +423,6 @@ private static void OnFocusOnLeftClickChanged(DependencyObject d, DependencyProp if (e.NewValue is not bool newValue) { notifyIcon.FocusOnLeftClick = false; - return; } @@ -427,7 +439,6 @@ private static void OnMenuOnRightClickChanged(DependencyObject d, DependencyProp if (e.NewValue is not bool newValue) { notifyIcon.MenuOnRightClick = false; - return; } @@ -455,6 +466,12 @@ private void InitializeIcon() internalNotifyIconManager.Icon = Icon; internalNotifyIconManager.MenuOnRightClick = MenuOnRightClick; internalNotifyIconManager.FocusOnLeftClick = FocusOnLeftClick; + + // Add Menu initialization + if (Menu != null) + { + OnMenuChanged(Menu); + } } private void RegisterHandlers() @@ -466,4 +483,13 @@ private void RegisterHandlers() internalNotifyIconManager.MiddleClick += OnMiddleClick; internalNotifyIconManager.MiddleDoubleClick += OnMiddleDoubleClick; } + + private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + // Menu?.DataContext = e.NewValue; + if (Menu != null) + { + Menu.DataContext = e.NewValue; + } + } } diff --git a/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs b/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs index fdd299344..9a2b059bb 100644 --- a/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs +++ b/src/Wpf.Ui.Tray/Internal/InternalNotifyIconManager.cs @@ -178,7 +178,10 @@ protected virtual void OpenMenu() // Without setting the handler window at the front, menu may appear behind the taskbar _ = Interop.User32.SetForegroundWindow(HookWindow.Handle); - ContextMenuService.SetPlacement(ContextMenu, PlacementMode.MousePoint); + + // Set placement properties for better positioning + ContextMenu.SetCurrentValue(ContextMenu.PlacementProperty, PlacementMode.MousePoint); + ContextMenu.SetCurrentValue(ContextMenu.PlacementTargetProperty, null); // ContextMenu.ApplyMica(); ContextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true); diff --git a/src/Wpf.Ui.Tray/Wpf.Ui.Tray.csproj b/src/Wpf.Ui.Tray/Wpf.Ui.Tray.csproj index b06f1e6d5..a33fde3a9 100644 --- a/src/Wpf.Ui.Tray/Wpf.Ui.Tray.csproj +++ b/src/Wpf.Ui.Tray/Wpf.Ui.Tray.csproj @@ -2,7 +2,7 @@ WPF-UI.Tray - net462;net472;net481;net6.0-windows;net8.0-windows;net9.0-windows + net10.0-windows;net9.0-windows;net8.0-windows;net481;net472;net462 Native tray menu icon support for WPF using the WPF UI library. $(CommonTags);tray;notifyicon;notify true @@ -35,18 +35,10 @@ - - all - build; analyzers - all runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/Wpf.Ui/Appearance/ApplicationAccentColorManager.cs b/src/Wpf.Ui/Appearance/ApplicationAccentColorManager.cs index f9fd090a7..4da5191f3 100644 --- a/src/Wpf.Ui/Appearance/ApplicationAccentColorManager.cs +++ b/src/Wpf.Ui/Appearance/ApplicationAccentColorManager.cs @@ -3,7 +3,11 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. +using System.IO; +using System.Runtime.InteropServices; using Wpf.Ui.Interop; +using Wpf.Ui.Win32; +using static Wpf.Ui.Appearance.UISettingsRCW; namespace Wpf.Ui.Appearance; @@ -28,6 +32,23 @@ namespace Wpf.Ui.Appearance; /// public static class ApplicationAccentColorManager { + private static readonly IUISettings3? _uisettings; + private static readonly bool _isSupported; + + static ApplicationAccentColorManager() + { + try + { + _uisettings = GetWinRTInstance() as IUISettings3; + _isSupported = _uisettings != null; + } + catch (COMException) + { + // We don't want to throw any exceptions here. + // If we can't get the instance, we will use the fallback accent color. + } + } + /// /// The maximum value of the background HSV brightness after which the text on the accent will be turned dark. /// @@ -125,6 +146,12 @@ public static Color TertiaryAccent /// public static Brush TertiaryAccentBrush => new SolidColorBrush(TertiaryAccent); + /// + /// Gets a value indicating whether the user has enabled accent color on title bars and window borders in Windows settings. + /// + public static bool IsAccentColorOnTitleBarsEnabled => + UnsafeNativeMethods.IsAccentColorOnTitleBarsEnabled(); + /// /// Changes the color accents of the application based on the color entered. /// @@ -149,18 +176,28 @@ public static void Apply( if (applicationTheme == ApplicationTheme.Dark) { - primaryAccent = systemAccent.Update(15f, -12f); - secondaryAccent = systemAccent.Update(30f, -24f); - tertiaryAccent = systemAccent.Update(45f, -36f); + primaryAccent = GetColor(UIColorType.AccentLight1, 17, -30f); + secondaryAccent = GetColor(UIColorType.AccentLight2, 17, -45f); + tertiaryAccent = GetColor(UIColorType.AccentLight3, 17, -65f); } else { - primaryAccent = systemAccent.UpdateBrightness(-5f); - secondaryAccent = systemAccent.UpdateBrightness(-10f); - tertiaryAccent = systemAccent.UpdateBrightness(-15f); + primaryAccent = GetColor(UIColorType.AccentDark1, -10); + secondaryAccent = GetColor(UIColorType.AccentDark2, -25); + tertiaryAccent = GetColor(UIColorType.AccentDark3, -40); } - UpdateColorResources(systemAccent, primaryAccent, secondaryAccent, tertiaryAccent); + UpdateColorResources(applicationTheme, systemAccent, primaryAccent, secondaryAccent, tertiaryAccent); + + Color GetColor(UIColorType colorType, float brightnessFactor, float saturationFactor = 0.0f) + { + if (GetUiColor(colorType) is { } color) + { + return color; + } + + return systemAccent.Update(brightnessFactor, saturationFactor); + } } /// @@ -177,7 +214,13 @@ public static void Apply( Color tertiaryAccent ) { - UpdateColorResources(systemAccent, primaryAccent, secondaryAccent, tertiaryAccent); + UpdateColorResources( + ApplicationThemeManager.GetAppTheme(), + systemAccent, + primaryAccent, + secondaryAccent, + tertiaryAccent + ); } /// @@ -194,13 +237,19 @@ public static void ApplySystemAccent() /// public static Color GetColorizationColor() { - return UnsafeNativeMethods.GetDwmColor(); + if (GetUiColor(UIColorType.Accent) is { } accentColor) + { + return accentColor; + } + + return UnsafeNativeMethods.GetAccentColor(); } /// /// Updates application resources. /// private static void UpdateColorResources( + ApplicationTheme applicationTheme, Color systemAccent, Color primaryAccent, Color secondaryAccent, @@ -295,16 +344,79 @@ Color tertiaryAccent UiApplication.Current.Resources["SystemAccentColorSecondary"] = secondaryAccent; UiApplication.Current.Resources["SystemAccentColorTertiary"] = tertiaryAccent; - UiApplication.Current.Resources["SystemAccentBrush"] = secondaryAccent.ToBrush(); + UiApplication.Current.Resources["SystemAccentBrush"] = systemAccent.ToBrush(); UiApplication.Current.Resources["SystemFillColorAttentionBrush"] = secondaryAccent.ToBrush(); - UiApplication.Current.Resources["AccentTextFillColorPrimaryBrush"] = tertiaryAccent.ToBrush(); + + UiApplication.Current.Resources["AccentTextFillColorPrimaryBrush"] = secondaryAccent.ToBrush(); UiApplication.Current.Resources["AccentTextFillColorSecondaryBrush"] = tertiaryAccent.ToBrush(); - UiApplication.Current.Resources["AccentTextFillColorTertiaryBrush"] = secondaryAccent.ToBrush(); + UiApplication.Current.Resources["AccentTextFillColorTertiaryBrush"] = primaryAccent.ToBrush(); + UiApplication.Current.Resources["AccentFillColorSelectedTextBackgroundBrush"] = systemAccent.ToBrush(); - UiApplication.Current.Resources["AccentFillColorDefaultBrush"] = secondaryAccent.ToBrush(); - UiApplication.Current.Resources["AccentFillColorSecondaryBrush"] = secondaryAccent.ToBrush(0.9); - UiApplication.Current.Resources["AccentFillColorTertiaryBrush"] = secondaryAccent.ToBrush(0.8); + var themeAccent = applicationTheme == ApplicationTheme.Dark ? secondaryAccent : primaryAccent; + UiApplication.Current.Resources["AccentFillColorDefault"] = themeAccent; + UiApplication.Current.Resources["AccentFillColorDefaultBrush"] = themeAccent.ToBrush(); + UiApplication.Current.Resources["AccentFillColorSecondary"] = Color.FromArgb( + 229, + themeAccent.R, + themeAccent.G, + themeAccent.B + ); // 229 = 0.9 * 255 + UiApplication.Current.Resources["AccentFillColorSecondaryBrush"] = themeAccent.ToBrush(0.9); + UiApplication.Current.Resources["AccentFillColorTertiary"] = Color.FromArgb( + 204, + themeAccent.R, + themeAccent.G, + themeAccent.B + ); // 204 = 0.8 * 255 + UiApplication.Current.Resources["AccentFillColorTertiaryBrush"] = themeAccent.ToBrush(0.8); + } + + /// + /// Gets the color of the UI. + /// + /// Type of the color. + private static Color? GetUiColor(UIColorType colorType) + { + if (_isSupported) + { + try + { + UIColor uiColor = _uisettings!.GetColorValue(colorType); + return Color.FromArgb(uiColor.A, uiColor.R, uiColor.G, uiColor.B); + } + catch + { + // We don't want to throw any exceptions here. + // If we can't get the instance, we can fallback to another method. + } + } + + return null; + } + + /// + /// Gets the WinRT instance of UISettings. + /// + private static object? GetWinRTInstance() + { + if (!Utilities.IsOSWindows10OrNewer) + { + return null; + } + + object? winRtInstance; + + try + { + winRtInstance = GetUISettingsInstance(); + } + catch (Exception e) when (e is TypeLoadException or FileNotFoundException) + { + winRtInstance = null; + } + + return winRtInstance; } } diff --git a/src/Wpf.Ui/Appearance/SystemThemeWatcher.cs b/src/Wpf.Ui/Appearance/SystemThemeWatcher.cs index 7d300fe68..841a6b4bd 100644 --- a/src/Wpf.Ui/Appearance/SystemThemeWatcher.cs +++ b/src/Wpf.Ui/Appearance/SystemThemeWatcher.cs @@ -3,6 +3,7 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. +using Windows.Win32; using Wpf.Ui.Controls; using Wpf.Ui.Interop; @@ -150,7 +151,7 @@ public static void UnWatch(Window? window) /// private static IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { - if (msg == (int)User32.WM.WININICHANGE) + if (msg == (int)PInvoke.WM_WININICHANGE) { UpdateObservedWindow(hWnd); } diff --git a/src/Wpf.Ui/Appearance/UISettingsRCW.cs b/src/Wpf.Ui/Appearance/UISettingsRCW.cs new file mode 100644 index 000000000..1a85d9a58 --- /dev/null +++ b/src/Wpf.Ui/Appearance/UISettingsRCW.cs @@ -0,0 +1,93 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Runtime.InteropServices; + +namespace Wpf.Ui.Appearance; + +/// +/// Contains internal RCWs for invoking the UISettings +/// +internal static class UISettingsRCW +{ + public enum UIColorType + { + Background = 0, + Foreground = 1, + AccentDark3 = 2, + AccentDark2 = 3, + AccentDark1 = 4, + Accent = 5, + AccentLight1 = 6, + AccentLight2 = 7, + AccentLight3 = 8, + Complement = 9, + } + + public static object GetUISettingsInstance() + { + const string typeName = "Windows.UI.ViewManagement.UISettings"; + + int hr = NativeMethods.WindowsCreateString(typeName, typeName.Length, out IntPtr hstring); + Marshal.ThrowExceptionForHR(hr); + + try + { + hr = NativeMethods.RoActivateInstance(hstring, out object instance); + Marshal.ThrowExceptionForHR(hr); + return instance; + } + finally + { + hr = NativeMethods.WindowsDeleteString(hstring); + Marshal.ThrowExceptionForHR(hr); + } + } + + /// + /// Contains internal RCWs for invoking the InputPane (tiptsf touch keyboard) + /// + internal static class NativeMethods + { + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + internal static extern int WindowsCreateString( + [MarshalAs(UnmanagedType.LPWStr)] string sourceString, + int length, + out IntPtr hstring + ); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + internal static extern int WindowsDeleteString(IntPtr hstring); + + [DllImport("api-ms-win-core-winrt-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + internal static extern int RoActivateInstance( + IntPtr runtimeClassId, + [MarshalAs(UnmanagedType.Interface)] out object instance + ); + } + + [Guid("03021BE4-5254-4781-8194-5168F7D06D7B")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + internal interface IUISettings3 + { + void GetIids(out uint iidCount, out IntPtr iids); + + void GetRuntimeClassName(out string className); + + void GetTrustLevel(out TrustLevel TrustLevel); + + UIColor GetColorValue(UIColorType desiredColor); + } + + internal enum TrustLevel + { + BaseTrust, + PartialTrust, + FullTrust, + } + + internal readonly record struct UIColor(byte A, byte R, byte G, byte B); +} diff --git a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs index ac57745d6..3f84f446d 100644 --- a/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs +++ b/src/Wpf.Ui/Controls/AutoSuggestBox/AutoSuggestBox.cs @@ -7,8 +7,8 @@ using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; +using Windows.Win32; using Wpf.Ui.Input; -using Wpf.Ui.Interop; // ReSharper disable once CheckNamespace namespace Wpf.Ui.Controls; @@ -534,9 +534,9 @@ private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool return IntPtr.Zero; } - var message = (User32.WM)msg; + var message = (uint)msg; - if (message is User32.WM.NCACTIVATE or User32.WM.WINDOWPOSCHANGED) + if (message is PInvoke.WM_NCACTIVATE or PInvoke.WM_WINDOWPOSCHANGED) { SetCurrentValue(IsSuggestionListOpenProperty, false); } diff --git a/src/Wpf.Ui/Controls/Button/Button.xaml b/src/Wpf.Ui/Controls/Button/Button.xaml index 917df73e0..bae3c0ad8 100644 --- a/src/Wpf.Ui/Controls/Button/Button.xaml +++ b/src/Wpf.Ui/Controls/Button/Button.xaml @@ -245,34 +245,43 @@ HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" Background="{TemplateBinding Background}" - Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" - BorderThickness="{TemplateBinding BorderThickness}" + BorderThickness="0" CornerRadius="{TemplateBinding CornerRadius}"> - - - - - + + + + + + + - + - - + + + @@ -311,6 +320,10 @@ + + + + @@ -329,7 +342,8 @@ - + + @@ -349,16 +363,11 @@ - - - - - - - - - - + + + + + @@ -378,16 +387,8 @@ - - - - - - - - - - + + @@ -407,11 +408,10 @@ - - - - - + + + + @@ -431,6 +431,9 @@ + + + @@ -450,6 +453,9 @@ + + + @@ -469,6 +475,9 @@ + + + diff --git a/src/Wpf.Ui/Controls/Calendar/Calendar.xaml b/src/Wpf.Ui/Controls/Calendar/Calendar.xaml index b4948930d..c287cf894 100644 --- a/src/Wpf.Ui/Controls/Calendar/Calendar.xaml +++ b/src/Wpf.Ui/Controls/Calendar/Calendar.xaml @@ -19,9 +19,14 @@ + + + + - + BorderThickness="0" + Focusable="True" + Foreground="{DynamicResource CalendarViewNavigationButtonForeground}" + MouseOverBackground="{DynamicResource CalendarViewItemBackgroundPointerOver}" + PressedBackground="{DynamicResource CalendarViewItemBackgroundPressed}"> + - + FontSize="16" + Symbol="CaretDown16"> + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -487,9 +554,11 @@ - - - + + + + + @@ -497,19 +566,19 @@ x:Name="PART_Root" Margin="0" Padding="0" - Background="{DynamicResource CalendarViewBackground}" - BorderBrush="{DynamicResource CalendarViewBorderBrush}" - BorderThickness="1" - CornerRadius="8"> + Background="{TemplateBinding Background}" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" + CornerRadius="4"> @@ -517,6 +586,47 @@ + + + + @@ -584,8 +596,9 @@ + Opacity="0.135" + ShadowDepth="10" + Color="#202020" /> @@ -723,7 +736,7 @@ @@ -906,8 +919,9 @@ + Opacity="0.135" + ShadowDepth="10" + Color="#202020" /> @@ -977,7 +991,13 @@ + + diff --git a/src/Wpf.Ui/Controls/MessageBox/MessageBox.xaml b/src/Wpf.Ui/Controls/MessageBox/MessageBox.xaml index 7686262ad..b3537d189 100644 --- a/src/Wpf.Ui/Controls/MessageBox/MessageBox.xaml +++ b/src/Wpf.Ui/Controls/MessageBox/MessageBox.xaml @@ -8,7 +8,10 @@ + xmlns:controls="clr-namespace:Wpf.Ui.Controls" + xmlns:converters="clr-namespace:Wpf.Ui.Converters"> + + - + @@ -232,7 +232,7 @@ - + diff --git a/src/Wpf.Ui/Controls/SplitButton/SplitButton.cs b/src/Wpf.Ui/Controls/SplitButton/SplitButton.cs index a9cefb145..66c63ca27 100644 --- a/src/Wpf.Ui/Controls/SplitButton/SplitButton.cs +++ b/src/Wpf.Ui/Controls/SplitButton/SplitButton.cs @@ -5,6 +5,7 @@ using System.Windows.Controls; using System.Windows.Controls.Primitives; +using System.Windows.Input; // ReSharper disable once CheckNamespace namespace Wpf.Ui.Controls; @@ -12,9 +13,12 @@ namespace Wpf.Ui.Controls; /// /// Represents a button with two parts that can be invoked separately. One part behaves like a standard button and the other part invokes a flyout. /// +[TemplatePart(Name = TemplateElementToggle, Type = typeof(Border))] [TemplatePart(Name = TemplateElementToggleButton, Type = typeof(ToggleButton))] public class SplitButton : Button { + private const string TemplateElementToggle = "PART_Toggle"; + /// /// Template element represented by the ToggleButton name. /// @@ -25,7 +29,9 @@ public class SplitButton : Button /// /// Gets or sets control responsible for toggling the drop-down button. /// - protected ToggleButton SplitButtonToggleButton { get; set; } = null!; + protected ToggleButton? SplitButtonToggleButton { get; set; } + + private Border? _splitButtonToggleBorder; /// Identifies the dependency property. public static readonly DependencyProperty FlyoutProperty = DependencyProperty.Register( @@ -85,29 +91,14 @@ public SplitButton() }; } - protected virtual void AttachTemplateResources() - { - base.OnApplyTemplate(); - - if (GetTemplateChild(TemplateElementToggleButton) is ToggleButton toggleButton) - { - SplitButtonToggleButton = toggleButton; - AttachToggleButtonClick(); - } - else - { - throw new NullReferenceException( - $"Element {nameof(TemplateElementToggleButton)} of type {typeof(ToggleButton)} not found in {typeof(SplitButton)}" - ); - } - } - private void AttachToggleButtonClick() { if (SplitButtonToggleButton != null) { - SplitButtonToggleButton.Click -= OnSplitButtonToggleButtonOnClick; - SplitButtonToggleButton.Click += OnSplitButtonToggleButtonOnClick; + SplitButtonToggleButton.PreviewMouseLeftButtonUp -= + OnSplitButtonToggleButtonOnPreviewMouseLeftButtonUp; + SplitButtonToggleButton.PreviewMouseLeftButtonUp += + OnSplitButtonToggleButtonOnPreviewMouseLeftButtonUp; } } @@ -169,6 +160,11 @@ public override void OnApplyTemplate() $"Element {nameof(TemplateElementToggleButton)} of type {typeof(ToggleButton)} not found in {typeof(SplitButton)}" ); } + + if (GetTemplateChild(TemplateElementToggle) is Border toggleBorder) + { + _splitButtonToggleBorder = toggleBorder; + } } /// @@ -176,12 +172,24 @@ public override void OnApplyTemplate() /// protected virtual void ReleaseTemplateResources() { - SplitButtonToggleButton.Click -= OnSplitButtonToggleButtonOnClick; + if (SplitButtonToggleButton != null) + { + SplitButtonToggleButton.PreviewMouseLeftButtonUp -= + OnSplitButtonToggleButtonOnPreviewMouseLeftButtonUp; + } } - private void OnSplitButtonToggleButtonOnClick(object sender, RoutedEventArgs e) + private void OnSplitButtonToggleButtonOnPreviewMouseLeftButtonUp(object sender, MouseEventArgs e) { - if (sender is not ToggleButton || _contextMenu is null) + if (sender is not ToggleButton || _contextMenu is null || _splitButtonToggleBorder is null) + { + return; + } + + // Ensure mouse up actually happened inside the toggler, and not outside. + var position = e.GetPosition(_splitButtonToggleBorder); + HitTestResult hitTestResult = VisualTreeHelper.HitTest(_splitButtonToggleBorder, position); + if (hitTestResult?.VisualHit == null) { return; } diff --git a/src/Wpf.Ui/Controls/SplitButton/SplitButton.xaml b/src/Wpf.Ui/Controls/SplitButton/SplitButton.xaml index 594a093d7..da51063ca 100644 --- a/src/Wpf.Ui/Controls/SplitButton/SplitButton.xaml +++ b/src/Wpf.Ui/Controls/SplitButton/SplitButton.xaml @@ -42,7 +42,6 @@ @@ -87,9 +84,11 @@ + Style="{DynamicResource DefaultSplitButtonToggleButtonStyle}"> @@ -143,19 +145,35 @@ - + - - + + + + + + + + + + + + + + + + + + - + - - + + diff --git a/src/Wpf.Ui/Controls/TabControl/TabControl.xaml b/src/Wpf.Ui/Controls/TabControl/TabControl.xaml index 4d18232f2..8c2449aba 100644 --- a/src/Wpf.Ui/Controls/TabControl/TabControl.xaml +++ b/src/Wpf.Ui/Controls/TabControl/TabControl.xaml @@ -76,7 +76,6 @@ + IsTabStop="False" + MouseOverBackground="{DynamicResource SubtleFillColorSecondaryBrush}" + MouseOverBorderBrush="Transparent" + PressedBackground="{DynamicResource SubtleFillColorTertiaryBrush}" + PressedBorderBrush="Transparent"> @@ -253,6 +258,7 @@ + diff --git a/src/Wpf.Ui/Controls/TitleBar/HwndProcEventArgs.cs b/src/Wpf.Ui/Controls/TitleBar/HwndProcEventArgs.cs new file mode 100644 index 000000000..79b7c948c --- /dev/null +++ b/src/Wpf.Ui/Controls/TitleBar/HwndProcEventArgs.cs @@ -0,0 +1,39 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +// ReSharper disable once CheckNamespace +namespace Wpf.Ui.Controls; + +public class HwndProcEventArgs : EventArgs +{ + public bool Handled { get; set; } + + public IntPtr? ReturnValue { get; set; } + + public bool IsMouseOverDetectedHeaderContent { get; } + + public IntPtr HWND { get; } + + public int Message { get; } + + public IntPtr WParam { get; } + + public IntPtr LParam { get; } + + internal HwndProcEventArgs( + IntPtr hwnd, + int msg, + IntPtr wParam, + IntPtr lParam, + bool isMouseOverDetectedHeaderContent + ) + { + HWND = hwnd; + Message = msg; + WParam = wParam; + LParam = lParam; + IsMouseOverDetectedHeaderContent = isMouseOverDetectedHeaderContent; + } +} diff --git a/src/Wpf.Ui/Controls/TitleBar/TitleBar.WindowResize.cs b/src/Wpf.Ui/Controls/TitleBar/TitleBar.WindowResize.cs new file mode 100644 index 000000000..bb6b19a49 --- /dev/null +++ b/src/Wpf.Ui/Controls/TitleBar/TitleBar.WindowResize.cs @@ -0,0 +1,283 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows.Shell; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; +using RECT = Windows.Win32.Foundation.RECT; + +// ReSharper disable once CheckNamespace +namespace Wpf.Ui.Controls; + +/// +/// Provides optional window-resize related hit-testing support for the control. +/// +/// +/// This partial class implements logic to return appropriate WM_NCHITTEST results (for example +/// HTLEFT, HTBOTTOMRIGHT, etc.) when the mouse is positioned over the window edges +/// or corners. This enables intuitive resizing behavior when the user drags the window borders. +/// +/// Key points: +/// - The implementation prefers the +/// value (expressed in device-independent units) when available and translates it into physical pixels; +/// - If WindowChrome or DPI information is not available, the code falls back to system metrics via +/// ; +/// - Because WM_NCHITTEST is raised frequently, computed border pixel sizes are cached to +/// reduce overhead; the cache is invalidated when DPI or relevant system parameters change; +/// - This component only augments the control's non-client hit-testing to +/// improve resize behavior and does not alter the window style or system behavior itself. +/// +/// Splitting this functionality into a partial class keeps the resize-related responsibilities +/// clearly separated from other TitleBar UI and interaction logic. +/// +public partial class TitleBar +{ + private int _borderX; + private int _borderY; + + private bool _borderXCached; + private bool _borderYCached; + + private bool _systemParamsSubscribed; + + private IntPtr GetWindowBorderHitTestResult(IntPtr hwnd, IntPtr lParam) + { + if (!PInvoke.GetWindowRect(new HWND(hwnd), out RECT windowRect)) + { + return (IntPtr)PInvoke.HTNOWHERE; + } + + if (!_borderXCached || !_borderYCached) + { + ComputeAndCacheBorderSizes(hwnd); + } + + long lp = lParam.ToInt64(); + + int x = (short)(lp & 0xFFFF); + int y = (short)((lp >> 16) & 0xFFFF); + + uint hit = 0u; + +#pragma warning disable + if (x < windowRect.left + _borderX) + hit |= 0b0001u; // left + if (x >= windowRect.right - _borderX) + hit |= 0b0010u; // right + if (y < windowRect.top + _borderY) + hit |= 0b0100u; // top + if (y >= windowRect.bottom - _borderY) + hit |= 0b1000u; // bottom +#pragma warning restore + + return hit switch + { + 0b0101u => (IntPtr)PInvoke.HTTOPLEFT, // top + left (0b0100 | 0b0001) + 0b0110u => (IntPtr)PInvoke.HTTOPRIGHT, // top + right (0b0100 | 0b0010) + 0b1001u => (IntPtr)PInvoke.HTBOTTOMLEFT, // bottom + left (0b1000 | 0b0001) + 0b1010u => (IntPtr)PInvoke.HTBOTTOMRIGHT, // bottom + right (0b1000 | 0b0010) + 0b0100u => (IntPtr)PInvoke.HTTOP, // top + 0b0001u => (IntPtr)PInvoke.HTLEFT, // left + 0b1000u => (IntPtr)PInvoke.HTBOTTOM, // bottom + 0b0010u => (IntPtr)PInvoke.HTRIGHT, // right + + // no match = HTNOWHERE (stop processing) + _ => (IntPtr)PInvoke.HTNOWHERE, + }; + } + + private void SubscribeToSystemParameters() + { + if (_systemParamsSubscribed) + { + return; + } + + SystemParameters.StaticPropertyChanged += OnSystemParametersChanged; + _systemParamsSubscribed = true; + } + + private void UnsubscribeToSystemParameters() + { + if (!_systemParamsSubscribed) + { + return; + } + + SystemParameters.StaticPropertyChanged -= OnSystemParametersChanged; + _systemParamsSubscribed = false; + } + + private void OnSystemParametersChanged(object? sender, PropertyChangedEventArgs e) + { + InvalidateBorderCache(); + } + + private void InvalidateBorderCache() + { + _borderXCached = false; + _borderYCached = false; + } + + private void ComputeAndCacheBorderSizes(IntPtr hwnd) + { + try + { + double dipBorderX; + double dipBorderY; + + Window? win = null; + try + { + var src = HwndSource.FromHwnd(hwnd); + if (src?.RootVisual is DependencyObject dep) + { + win = Window.GetWindow(dep); + } + } + catch + { + // ignored + } + + // FluentWindow uses WindowChrome - get border from it first + WindowChrome? chrome = win is null ? null : WindowChrome.GetWindowChrome(win); + + if (chrome is not null) + { + dipBorderX = Math.Max(chrome.ResizeBorderThickness.Left, chrome.ResizeBorderThickness.Right); + dipBorderY = Math.Max(chrome.ResizeBorderThickness.Top, chrome.ResizeBorderThickness.Bottom); + } + else + { + dipBorderX = SystemParameters.WindowResizeBorderThickness.Left; + dipBorderY = SystemParameters.WindowResizeBorderThickness.Top; + } + + int borderX; + int borderY; + + if ( + TryComputeFromPresentationSource(win, dipBorderX, dipBorderY, out borderX, out borderY) + || TryComputeFromDpiApi(hwnd, dipBorderX, dipBorderY, out borderX, out borderY) + || TryGetFromSystemMetrics(out borderX, out borderY) + ) + { + _borderX = borderX; + _borderY = borderY; + } + else + { + _borderX = 4; + _borderY = 4; + } + } + catch + { + _borderX = 4; + _borderY = 4; + } + + _borderXCached = true; + _borderYCached = true; + } + + // Try to compute border sizes from PresentationSource (per-monitor DPI-aware path). + private static bool TryComputeFromPresentationSource( + Window? win, + double dipBorderX, + double dipBorderY, + out int borderX, + out int borderY + ) + { + if (win is not null) + { + try + { + PresentationSource? source = PresentationSource.FromVisual(win); + + if (source?.CompositionTarget is not null) + { + Matrix m = source.CompositionTarget.TransformToDevice; + + borderX = Math.Max(2, (int)Math.Ceiling(dipBorderX * m.M11)); + borderY = Math.Max(2, (int)Math.Ceiling(dipBorderY * m.M22)); + + return true; + } + } + catch + { + // ignored + } + } + + borderX = 0; + borderY = 0; + + return false; + } + + // Try to compute border sizes using GetDpiForWindow (if available). + private static bool TryComputeFromDpiApi( + IntPtr hwnd, + double dipBorderX, + double dipBorderY, + out int borderX, + out int borderY + ) + { + try + { + uint dpi = PInvoke.GetDpiForWindow(new HWND(hwnd)); + if (dpi == 0) + { + dpi = 96; + } + + double scale = dpi / 96.0; + + borderX = Math.Max(2, (int)Math.Ceiling(dipBorderX * scale)); + borderY = Math.Max(2, (int)Math.Ceiling(dipBorderY * scale)); + + return true; + } + catch + { + borderX = 0; + borderY = 0; + + return false; + } + } + + // Try to compute border sizes using GetSystemMetrics as a safe fallback. + private static bool TryGetFromSystemMetrics(out int borderX, out int borderY) + { + try + { + int sx = + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXSIZEFRAME) + + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXPADDEDBORDER); + int sy = + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYSIZEFRAME) + + PInvoke.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXPADDEDBORDER); + + borderX = Math.Max(2, sx); + borderY = Math.Max(2, sy); + + return true; + } + catch + { + borderX = 0; + borderY = 0; + + return false; + } + } +} diff --git a/src/Wpf.Ui/Controls/TitleBar/TitleBar.cs b/src/Wpf.Ui/Controls/TitleBar/TitleBar.cs index 78583ed9e..36abd1108 100644 --- a/src/Wpf.Ui/Controls/TitleBar/TitleBar.cs +++ b/src/Wpf.Ui/Controls/TitleBar/TitleBar.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Windows.Data; using System.Windows.Input; +using Windows.Win32; using Wpf.Ui.Designer; using Wpf.Ui.Input; using Wpf.Ui.Interop; @@ -23,7 +24,7 @@ namespace Wpf.Ui.Controls; [TemplatePart(Name = ElementMaximizeButton, Type = typeof(TitleBarButton))] [TemplatePart(Name = ElementRestoreButton, Type = typeof(TitleBarButton))] [TemplatePart(Name = ElementCloseButton, Type = typeof(TitleBarButton))] -public class TitleBar : System.Windows.Controls.Control, IThemeControl +public partial class TitleBar : System.Windows.Controls.Control, IThemeControl { private const string ElementIcon = "PART_Icon"; private const string ElementMainGrid = "PART_MainGrid"; @@ -37,6 +38,8 @@ public class TitleBar : System.Windows.Controls.Control, IThemeControl private DependencyObject? _parentWindow; + public event EventHandler? WndProcInvoked; + /// Identifies the dependency property. public static readonly DependencyProperty ApplicationThemeProperty = DependencyProperty.Register( nameof(ApplicationTheme), @@ -466,8 +469,16 @@ protected virtual void OnLoaded(object sender, RoutedEventArgs e) _currentWindow = System.Windows.Window.GetWindow(this) ?? throw new InvalidOperationException("Window is null"); + if (_currentWindow.WindowState == WindowState.Maximized) + { + SetCurrentValue(IsMaximizedProperty, true); + _currentWindow.SetCurrentValue(Window.WindowStateProperty, WindowState.Maximized); + } + _currentWindow.StateChanged += OnParentWindowStateChanged; _currentWindow.ContentRendered += OnWindowContentRendered; + + SubscribeToSystemParameters(); } private void OnUnloaded(object sender, RoutedEventArgs e) @@ -476,6 +487,7 @@ private void OnUnloaded(object sender, RoutedEventArgs e) Unloaded -= OnUnloaded; Appearance.ApplicationThemeManager.Changed -= OnThemeChanged; + UnsubscribeToSystemParameters(); } /// @@ -591,8 +603,7 @@ private void OnTemplateButtonClick(TitleBarButtonType buttonType) { switch (buttonType) { - case TitleBarButtonType.Maximize - or TitleBarButtonType.Restore: + case TitleBarButtonType.Maximize or TitleBarButtonType.Restore: RaiseEvent(new RoutedEventArgs(MaximizeClickedEvent, this)); MaximizeWindow(); break; @@ -642,15 +653,21 @@ private void OnWindowContentRendered(object? sender, EventArgs e) private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { - var message = (User32.WM)msg; + var message = (uint)msg; + + // Invalidate cached border size on DPI change message + if (message == PInvoke.WM_DPICHANGED) + { + InvalidateBorderCache(); + } if ( message is not ( - User32.WM.NCHITTEST - or User32.WM.NCMOUSELEAVE - or User32.WM.NCLBUTTONDOWN - or User32.WM.NCLBUTTONUP + PInvoke.WM_NCHITTEST + or PInvoke.WM_NCMOUSELEAVE + or PInvoke.WM_NCLBUTTONDOWN + or PInvoke.WM_NCLBUTTONUP ) ) { @@ -683,27 +700,36 @@ or User32.WM.NCLBUTTONUP } bool isMouseOverHeaderContent = false; + IntPtr htResult = (IntPtr)PInvoke.HTNOWHERE; - if (message == User32.WM.NCHITTEST && (TrailingContent is UIElement || Header is UIElement)) - { - UIElement? headerLeftUIElement = Header as UIElement; - UIElement? headerCenterUIElement = CenterContent as UIElement; - UIElement? headerRightUiElement = TrailingContent as UIElement; + if (message == PInvoke.WM_NCHITTEST) + { + if (TrailingContent is UIElement || Header is UIElement || CenterContent is UIElement) + { + UIElement? headerLeftUIElement = Header as UIElement; + UIElement? headerCenterUIElement = CenterContent as UIElement; + UIElement? headerRightUiElement = TrailingContent as UIElement; - isMouseOverHeaderContent = (headerLeftUIElement is not null && headerLeftUIElement != _titleBlock && headerLeftUIElement.IsMouseOverElement(lParam)) - || (headerCenterUIElement?.IsMouseOverElement(lParam) ?? false) - || (headerRightUiElement?.IsMouseOverElement(lParam) ?? false); - } + isMouseOverHeaderContent = (headerLeftUIElement is not null && headerLeftUIElement != _titleBlock && headerLeftUIElement.IsMouseOverElement(lParam)) + || (headerCenterUIElement?.IsMouseOverElement(lParam) ?? false) + || (headerRightUiElement?.IsMouseOverElement(lParam) ?? false); + } + + htResult = GetWindowBorderHitTestResult(hwnd, lParam); + } - switch (message) + switch (message) { - case User32.WM.NCHITTEST when CloseWindowByDoubleClickOnIcon && _icon.IsMouseOverElement(lParam): + case PInvoke.WM_NCHITTEST when CloseWindowByDoubleClickOnIcon && _icon.IsMouseOverElement(lParam): // Ideally, clicking on the icon should open the system menu, but when the system menu is opened manually, double-clicking on the icon does not close the window handled = true; - return (IntPtr)User32.WM_NCHITTEST.HTSYSMENU; - case User32.WM.NCHITTEST when this.IsMouseOverElement(lParam) && !isMouseOverHeaderContent: + return (IntPtr)PInvoke.HTSYSMENU; + case PInvoke.WM_NCHITTEST when htResult != (IntPtr)PInvoke.HTNOWHERE: + handled = true; + return htResult; + case PInvoke.WM_NCHITTEST when this.IsMouseOverElement(lParam) && !isMouseOverHeaderContent: handled = true; - return (IntPtr)User32.WM_NCHITTEST.HTCAPTION; + return (IntPtr)PInvoke.HTCAPTION; default: return IntPtr.Zero; } diff --git a/src/Wpf.Ui/Controls/TitleBar/TitleBar.xaml b/src/Wpf.Ui/Controls/TitleBar/TitleBar.xaml index 6e7be70f9..97f5de643 100644 --- a/src/Wpf.Ui/Controls/TitleBar/TitleBar.xaml +++ b/src/Wpf.Ui/Controls/TitleBar/TitleBar.xaml @@ -126,6 +126,7 @@ Grid.Column="0" Height="16" VerticalAlignment="Center" + AutomationProperties.AutomationId="TitleBarIcon" Content="{TemplateBinding Icon}" Focusable="False" RenderOptions.BitmapScalingMode="HighQuality" /> @@ -174,22 +175,26 @@ diff --git a/src/Wpf.Ui/Controls/TitleBar/TitleBarButton.cs b/src/Wpf.Ui/Controls/TitleBar/TitleBarButton.cs index 7079a112a..56bfca17c 100644 --- a/src/Wpf.Ui/Controls/TitleBar/TitleBarButton.cs +++ b/src/Wpf.Ui/Controls/TitleBar/TitleBarButton.cs @@ -5,7 +5,7 @@ using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; -using Wpf.Ui.Interop; +using Windows.Win32; // ReSharper disable once CheckNamespace namespace Wpf.Ui.Controls; @@ -87,7 +87,7 @@ public Brush RenderButtonsForeground public bool IsHovered { get; private set; } private readonly Brush _defaultBackgroundBrush = Brushes.Transparent; // REVIEW: Should it be transparent? - private User32.WM_NCHITTEST _returnValue; + private uint _returnValue; private bool _isClickedDown; @@ -172,13 +172,13 @@ is IInvokeProvider invokeProvider _isClickedDown = false; } - internal bool ReactToHwndHook(User32.WM msg, IntPtr lParam, out IntPtr returnIntPtr) + internal bool ReactToHwndHook(uint msg, IntPtr lParam, out IntPtr returnIntPtr) { returnIntPtr = IntPtr.Zero; switch (msg) { - case User32.WM.NCHITTEST: + case PInvoke.WM_NCHITTEST: if (this.IsMouseOverElement(lParam)) { /*Debug.WriteLine($"Hitting {ButtonType} | return code {_returnValue}");*/ @@ -189,13 +189,13 @@ internal bool ReactToHwndHook(User32.WM msg, IntPtr lParam, out IntPtr returnInt RemoveHover(); return false; - case User32.WM.NCMOUSELEAVE: // Mouse leaves the window + case PInvoke.WM_NCMOUSELEAVE: // Mouse leaves the window RemoveHover(); return false; - case User32.WM.NCLBUTTONDOWN when this.IsMouseOverElement(lParam): // Left button clicked down + case PInvoke.WM_NCLBUTTONDOWN when this.IsMouseOverElement(lParam): // Left button clicked down _isClickedDown = true; return true; - case User32.WM.NCLBUTTONUP when _isClickedDown && this.IsMouseOverElement(lParam): // Left button clicked up + case PInvoke.WM_NCLBUTTONUP when _isClickedDown && this.IsMouseOverElement(lParam): // Left button clicked up InvokeClick(); return true; default: @@ -219,12 +219,12 @@ protected void OnButtonTypeChanged(DependencyPropertyChangedEventArgs e) _returnValue = buttonType switch { - TitleBarButtonType.Unknown => User32.WM_NCHITTEST.HTNOWHERE, - TitleBarButtonType.Help => User32.WM_NCHITTEST.HTHELP, - TitleBarButtonType.Minimize => User32.WM_NCHITTEST.HTMINBUTTON, - TitleBarButtonType.Close => User32.WM_NCHITTEST.HTCLOSE, - TitleBarButtonType.Restore => User32.WM_NCHITTEST.HTMAXBUTTON, - TitleBarButtonType.Maximize => User32.WM_NCHITTEST.HTMAXBUTTON, + TitleBarButtonType.Unknown => PInvoke.HTNOWHERE, + TitleBarButtonType.Help => PInvoke.HTHELP, + TitleBarButtonType.Minimize => PInvoke.HTMINBUTTON, + TitleBarButtonType.Close => PInvoke.HTCLOSE, + TitleBarButtonType.Restore => PInvoke.HTMAXBUTTON, + TitleBarButtonType.Maximize => PInvoke.HTMAXBUTTON, _ => throw new ArgumentOutOfRangeException( "e.NewValue", buttonType, diff --git a/src/Wpf.Ui/Controls/ToggleSwitch/ToggleSwitch.xaml b/src/Wpf.Ui/Controls/ToggleSwitch/ToggleSwitch.xaml index 5c397e420..93ed88c2d 100644 --- a/src/Wpf.Ui/Controls/ToggleSwitch/ToggleSwitch.xaml +++ b/src/Wpf.Ui/Controls/ToggleSwitch/ToggleSwitch.xaml @@ -14,12 +14,10 @@ xmlns:controls="clr-namespace:Wpf.Ui.Controls" xmlns:system="clr-namespace:System;assembly=System.Runtime"> - - 1 40 20 - 8,6,0,0 + 0,0,0,0 1 8,0,0,0 @@ -43,7 +41,7 @@ - + @@ -74,33 +72,35 @@ RadiusX="10" RadiusY="10" StrokeThickness="0" /> - - + RadiusX="7" + RadiusY="7" + StrokeThickness="0"> + - - - + + - + RadiusX="7" + RadiusY="7" + StrokeThickness="0"> + - - + + @@ -132,7 +133,7 @@ - + @@ -190,25 +191,25 @@ Duration="00:00:00.167" /> @@ -216,6 +217,12 @@ + + + + + + @@ -248,9 +255,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -258,9 +299,23 @@ + + + + + + + + + + + + + + @@ -268,6 +323,19 @@ + + + + + + + + + + + + + diff --git a/src/Wpf.Ui/Controls/ToolTip/ToolTip.xaml b/src/Wpf.Ui/Controls/ToolTip/ToolTip.xaml index bf9bd19e5..e1c8ad362 100644 --- a/src/Wpf.Ui/Controls/ToolTip/ToolTip.xaml +++ b/src/Wpf.Ui/Controls/ToolTip/ToolTip.xaml @@ -5,13 +5,18 @@ All Rights Reserved. --> - + - - - + + + + + + + + + + + + diff --git a/src/Wpf.Ui/Controls/TreeView/TreeViewItem.xaml b/src/Wpf.Ui/Controls/TreeView/TreeViewItem.xaml index 7a1fdb7f6..ec6c4bf05 100644 --- a/src/Wpf.Ui/Controls/TreeView/TreeViewItem.xaml +++ b/src/Wpf.Ui/Controls/TreeView/TreeViewItem.xaml @@ -97,7 +97,6 @@ - - diff --git a/src/Wpf.Ui/Controls/Window/WindowBackdrop.cs b/src/Wpf.Ui/Controls/Window/WindowBackdrop.cs index dcec2f18b..9eb42f385 100644 --- a/src/Wpf.Ui/Controls/Window/WindowBackdrop.cs +++ b/src/Wpf.Ui/Controls/Window/WindowBackdrop.cs @@ -3,7 +3,9 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Dwm; using Wpf.Ui.Appearance; using Wpf.Ui.Interop; @@ -38,7 +40,7 @@ public static bool IsSupported(WindowBackdropType backdropType) /// The window to which the backdrop effect will be applied. /// The type of backdrop effect to apply. Determines the visual appearance of the window's backdrop. /// if the operation was successful; otherwise, . - public static bool ApplyBackdrop(System.Windows.Window? window, WindowBackdropType backdropType) + public static bool ApplyBackdrop(Window? window, WindowBackdropType backdropType) { if (window is null) { @@ -59,8 +61,7 @@ public static bool ApplyBackdrop(System.Windows.Window? window, WindowBackdropTy window.Loaded += (sender, _1) => { - IntPtr windowHandle = - new WindowInteropHelper(sender as System.Windows.Window ?? null)?.Handle ?? IntPtr.Zero; + IntPtr windowHandle = new WindowInteropHelper(sender as Window)?.Handle ?? IntPtr.Zero; if (windowHandle == IntPtr.Zero) { @@ -86,7 +87,7 @@ public static bool ApplyBackdrop(IntPtr hWnd, WindowBackdropType backdropType) return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } @@ -115,14 +116,20 @@ public static bool ApplyBackdrop(IntPtr hWnd, WindowBackdropType backdropType) return backdropType switch { - WindowBackdropType.Auto => ApplyDwmwWindowAttrubute(hWnd, Dwmapi.DWMSBT.DWMSBT_AUTO), - WindowBackdropType.Mica => ApplyDwmwWindowAttrubute(hWnd, Dwmapi.DWMSBT.DWMSBT_MAINWINDOW), - WindowBackdropType.Acrylic => ApplyDwmwWindowAttrubute( + WindowBackdropType.Auto => ApplyDwmwWindowAttribute(hWnd, DWM_SYSTEMBACKDROP_TYPE.DWMSBT_AUTO), + WindowBackdropType.Mica => ApplyDwmwWindowAttribute( hWnd, - Dwmapi.DWMSBT.DWMSBT_TRANSIENTWINDOW + DWM_SYSTEMBACKDROP_TYPE.DWMSBT_MAINWINDOW ), - WindowBackdropType.Tabbed => ApplyDwmwWindowAttrubute(hWnd, Dwmapi.DWMSBT.DWMSBT_TABBEDWINDOW), - _ => ApplyDwmwWindowAttrubute(hWnd, Dwmapi.DWMSBT.DWMSBT_DISABLE), + WindowBackdropType.Acrylic => ApplyDwmwWindowAttribute( + hWnd, + DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TRANSIENTWINDOW + ), + WindowBackdropType.Tabbed => ApplyDwmwWindowAttribute( + hWnd, + DWM_SYSTEMBACKDROP_TYPE.DWMSBT_TABBEDWINDOW + ), + _ => ApplyDwmwWindowAttribute(hWnd, DWM_SYSTEMBACKDROP_TYPE.DWMSBT_NONE), }; } @@ -130,7 +137,7 @@ public static bool ApplyBackdrop(IntPtr hWnd, WindowBackdropType backdropType) /// Tries to remove backdrop effects if they have been applied to the . /// /// The window from which the effect should be removed. - public static bool RemoveBackdrop(System.Windows.Window? window) + public static bool RemoveBackdrop(Window? window) { if (window is null) { @@ -146,7 +153,7 @@ public static bool RemoveBackdrop(System.Windows.Window? window) /// Tries to remove all effects if they have been applied to the hWnd. /// /// Pointer to the window handle. - public static bool RemoveBackdrop(IntPtr hWnd) + public static unsafe bool RemoveBackdrop(IntPtr hWnd) { if (hWnd == IntPtr.Zero) { @@ -160,26 +167,27 @@ public static bool RemoveBackdrop(IntPtr hWnd) return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } - var pvAttribute = 0; // Disable - var backdropPvAttribute = (int)Dwmapi.DWMSBT.DWMSBT_DISABLE; + BOOL pvAttribute = false; - _ = Dwmapi.DwmSetWindowAttribute( - hWnd, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, - ref pvAttribute, - Marshal.SizeOf(typeof(int)) + _ = PInvoke.DwmSetWindowAttribute( + new HWND(hWnd), + DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, + &pvAttribute, + (uint)sizeof(BOOL) ); - _ = Dwmapi.DwmSetWindowAttribute( - hWnd, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, - ref backdropPvAttribute, - Marshal.SizeOf(typeof(int)) + DWM_SYSTEMBACKDROP_TYPE backdropPvAttribute = DWM_SYSTEMBACKDROP_TYPE.DWMSBT_NONE; + + _ = PInvoke.DwmSetWindowAttribute( + new HWND(hWnd), + DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, + &backdropPvAttribute, + sizeof(DWM_SYSTEMBACKDROP_TYPE) ); return true; @@ -190,7 +198,7 @@ public static bool RemoveBackdrop(IntPtr hWnd) /// /// Window to manipulate. /// if operation was successful. - public static bool RemoveBackground(System.Windows.Window? window) + public static bool RemoveBackground(Window? window) { if (window is null) { @@ -218,7 +226,7 @@ public static bool RemoveBackground(System.Windows.Window? window) return true; } - public static bool RemoveTitlebarBackground(System.Windows.Window? window) + public static unsafe bool RemoveTitlebarBackground(Window? window) { if (window is null) { @@ -239,63 +247,53 @@ public static bool RemoveTitlebarBackground(System.Windows.Window? window) { // NOTE: https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute // Specifying DWMWA_COLOR_DEFAULT (value 0xFFFFFFFF) for the color will reset the window back to using the system's default behavior for the caption color. - uint titlebarPvAttribute = 0xFFFFFFFE; - - _ = Dwmapi.DwmSetWindowAttribute( - windowSource.Handle, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_CAPTION_COLOR, - ref titlebarPvAttribute, - sizeof(uint) - ); + uint titlebarPvAttribute = PInvoke.DWMWA_COLOR_NONE; + + return PInvoke.DwmSetWindowAttribute( + new HWND(windowSource.Handle), + DWMWINDOWATTRIBUTE.DWMWA_CAPTION_COLOR, + &titlebarPvAttribute, + sizeof(uint) + ) == HRESULT.S_OK; } return true; } - private static bool ApplyDwmwWindowAttrubute(IntPtr hWnd, Dwmapi.DWMSBT dwmSbt) + private static unsafe bool ApplyDwmwWindowAttribute(IntPtr hWnd, DWM_SYSTEMBACKDROP_TYPE dwmSbt) { if (hWnd == IntPtr.Zero) { return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } - int backdropPvAttribute = (int)dwmSbt; + DWM_SYSTEMBACKDROP_TYPE backdropPvAttribute = dwmSbt; - var dwmApiResult = Dwmapi.DwmSetWindowAttribute( - hWnd, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, - ref backdropPvAttribute, - Marshal.SizeOf(typeof(int)) - ); - - return dwmApiResult == HRESULT.S_OK; + return PInvoke.DwmSetWindowAttribute( + new HWND(hWnd), + DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, + &backdropPvAttribute, + sizeof(DWM_SYSTEMBACKDROP_TYPE) + ) == HRESULT.S_OK; } - private static bool ApplyLegacyMicaBackdrop(IntPtr hWnd) + private static unsafe bool ApplyLegacyMicaBackdrop(IntPtr hWnd) { - var backdropPvAttribute = 1; // Enable - - // TODO: Validate HRESULT - var dwmApiResult = Dwmapi.DwmSetWindowAttribute( - hWnd, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, - ref backdropPvAttribute, - Marshal.SizeOf(typeof(int)) - ); - - return dwmApiResult == HRESULT.S_OK; + BOOL backdropPvAttribute = true; + + return PInvoke.DwmSetWindowAttribute( + new HWND(hWnd), + DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, + &backdropPvAttribute, + (uint)sizeof(BOOL) + ) == HRESULT.S_OK; } - /*private static bool ApplyLegacyAcrylicBackdrop(IntPtr hWnd) - { - throw new NotImplementedException(); - }*/ - private static bool RestoreContentBackground(IntPtr hWnd) { if (hWnd == IntPtr.Zero) @@ -303,7 +301,7 @@ private static bool RestoreContentBackground(IntPtr hWnd) return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } @@ -316,7 +314,7 @@ private static bool RestoreContentBackground(IntPtr hWnd) windowSource.CompositionTarget.BackgroundColor = SystemColors.WindowColor; } - if (windowSource?.RootVisual is System.Windows.Window window) + if (windowSource?.RootVisual is Window window) { var backgroundBrush = window.Resources["ApplicationBackgroundBrush"]; diff --git a/src/Wpf.Ui/Converters/ClipConverter.cs b/src/Wpf.Ui/Converters/ClipConverter.cs new file mode 100644 index 000000000..ae6d0112c --- /dev/null +++ b/src/Wpf.Ui/Converters/ClipConverter.cs @@ -0,0 +1,39 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows.Data; + +namespace Wpf.Ui.Converters; + +public class ClipConverter : IMultiValueConverter +{ + /// + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length != 3) + { + return null; + } + + if ( + values[0] is not double width + || values[1] is not double height + || values[2] is not double percentage + ) + { + return null; + } + + double clippedWidth = width * percentage; + + return new RectangleGeometry(new Rect(0, 0, clippedWidth, height)); + } + + /// + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/Wpf.Ui/Converters/DatePickerButtonPaddingConverter.cs b/src/Wpf.Ui/Converters/DatePickerButtonPaddingConverter.cs new file mode 100644 index 000000000..68ace4546 --- /dev/null +++ b/src/Wpf.Ui/Converters/DatePickerButtonPaddingConverter.cs @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows.Data; + +namespace Wpf.Ui.Converters; + +internal class DatePickerButtonPaddingConverter : IMultiValueConverter +{ + /// + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values is [Thickness padding, Thickness buttonMargin, double buttonWidth]) + { + return new Thickness( + padding.Left, + padding.Top, + padding.Right + buttonMargin.Left + buttonMargin.Right + buttonWidth, + padding.Bottom + ); + } + + return default(Thickness); + } + + /// + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/Wpf.Ui/Extensions/PInvokeExtensions.cs b/src/Wpf.Ui/Extensions/PInvokeExtensions.cs new file mode 100644 index 000000000..aec029fe5 --- /dev/null +++ b/src/Wpf.Ui/Extensions/PInvokeExtensions.cs @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using Windows.Win32.Graphics.Dwm; + +namespace Wpf.Ui.Extensions; + +internal static class PInvokeExtensions +{ + extension(DWMWINDOWATTRIBUTE attr) + { + /// + /// Gets the undocumented window attribute for enabling Mica effect on a window. + /// + public static DWMWINDOWATTRIBUTE DWMWA_MICA_EFFECT => (DWMWINDOWATTRIBUTE)1029; + + /// + /// Gets the window attribute used to enable immersive dark mode prior to Windows 11. + /// + public static DWMWINDOWATTRIBUTE DMWA_USE_IMMERSIVE_DARK_MODE_OLD => + DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE - 1; + } +} diff --git a/src/Wpf.Ui/Extensions/UiElementExtensions.cs b/src/Wpf.Ui/Extensions/UiElementExtensions.cs index 4f82824a9..452c46686 100644 --- a/src/Wpf.Ui/Extensions/UiElementExtensions.cs +++ b/src/Wpf.Ui/Extensions/UiElementExtensions.cs @@ -10,7 +10,7 @@ internal static class UiElementExtensions /// /// Do not call it outside of NCHITTEST, NCLBUTTONUP, NCLBUTTONDOWN messages! /// - /// if mouse is over the element. otherwise. + /// if mouse is over the element. otherwise. public static bool IsMouseOverElement(this UIElement element, IntPtr lParam) { // This method will be invoked very often and must be as simple as possible. @@ -21,23 +21,20 @@ public static bool IsMouseOverElement(this UIElement element, IntPtr lParam) try { - var mousePosScreen = new Point(Get_X_LParam(lParam), Get_Y_LParam(lParam)); + // Ensure the visual is connected to a presentation source (needed for PointFromScreen). + if (PresentationSource.FromVisual(element) == null) + { + return false; + } - return new Rect(default, element.RenderSize).Contains(element.PointFromScreen(mousePosScreen)) + var mousePosition = new Point(Get_X_LParam(lParam), Get_Y_LParam(lParam)); + + // If element is Panel, check if children at mousePosition is with IsHitTestVisible false. + return new Rect(default, element.RenderSize).Contains(element.PointFromScreen(mousePosition)) && element.IsHitTestVisible && ( - !(element is System.Windows.Controls.Panel panel) - || // If element is Panel, check if children at mousePosRelative is with IsHitTestVisible false. - ( - panel - .Children.OfType() - .FirstOrDefault(child => - new Rect(default, child.RenderSize).Contains( - child.PointFromScreen(mousePosScreen) - ) - ) - ?.IsHitTestVisible ?? false - ) + element is not System.Windows.Controls.Panel panel + || IsChildHitTestVisibleAtPointFromScreen(panel, mousePosition) ); } catch @@ -55,4 +52,20 @@ private static int Get_Y_LParam(IntPtr lParam) { return (short)(lParam.ToInt32() >> 16); } + + private static bool IsChildHitTestVisibleAtPointFromScreen( + System.Windows.Controls.Panel panel, + Point mousePosition + ) + { + foreach (UIElement child in panel.Children) + { + if (new Rect(default, child.RenderSize).Contains(child.PointFromScreen(mousePosition))) + { + return child.IsHitTestVisible; + } + } + + return false; + } } diff --git a/src/Wpf.Ui/Hardware/DpiHelper.cs b/src/Wpf.Ui/Hardware/DpiHelper.cs index 335776bed..f8807ef57 100644 --- a/src/Wpf.Ui/Hardware/DpiHelper.cs +++ b/src/Wpf.Ui/Hardware/DpiHelper.cs @@ -3,6 +3,8 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. +using Windows.Win32; +using Windows.Win32.Foundation; using Wpf.Ui.Interop; namespace Wpf.Ui.Hardware; @@ -34,11 +36,11 @@ internal static class DpiHelper /// Gets DPI of the selected . /// /// The window that you want to get information about. - public static Hardware.DisplayDpi GetWindowDpi(Window? window) + public static DisplayDpi GetWindowDpi(Window? window) { if (window is null) { - return new Hardware.DisplayDpi(DefaultDpi, DefaultDpi); + return new DisplayDpi(DefaultDpi, DefaultDpi); } return GetWindowDpi(new WindowInteropHelper(window).Handle); @@ -48,16 +50,16 @@ public static Hardware.DisplayDpi GetWindowDpi(Window? window) /// Gets DPI of the selected based on it's handle. /// /// Handle of the window that you want to get information about. - public static Hardware.DisplayDpi GetWindowDpi(IntPtr windowHandle) + public static DisplayDpi GetWindowDpi(IntPtr windowHandle) { if (windowHandle == IntPtr.Zero || !UnsafeNativeMethods.IsValidWindow(windowHandle)) { - return new Hardware.DisplayDpi(DefaultDpi, DefaultDpi); + return new DisplayDpi(DefaultDpi, DefaultDpi); } - var windowDpi = (int)User32.GetDpiForWindow(windowHandle); + var windowDpi = (int)PInvoke.GetDpiForWindow(new HWND(windowHandle)); - return new Hardware.DisplayDpi(windowDpi, windowDpi); + return new DisplayDpi(windowDpi, windowDpi); } // TODO: Look into utilizing preprocessor symbols for more functionality @@ -76,7 +78,7 @@ public static Hardware.DisplayDpi GetWindowDpi(IntPtr windowHandle) /// Gets the DPI values from . /// /// The DPI values from . If the property cannot be accessed, the default value is returned. - public static Hardware.DisplayDpi GetSystemDpi() + public static DisplayDpi GetSystemDpi() { System.Reflection.PropertyInfo? dpiXProperty = typeof(SystemParameters).GetProperty( "DpiX", @@ -85,7 +87,7 @@ public static Hardware.DisplayDpi GetSystemDpi() if (dpiXProperty == null) { - return new Hardware.DisplayDpi(DefaultDpi, DefaultDpi); + return new DisplayDpi(DefaultDpi, DefaultDpi); } System.Reflection.PropertyInfo? dpiYProperty = typeof(SystemParameters).GetProperty( @@ -95,10 +97,10 @@ public static Hardware.DisplayDpi GetSystemDpi() if (dpiYProperty == null) { - return new Hardware.DisplayDpi(DefaultDpi, DefaultDpi); + return new DisplayDpi(DefaultDpi, DefaultDpi); } - return new Hardware.DisplayDpi( + return new DisplayDpi( (int)dpiXProperty.GetValue(null, null)!, (int)dpiYProperty.GetValue(null, null)! ); diff --git a/src/Wpf.Ui/Interop/Dwmapi.cs b/src/Wpf.Ui/Interop/Dwmapi.cs deleted file mode 100644 index 2259c3281..000000000 --- a/src/Wpf.Ui/Interop/Dwmapi.cs +++ /dev/null @@ -1,680 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -// This Source Code is partially based on reverse engineering of the Windows Operating System, -// and is intended for use on Windows systems only. -// This Source Code is partially based on the source code provided by the .NET Foundation. -// -// NOTE: -// I split unmanaged code stuff into the NativeMethods library. -// If you have suggestions for the code below, please submit your changes there. -// https://github.com/lepoco/nativemethods -// -// Windows Kits\10\Include\10.0.22000.0\um\dwmapi.h - -// ReSharper disable IdentifierTypo -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable CA1060 // Move pinvokes to native methods class - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop; - -/// -/// Desktop Window Manager (DWM). -/// -internal static class Dwmapi -{ - /// - /// Cloaked flags describing why a window is cloaked. - /// - public enum DWM_CLOAKED - { - DWM_CLOAKED_APP = 0x00000001, - DWM_CLOAKED_SHELL = 0x00000002, - DWM_CLOAKED_INHERITED = 0x00000004, - } - - /// - /// GT_* - /// - public enum GESTURE_TYPE - { - GT_PEN_TAP = 0, - GT_PEN_DOUBLETAP = 1, - GT_PEN_RIGHTTAP = 2, - GT_PEN_PRESSANDHOLD = 3, - GT_PEN_PRESSANDHOLDABORT = 4, - GT_TOUCH_TAP = 5, - GT_TOUCH_DOUBLETAP = 6, - GT_TOUCH_RIGHTTAP = 7, - GT_TOUCH_PRESSANDHOLD = 8, - GT_TOUCH_PRESSANDHOLDABORT = 9, - GT_TOUCH_PRESSANDTAP = 10, - } - - /// - /// DWMTWR_* Tab window requirements. - /// - public enum DWM_TAB_WINDOW_REQUIREMENTS - { - /// - /// This result means the window meets all requirements requested. - /// - DWMTWR_NONE = 0x0000, - - /// - /// In some configurations, admin/user setting or mode of the system means that windows won't be tabbed - /// This requirement says that the system/mode must implement tabbing and if it does not - /// nothing can be done to change this. - /// - DWMTWR_IMPLEMENTED_BY_SYSTEM = 0x0001, - - /// - /// The window has an owner or parent so is ineligible for tabbing. - /// - DWMTWR_WINDOW_RELATIONSHIP = 0x0002, - - /// - /// The window has styles that make it ineligible for tabbing. - /// To be eligible windows must: - /// Have the WS_OVERLAPPEDWINDOW (WS_CAPTION, WS_THICKFRAME, etc.) styles set. - /// Not have WS_POPUP, WS_CHILD or WS_DLGFRAME set. - /// Not have WS_EX_TOPMOST or WS_EX_TOOLWINDOW set. - /// - DWMTWR_WINDOW_STYLES = 0x0004, - - // The window has a region (set using SetWindowRgn) making it ineligible. - DWMTWR_WINDOW_REGION = 0x0008, - - /// - /// The window is ineligible due to its Dwm configuration. - /// It must not extended its client area into the title bar using DwmExtendFrameIntoClientArea - /// It must not have DWMWA_NCRENDERING_POLICY set to DWMNCRP_ENABLED - /// - DWMTWR_WINDOW_DWM_ATTRIBUTES = 0x0010, - - /// - /// The window is ineligible due to it's margins, most likely due to custom handling in WM_NCCALCSIZE. - /// The window must use the default window margins for the non-client area. - /// - DWMTWR_WINDOW_MARGINS = 0x0020, - - /// - /// The window has been explicitly opted out by setting DWMWA_TABBING_ENABLED to FALSE. - /// - DWMTWR_TABBING_ENABLED = 0x0040, - - /// - /// The user has configured this application to not participate in tabbing. - /// - DWMTWR_USER_POLICY = 0x0080, - - /// - /// The group policy has configured this application to not participate in tabbing. - /// - DWMTWR_GROUP_POLICY = 0x0100, - - /// - /// This is set if app compat has blocked tabs for this window. Can be overridden per window by setting - /// DWMWA_TABBING_ENABLED to TRUE. That does not override any other tabbing requirements. - /// - DWMTWR_APP_COMPAT = 0x0200, - } - - /// - /// Flags used by the DwmSetWindowAttribute function to specify the rounded corner preference for a window. - /// - [Flags] - public enum DWM_WINDOW_CORNER_PREFERENCE - { - DEFAULT = 0, - DONOTROUND = 1, - ROUND = 2, - ROUNDSMALL = 3, - } - - /// - /// Backdrop types. - /// - [Flags] - public enum DWMSBT : uint - { - /// - /// Automatically selects backdrop effect. - /// - DWMSBT_AUTO = 0, - - /// - /// Turns off the backdrop effect. - /// - DWMSBT_DISABLE = 1, - - /// - /// Sets Mica effect with generated wallpaper tint. - /// - DWMSBT_MAINWINDOW = 2, - - /// - /// Sets acrlic effect. - /// - DWMSBT_TRANSIENTWINDOW = 3, - - /// - /// Sets blurred wallpaper effect, like Mica without tint. - /// - DWMSBT_TABBEDWINDOW = 4, - } - - /// - /// Non-client rendering policy attribute values - /// - public enum DWMNCRENDERINGPOLICY - { - /// - /// Enable/disable non-client rendering based on window style - /// - DWMNCRP_USEWINDOWSTYLE, - - /// - /// Disabled non-client rendering; window style is ignored - /// - DWMNCRP_DISABLED, - - /// - /// Enabled non-client rendering; window style is ignored - /// - DWMNCRP_ENABLED, - - /// - /// Sentinel value. - /// - DWMNCRP_LAST, - } - - /// - /// Values designating how Flip3D treats a given window. - /// - public enum DWMFLIP3DWINDOWPOLICY - { - /// - /// Hide or include the window in Flip3D based on window style and visibility. - /// - DWMFLIP3D_DEFAULT, - - /// - /// Display the window under Flip3D and disabled. - /// - DWMFLIP3D_EXCLUDEBELOW, - - /// - /// Display the window above Flip3D and enabled. - /// - DWMFLIP3D_EXCLUDEABOVE, - - /// - /// Sentinel value. - /// - DWMFLIP3D_LAST, - } - - /// - /// Options used by the DwmGetWindowAttribute and DwmSetWindowAttribute functions. - /// - /// - [Flags] - public enum DWMWINDOWATTRIBUTE - { - /// - /// Is non-client rendering enabled/disabled - /// - DWMWA_NCRENDERING_ENABLED = 1, - - /// - /// DWMNCRENDERINGPOLICY - Non-client rendering policy - /// - DWMWA_NCRENDERING_POLICY = 2, - - /// - /// Potentially enable/forcibly disable transitions - /// - DWMWA_TRANSITIONS_FORCEDISABLED = 3, - - /// - /// Enables content rendered in the non-client area to be visible on the frame drawn by DWM. - /// - DWMWA_ALLOW_NCPAINT = 4, - - /// - /// Retrieves the bounds of the caption button area in the window-relative space. - /// - DWMWA_CAPTION_BUTTON_BOUNDS = 5, - - /// - /// Is non-client content RTL mirrored - /// - DWMWA_NONCLIENT_RTL_LAYOUT = 6, - - /// - /// Forces the window to display an iconic thumbnail or peek representation (a static bitmap), even if a live or snapshot representation of the window is available. - /// - DWMWA_FORCE_ICONIC_REPRESENTATION = 7, - - /// - /// Designates how Flip3D will treat the window. - /// - DWMWA_FLIP3D_POLICY = 8, - - /// - /// Gets the extended frame bounds rectangle in screen space - /// - DWMWA_EXTENDED_FRAME_BOUNDS = 9, - - /// - /// Indicates an available bitmap when there is no better thumbnail representation. - /// - DWMWA_HAS_ICONIC_BITMAP = 10, - - /// - /// Don't invoke Peek on the window. - /// - DWMWA_DISALLOW_PEEK = 11, - - /// - /// LivePreview exclusion information - /// - DWMWA_EXCLUDED_FROM_PEEK = 12, - - /// - /// Cloaks the window such that it is not visible to the user. - /// - DWMWA_CLOAK = 13, - - /// - /// If the window is cloaked, provides one of the following values explaining why. - /// - DWMWA_CLOAKED = 14, - - /// - /// Freeze the window's thumbnail image with its current visuals. Do no further live updates on the thumbnail image to match the window's contents. - /// - DWMWA_FREEZE_REPRESENTATION = 15, - - /// - /// BOOL, Updates the window only when desktop composition runs for other reasons - /// - DWMWA_PASSIVE_UPDATE_MODE = 16, - - /// - /// BOOL, Allows the use of host backdrop brushes for the window. - /// - DWMWA_USE_HOSTBACKDROPBRUSH = 17, - - /// - /// Allows a window to either use the accent color, or dark, according to the user Color Mode preferences. - /// - DMWA_USE_IMMERSIVE_DARK_MODE_OLD = 19, - - /// - /// Allows a window to either use the accent color, or dark, according to the user Color Mode preferences. - /// - DWMWA_USE_IMMERSIVE_DARK_MODE = 20, - - /// - /// Controls the policy that rounds top-level window corners. - /// Windows 11 and above. - /// - DWMWA_WINDOW_CORNER_PREFERENCE = 33, - - /// - /// The color of the thin border around a top-level window. - /// - DWMWA_BORDER_COLOR = 34, - - /// - /// The color of the caption. - /// Windows 11 and above. - /// - DWMWA_CAPTION_COLOR = 35, - - /// - /// The color of the caption text. - /// Windows 11 and above. - /// - DWMWA_TEXT_COLOR = 36, - - /// - /// Width of the visible border around a thick frame window. - /// Windows 11 and above. - /// - DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, - - /// - /// Allows to enter a value from 0 to 4 deciding on the imposed backdrop effect. - /// - DWMWA_SYSTEMBACKDROP_TYPE = 38, - - /// - /// Indicates whether the window should use the Mica effect. - /// Windows 11 and above. - /// - DWMWA_MICA_EFFECT = 1029, - } - - /// - /// Represents the current DWM color accent settings. - /// - [StructLayout(LayoutKind.Sequential)] - public struct DWMCOLORIZATIONPARAMS - { - /// - /// ColorizationColor - /// - public uint clrColor; - - /// - /// ColorizationAfterglow. - /// - public uint clrAfterGlow; - - /// - /// ColorizationColorBalance. - /// - public uint nIntensity; - - /// - /// ColorizationAfterglowBalance. - /// - public uint clrAfterGlowBalance; - - /// - /// ColorizationBlurBalance. - /// - public uint clrBlurBalance; - - /// - /// ColorizationGlassReflectionIntensity. - /// - public uint clrGlassReflectionIntensity; - - /// - /// ColorizationOpaqueBlend. - /// - public bool fOpaque; - } - - /// - /// Defines a data type used by the Desktop Window Manager (DWM) APIs. It represents a generic ratio and is used for different purposes and units even within a single API. - /// - [StructLayout(LayoutKind.Sequential)] - public struct UNSIGNED_RATIO - { - /// - /// The ratio numerator. - /// - public uint uiNumerator; - - /// - /// The ratio denominator. - /// - public uint uiDenominator; - } - - /// - /// Specifies the input operations for which visual feedback should be provided. This enumeration is used by the DwmShowContact function. - /// - public enum DWM_SHOWCONTACT - { - DWMSC_DOWN, - DWMSC_UP, - DWMSC_DRAG, - DWMSC_HOLD, - DWMSC_PENBARREL, - DWMSC_NONE, - DWMSC_ALL, - } - - /// - /// Flags used by the DwmSetPresentParameters function to specify the frame sampling type. - /// - public enum DWM_SOURCE_FRAME_SAMPLING - { - /// - /// Use the first source frame that includes the first refresh of the output frame - /// - DWM_SOURCE_FRAME_SAMPLING_POINT, - - /// - /// Use the source frame that includes the most refreshes of out the output frame - /// in case of multiple source frames with the same coverage the last will be used - /// - DWM_SOURCE_FRAME_SAMPLING_COVERAGE, - - /// - /// Sentinel value. - /// - DWM_SOURCE_FRAME_SAMPLING_LAST, - } - - /// - /// Specifies Desktop Window Manager (DWM) composition timing information. Used by the function. - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct DWM_TIMING_INFO - { - public int cbSize; - public UNSIGNED_RATIO rateRefresh; - public ulong qpcRefreshPeriod; - public UNSIGNED_RATIO rateCompose; - public ulong qpcVBlank; - public ulong cRefresh; - public uint cDXRefresh; - public ulong qpcCompose; - public ulong cFrame; - public uint cDXPresent; - public ulong cRefreshFrame; - public ulong cFrameSubmitted; - public uint cDXPresentSubmitted; - public ulong cFrameConfirmed; - public uint cDXPresentConfirmed; - public ulong cRefreshConfirmed; - public uint cDXRefreshConfirmed; - public ulong cFramesLate; - public uint cFramesOutstanding; - public ulong cFrameDisplayed; - public ulong qpcFrameDisplayed; - public ulong cRefreshFrameDisplayed; - public ulong cFrameComplete; - public ulong qpcFrameComplete; - public ulong cFramePending; - public ulong qpcFramePending; - public ulong cFramesDisplayed; - public ulong cFramesComplete; - public ulong cFramesPending; - public ulong cFramesAvailable; - public ulong cFramesDropped; - public ulong cFramesMissed; - public ulong cRefreshNextDisplayed; - public ulong cRefreshNextPresented; - public ulong cRefreshesDisplayed; - public ulong cRefreshesPresented; - public ulong cRefreshStarted; - public ulong cPixelsReceived; - public ulong cPixelsDrawn; - public ulong cBuffersEmpty; - } - - /// - /// SIT flags. - /// - public enum DWM_SIT - { - /// - /// None. - /// - NONE, - - /// - /// Displays a frame around the provided bitmap. - /// - DISPLAYFRAME = 1, - } - - /// - /// Obtains a value that indicates whether Desktop Window Manager (DWM) composition is enabled. - /// - /// A pointer to a value that, when this function returns successfully, receives TRUE if DWM composition is enabled; otherwise, FALSE. - /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - [DllImport(Libraries.Dwmapi, BestFitMapping = false)] - public static extern int DwmIsCompositionEnabled([Out] out int pfEnabled); - - /// - /// Extends the window frame into the client area. - /// - /// The handle to the window in which the frame will be extended into the client area. - /// A pointer to a MARGINS structure that describes the margins to use when extending the frame into the client area. - [DllImport(Libraries.Dwmapi, PreserveSig = false)] - public static extern void DwmExtendFrameIntoClientArea( - [In] IntPtr hWnd, - [In] ref UxTheme.MARGINS pMarInset - ); - - /// - /// Retrieves the current composition timing information for a specified window. - /// - /// The handle to the window for which the composition timing information should be retrieved. - /// A pointer to a structure that, when this function returns successfully, receives the current composition timing information for the window. - [DllImport(Libraries.Dwmapi)] - public static extern void DwmGetCompositionTimingInfo( - [In] IntPtr hWnd, - [In] ref DWM_TIMING_INFO pTimingInfo - ); - - /// - /// Called by an application to indicate that all previously provided iconic bitmaps from a window, both thumbnails and peek representations, should be refreshed. - /// - /// A handle to the window or tab whose bitmaps are being invalidated through this call. This window must belong to the calling process. - [DllImport(Libraries.Dwmapi, PreserveSig = false)] - public static extern void DwmInvalidateIconicBitmaps([In] IntPtr hWnd); - - /// - /// Sets a static, iconic bitmap on a window or tab to use as a thumbnail representation. The taskbar can use this bitmap as a thumbnail switch target for the window or tab. - /// - /// A handle to the window or tab. This window must belong to the calling process. - /// A handle to the bitmap to represent the window that hwnd specifies. - /// The display options for the thumbnail. - [DllImport(Libraries.Dwmapi, PreserveSig = false)] - public static extern void DwmSetIconicThumbnail( - [In] IntPtr hWnd, - [In] IntPtr hbmp, - [In] DWM_SIT dwSITFlags - ); - - /// - /// Sets a static, iconic bitmap to display a live preview (also known as a Peek preview) of a window or tab. The taskbar can use this bitmap to show a full-sized preview of a window or tab. - /// - /// A handle to the window. This window must belong to the calling process. - /// A handle to the bitmap to represent the window that hwnd specifies. - /// The offset of a tab window's client region (the content area inside the client window frame) from the host window's frame. This offset enables the tab window's contents to be drawn correctly in a live preview when it is drawn without its frame. - /// The display options for the live preview. - [DllImport(Libraries.Dwmapi, PreserveSig = false)] - public static extern int DwmSetIconicLivePreviewBitmap( - [In] IntPtr hWnd, - [In] IntPtr hbmp, - [In, Optional] WinDef.POINT pptClient, - [In] DWM_SIT dwSITFlags - ); - - /// - /// Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window. - /// - /// The handle to the window for which the attribute value is to be set. - /// A flag describing which value to set, specified as a value of the DWMWINDOWATTRIBUTE enumeration. - /// A pointer to an object containing the attribute value to set. - /// The size, in bytes, of the attribute value being set via the pvAttribute parameter. - /// If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - [DllImport(Libraries.Dwmapi)] - public static extern int DwmSetWindowAttribute( - [In] IntPtr hWnd, - [In] int dwAttribute, - [In] ref int pvAttribute, - [In] int cbAttribute - ); - - /// - /// Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window. - /// - /// The handle to the window for which the attribute value is to be set. - /// A flag describing which value to set, specified as a value of the DWMWINDOWATTRIBUTE enumeration. - /// A pointer to an object containing the attribute value to set. - /// The size, in bytes, of the attribute value being set via the pvAttribute parameter. - /// If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - [DllImport(Libraries.Dwmapi)] - public static extern int DwmSetWindowAttribute( - [In] IntPtr hWnd, - [In] DWMWINDOWATTRIBUTE dwAttribute, - [In] ref int pvAttribute, - [In] int cbAttribute - ); - - /// - /// Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window. - /// - /// The handle to the window for which the attribute value is to be set. - /// A flag describing which value to set, specified as a value of the DWMWINDOWATTRIBUTE enumeration. - /// A pointer to an object containing the attribute value to set. - /// The size, in bytes, of the attribute value being set via the pvAttribute parameter. - /// If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - [DllImport(Libraries.Dwmapi)] - public static extern int DwmSetWindowAttribute( - [In] IntPtr hWnd, - [In] DWMWINDOWATTRIBUTE dwAttribute, - [In] ref uint pvAttribute, - [In] int cbAttribute - ); - - /// - /// Retrieves the current value of a specified Desktop Window Manager (DWM) attribute applied to a window. For programming guidance, and code examples, see Controlling non-client region rendering. - /// - /// The handle to the window from which the attribute value is to be retrieved. - /// A flag describing which value to retrieve, specified as a value of the enumeration. - /// A pointer to a value which, when this function returns successfully, receives the current value of the attribute. The type of the retrieved value depends on the value of the dwAttribute parameter. - /// The size, in bytes, of the attribute value being received via the pvAttribute parameter. - /// If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - [DllImport(Libraries.Dwmapi)] - public static extern int DwmGetWindowAttribute( - [In] IntPtr hWnd, - [In] DWMWINDOWATTRIBUTE dwAttributeToGet, - [In] ref int pvAttributeValue, - [In] int cbAttribute - ); - - /// - /// Retrieves the current value of a specified Desktop Window Manager (DWM) attribute applied to a window. For programming guidance, and code examples, see Controlling non-client region rendering. - /// - /// The handle to the window from which the attribute value is to be retrieved. - /// A flag describing which value to retrieve, specified as a value of the enumeration. - /// A pointer to a value which, when this function returns successfully, receives the current value of the attribute. The type of the retrieved value depends on the value of the dwAttribute parameter. - /// The size, in bytes, of the attribute value being received via the pvAttribute parameter. - /// If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. - [DllImport(Libraries.Dwmapi)] - public static extern int DwmGetWindowAttribute( - [In] IntPtr hWnd, - [In] int dwAttributeToGet, - [In] ref int pvAttributeValue, - [In] int cbAttribute - ); - - /// - /// The feature is not included in the Microsoft documentation. Reads Desktop Window Manager (DWM) color information. - /// - /// A pointer to a reference value that will hold the color information. - [DllImport(Libraries.Dwmapi, EntryPoint = "#127", PreserveSig = false, CharSet = CharSet.Unicode)] - public static extern void DwmGetColorizationParameters([Out] out DWMCOLORIZATIONPARAMS dwParameters); -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter -#pragma warning restore CA1060 // Move pinvokes to native methods class diff --git a/src/Wpf.Ui/Interop/HRESULT.cs b/src/Wpf.Ui/Interop/HRESULT.cs deleted file mode 100644 index 3d829dab2..000000000 --- a/src/Wpf.Ui/Interop/HRESULT.cs +++ /dev/null @@ -1,44 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop; - -/// -/// Common Windows API result; -/// -internal struct HRESULT -{ - /// - /// Operation successful. - /// - public const int S_OK = unchecked((int)0x00000000); - - /// - /// Operation successful. - /// - public const int NO_ERROR = unchecked((int)0x00000000); - - /// - /// Operation successful. - /// - public const int NOERROR = unchecked((int)0x00000000); - - /// - /// Unspecified failure. - /// - public const int S_FALSE = unchecked((int)0x00000001); - - public static void Check(int hr) - { - if (hr >= S_OK) - { - return; - } - - Marshal.ThrowExceptionForHR(hr, (IntPtr)(-1)); - } -} diff --git a/src/Wpf.Ui/Interop/Kernel32.cs b/src/Wpf.Ui/Interop/Kernel32.cs deleted file mode 100644 index a460c99cf..000000000 --- a/src/Wpf.Ui/Interop/Kernel32.cs +++ /dev/null @@ -1,51 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -// This Source Code is partially based on reverse engineering of the Windows Operating System, -// and is intended for use on Windows systems only. -// This Source Code is partially based on the source code provided by the .NET Foundation. -// -// NOTE: -// I split unmanaged code stuff into the NativeMethods library. -// If you have suggestions for the code below, please submit your changes there. -// https://github.com/lepoco/nativemethods - -// ReSharper disable IdentifierTypo -// ReSharper disable InconsistentNaming -using System.Runtime.InteropServices; - -#pragma warning disable CA1060 // Move pinvokes to native methods class - -namespace Wpf.Ui.Interop; - -/// -/// Used by multiple technologies. -/// -internal class Kernel32 -{ - /// - /// Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. Multiple threads do not overwrite each other's last-error code. - /// - /// The return value is the calling thread's last-error code. - [DllImport(Libraries.Kernel32)] - public static extern int GetLastError(); - - /// - /// Sets the last-error code for the calling thread. - /// - /// The last-error code for the thread. - [DllImport(Libraries.Kernel32, ExactSpelling = true, CharSet = CharSet.Auto)] - public static extern void SetLastError([In] int dwErrorCode); - - /// - /// Determines whether the calling process is being debugged by a user-mode debugger. - /// - /// If the current process is running in the context of a debugger, the return value is nonzero. - [DllImport(Libraries.Kernel32, ExactSpelling = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsDebuggerPresent(); -} - -#pragma warning restore CA1060 // Move pinvokes to native methods class diff --git a/src/Wpf.Ui/Interop/Libraries.cs b/src/Wpf.Ui/Interop/Libraries.cs deleted file mode 100644 index f40e3613b..000000000 --- a/src/Wpf.Ui/Interop/Libraries.cs +++ /dev/null @@ -1,48 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -namespace Wpf.Ui.Interop; - -/// -/// Windows kernel module. -/// -internal static class Libraries -{ - /*internal const string Advapi32 = "advapi32.dll"; - internal const string BCrypt = "BCrypt.dll"; - internal const string CoreComm_L1_1_1 = "api-ms-win-core-comm-l1-1-1.dll"; - internal const string Crypt32 = "crypt32.dll";*/ - internal const string Dwmapi = "dwmapi.dll"; - - /*internal const string Error_L1 = "api-ms-win-core-winrt-error-l1-1-0.dll"; - internal const string HttpApi = "httpapi.dll"; - internal const string IpHlpApi = "iphlpapi.dll";*/ - internal const string Kernel32 = "kernel32.dll"; - - /*internal const string Memory_L1_3 = "api-ms-win-core-memory-l1-1-3.dll"; - internal const string Mswsock = "mswsock.dll"; - internal const string NCrypt = "ncrypt.dll"; - internal const string NtDll = "ntdll.dll"; - internal const string Odbc32 = "odbc32.dll"; - internal const string OleAut32 = "oleaut32.dll"; - internal const string PerfCounter = "perfcounter.dll"; - internal const string RoBuffer = "api-ms-win-core-winrt-robuffer-l1-1-0.dll"; - internal const string Secur32 = "secur32.dll";*/ - internal const string Shell32 = "shell32.dll"; - - /*internal const string SspiCli = "sspicli.dll";*/ - internal const string User32 = "user32.dll"; - internal const string UxTheme = "uxtheme.dll"; - - /*internal const string Gdi32 = "gdi32.dll"; - internal const string Gdip = "gdiplus.dll";*/ - internal const string Version = "version.dll"; - /*internal const string WebSocket = "websocket.dll"; - internal const string WinHttp = "winhttp.dll"; - internal const string WinMM = "winmm.dll"; - internal const string Ws2_32 = "ws2_32.dll"; - internal const string Wtsapi32 = "wtsapi32.dll"; - internal const string CompressionNative = "System.IO.Compression.Native.dll";*/ -} diff --git a/src/Wpf.Ui/Interop/PInvoke.cs b/src/Wpf.Ui/Interop/PInvoke.cs new file mode 100644 index 000000000..1d4c052c5 --- /dev/null +++ b/src/Wpf.Ui/Interop/PInvoke.cs @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Runtime.InteropServices; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; + +namespace Windows.Win32; + +internal static partial class PInvoke +{ + [ + DllImport("USER32.dll", ExactSpelling = true, EntryPoint = "SetWindowLongPtrW", SetLastError = true), + DefaultDllImportSearchPaths(DllImportSearchPath.System32) + ] + internal static extern nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong); +} diff --git a/src/Wpf.Ui/Interop/ShObjIdl.cs b/src/Wpf.Ui/Interop/ShObjIdl.cs deleted file mode 100644 index 5c8efaae3..000000000 --- a/src/Wpf.Ui/Interop/ShObjIdl.cs +++ /dev/null @@ -1,243 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -// This Source Code is partially based on reverse engineering of the Windows Operating System, -// and is intended for use on Windows systems only. -// This Source Code is partially based on the source code provided by the .NET Foundation. -// -// NOTE: -// I split unmanaged code stuff into the NativeMethods library. -// If you have suggestions for the code below, please submit your changes there. -// https://github.com/lepoco/nativemethods - -// ReSharper disable IdentifierTypo -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable CA1060 // Move pinvokes to native methods class - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop; - -/// -/// Exposes methods that enumerate the contents of a view and receive notification from callback upon enumeration completion. -/// -internal static class ShObjIdl -{ - /// - /// THUMBBUTTON flags. THBF_* - /// - [Flags] - public enum THUMBBUTTONFLAGS - { - THBF_ENABLED = 0, - THBF_DISABLED = 0x1, - THBF_DISMISSONCLICK = 0x2, - THBF_NOBACKGROUND = 0x4, - THBF_HIDDEN = 0x8, - THBF_NONINTERACTIVE = 0x10, - } - - /// - /// THUMBBUTTON mask. THB_* - /// - [Flags] - public enum THUMBBUTTONMASK - { - THB_BITMAP = 0x1, - THB_ICON = 0x2, - THB_TOOLTIP = 0x4, - THB_FLAGS = 0x8, - } - - /// - /// TBPF_* - /// - [Flags] - public enum TBPFLAG - { - TBPF_NOPROGRESS = 0, - TBPF_INDETERMINATE = 0x1, - TBPF_NORMAL = 0x2, - TBPF_ERROR = 0x4, - TBPF_PAUSED = 0x8, - } - - /// - /// STPF_* - /// - [Flags] - public enum STPFLAG - { - STPF_NONE = 0, - STPF_USEAPPTHUMBNAILALWAYS = 0x1, - STPF_USEAPPTHUMBNAILWHENACTIVE = 0x2, - STPF_USEAPPPEEKALWAYS = 0x4, - STPF_USEAPPPEEKWHENACTIVE = 0x8, - } - - /// - /// EBO_* - /// - public enum EXPLORER_BROWSER_OPTIONS - { - EBO_NONE = 0, - EBO_NAVIGATEONCE = 0x1, - EBO_SHOWFRAMES = 0x2, - EBO_ALWAYSNAVIGATE = 0x4, - EBO_NOTRAVELLOG = 0x8, - EBO_NOWRAPPERWINDOW = 0x10, - EBO_HTMLSHAREPOINTVIEW = 0x20, - EBO_NOBORDER = 0x40, - EBO_NOPERSISTVIEWSTATE = 0x80, - } - - /// - /// EBF_* - /// - public enum EXPLORER_BROWSER_FILL_FLAGS - { - EBF_NONE = 0, - EBF_SELECTFROMDATAOBJECT = 0x100, - EBF_NODROPTARGET = 0x200, - } - - /// - /// THUMBBUTTON - /// - [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] - public struct THUMBBUTTON - { - /// - /// WPARAM value for a THUMBBUTTON being clicked. - /// - public const int THBN_CLICKED = 0x1800; - - public THUMBBUTTONMASK dwMask; - public uint iId; - public uint iBitmap; - public IntPtr hIcon; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szTip; - - public THUMBBUTTONFLAGS dwFlags; - } - - /// - /// Class DECLSPEC_UUID("56FDF344-FD6D-11d0-958A-006097C9A090") - /// - [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] - [ClassInterface(ClassInterfaceType.None)] - [ComImport] - public class CTaskbarList { } - - /// - /// Class DECLSPEC_UUID("9ac9fbe1-e0a2-4ad6-b4ee-e212013ea917") - /// - [Guid("9ac9fbe1-e0a2-4ad6-b4ee-e212013ea917")] - [ClassInterface(ClassInterfaceType.None)] - [ComImport] - public class ShellItem { } - - /// - /// MIDL_INTERFACE("c43dc798-95d1-4bea-9030-bb99e2983a1a") - /// ITaskbarList4 : public ITaskbarList3 - /// - [ComImport] - [Guid("c43dc798-95d1-4bea-9030-bb99e2983a1a")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface ITaskbarList4 - { - // ITaskbarList - [PreserveSig] - void HrInit(); - - [PreserveSig] - void AddTab(IntPtr hwnd); - - [PreserveSig] - void DeleteTab(IntPtr hwnd); - - [PreserveSig] - void ActivateTab(IntPtr hwnd); - - [PreserveSig] - void SetActiveAlt(IntPtr hwnd); - - // ITaskbarList2 - [PreserveSig] - void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); - - // ITaskbarList3 - [PreserveSig] - void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); - - [PreserveSig] - void SetProgressState(IntPtr hwnd, TBPFLAG tbpFlags); - - [PreserveSig] - void RegisterTab(IntPtr hwndTab, IntPtr hwndMDI); - - [PreserveSig] - void UnregisterTab(IntPtr hwndTab); - - [PreserveSig] - void SetTabOrder(IntPtr hwndTab, IntPtr hwndInsertBefore); - - [PreserveSig] - void SetTabActive(IntPtr hwndTab, IntPtr hwndInsertBefore, uint dwReserved); - - /// - /// Adds thumbnail toolbar buttons to the specified window. - /// - /// Window handle. - /// Number of buttons. - /// Array of buttons. - /// HRESULT - [PreserveSig] - int ThumbBarAddButtons( - IntPtr hwnd, - uint cButtons, - [MarshalAs(UnmanagedType.LPArray)] THUMBBUTTON[] pButtons - ); - - /// - /// Updates thumbnail toolbar buttons for the specified window. - /// - /// Window handle. - /// Number of buttons. - /// Array of buttons to update. - /// HRESULT - [PreserveSig] - int ThumbBarUpdateButtons( - IntPtr hwnd, - uint cButtons, - [MarshalAs(UnmanagedType.LPArray)] THUMBBUTTON[] pButtons - ); - - [PreserveSig] - void ThumbBarSetImageList(IntPtr hWnd, IntPtr himl); - - [PreserveSig] - void SetOverlayIcon( - IntPtr hwnd, - IntPtr hIcon, - [MarshalAs(UnmanagedType.LPWStr)] string pszDescription - ); - - [PreserveSig] - void SetThumbnailTooltip(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)] string pszTip); - - [PreserveSig] - void SetThumbnailClip(IntPtr hwnd, IntPtr prcClip); - - // ITaskbarList4 - void SetTabProperties(IntPtr hwndTab, STPFLAG stpFlags); - } -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter -#pragma warning restore CA1060 // Move pinvokes to native methods class diff --git a/src/Wpf.Ui/Interop/Shell32.cs b/src/Wpf.Ui/Interop/Shell32.cs deleted file mode 100644 index ce4580da6..000000000 --- a/src/Wpf.Ui/Interop/Shell32.cs +++ /dev/null @@ -1,192 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -// This Source Code is partially based on reverse engineering of the Windows Operating System, -// and is intended for use on Windows systems only. -// This Source Code is partially based on the source code provided by the .NET Foundation. -// -// NOTE: -// I split unmanaged code stuff into the NativeMethods library. -// If you have suggestions for the code below, please submit your changes there. -// https://github.com/lepoco/nativemethods - -// ReSharper disable IdentifierTypo -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable SA1401 // Fields should be private -#pragma warning disable CA1060 // Move pinvokes to native methods class - -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; - -namespace Wpf.Ui.Interop; - -/// -/// The Windows UI provides users with access to a wide variety of objects necessary to run applications and manage the operating system. -/// -internal static class Shell32 -{ - /// - /// DATAOBJ_GET_ITEM_FLAGS. DOGIF_*. - /// - public enum DOGIF - { - DEFAULT = 0x0000, - TRAVERSE_LINK = 0x0001, // if the item is a link get the target - NO_HDROP = 0x0002, // don't fallback and use CF_HDROP clipboard format - NO_URL = 0x0004, // don't fallback and use URL clipboard format - ONLY_IF_ONE = 0x0008, // only return the item if there is one item in the array - } - - /// - /// Shell_NotifyIcon messages. NIM_* - /// - public enum NIM : uint - { - ADD = 0, - MODIFY = 1, - DELETE = 2, - SETFOCUS = 3, - SETVERSION = 4, - } - - /// - /// Shell_NotifyIcon flags. NIF_* - /// - [Flags] - public enum NIF : uint - { - MESSAGE = 0x0001, - ICON = 0x0002, - TIP = 0x0004, - STATE = 0x0008, - INFO = 0x0010, - GUID = 0x0020, - - /// - /// Vista only. - /// - REALTIME = 0x0040, - - /// - /// Vista only. - /// - SHOWTIP = 0x0080, - - XP_MASK = MESSAGE | ICON | STATE | INFO | GUID, - VISTA_MASK = XP_MASK | REALTIME | SHOWTIP, - } - - [StructLayout(LayoutKind.Sequential)] - public class NOTIFYICONDATA - { - /// - /// The size of this structure, in bytes. - /// - public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA)); - - /// - /// A handle to the window that receives notifications associated with an icon in the notification area. - /// - public IntPtr hWnd; - - /// - /// The application-defined identifier of the taskbar icon. The Shell uses either (hWnd plus uID) or guidItem to identify which icon to operate on when Shell_NotifyIcon is invoked. - /// You can have multiple icons associated with a single hWnd by assigning each a different uID. If guidItem is specified, uID is ignored. - /// - public int uID; - - /// - /// Flags that either indicate which of the other members of the structure contain valid data or provide additional information to the tooltip as to how it should display. - /// - public NIF uFlags; - - /// - /// 0x00000001. The uCallbackMessage member is valid. - /// - public int uCallbackMessage; - - /// - /// 0x00000002. The hIcon member is valid. - /// - public IntPtr hIcon; - - /// - /// 0x00000004. The szTip member is valid. - /// - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)] // 128 - public string? szTip; - - /// - /// The state of the icon. There are two flags that can be set independently. - /// NIS_HIDDEN = 1. The icon is hidden. - /// NIS_SHAREDICON = 2. The icon is shared. - /// - public uint dwState; - - public uint dwStateMask; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x100)] // 256 - public string? szInfo; - - /// - /// Prior to Vista this was a union of uTimeout and uVersion. As of Vista, uTimeout has been deprecated. - /// - public uint uVersion; // Used with Shell_NotifyIcon flag NIM_SETVERSION. - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x40)] // 64 - public string? szInfoTitle; - - public uint dwInfoFlags; - - public Guid guidItem; - - // Vista only - public IntPtr hBalloonIcon; - } - - [DllImport(Libraries.Shell32, PreserveSig = false)] - public static extern void SHGetItemFromDataObject( - System.Runtime.InteropServices.ComTypes.IDataObject pdtobj, - DOGIF dwFlags, - [In] ref Guid riid, - [Out, MarshalAs(UnmanagedType.Interface)] out object ppv - ); - - [DllImport(Libraries.Shell32)] - public static extern int SHCreateItemFromParsingName( - [MarshalAs(UnmanagedType.LPWStr)] string pszPath, - IBindCtx pbc, - [In] ref Guid riid, - [Out, MarshalAs(UnmanagedType.Interface)] out object ppv - ); - - [DllImport(Libraries.Shell32)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool Shell_NotifyIcon([In] NIM dwMessage, [In] NOTIFYICONDATA lpdata); - - /// - /// Sets the User Model AppID for the current process, enabling Windows to retrieve this ID - /// - /// The string ID - [DllImport(Libraries.Shell32, PreserveSig = false)] - public static extern void SetCurrentProcessExplicitAppUserModelID( - [MarshalAs(UnmanagedType.LPWStr)] string AppID - ); - - /// - /// Retrieves the User Model AppID that has been explicitly set for the current process via SetCurrentProcessExplicitAppUserModelID - /// - /// The returned string ID of the Application User Model. - /// An HRESULT indicating success or failure of the operation. - [DllImport(Libraries.Shell32)] - public static extern int GetCurrentProcessExplicitAppUserModelID( - [Out, MarshalAs(UnmanagedType.LPWStr)] out string AppID - ); -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter -#pragma warning restore SA1401 // Fields should be private -#pragma warning restore CA1060 // Move pinvokes to native methods class diff --git a/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs b/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs index be8c6f217..067e3ef5b 100644 --- a/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs +++ b/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs @@ -7,57 +7,129 @@ and is intended for use on Windows systems only. This Source Code is partially based on the source code provided by the .NET Foundation. */ -using System.Runtime.InteropServices; using Microsoft.Win32; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Dwm; +using Windows.Win32.UI.Controls; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.WindowsAndMessaging; using Wpf.Ui.Controls; -using Wpf.Ui.Hardware; namespace Wpf.Ui.Interop; /// /// A set of dangerous methods to modify the appearance. /// -public static class UnsafeNativeMethods +internal static class UnsafeNativeMethods { /// - /// Tries to set the corner preference. + /// Tries to set the corner preference of the selected window. /// - /// Selected window. + /// Selected window handle. /// Window corner preference. /// if invocation of native Windows function succeeds. - public static bool ApplyWindowCornerPreference(Window window, WindowCornerPreference cornerPreference) => - GetHandle(window, out IntPtr windowHandle) - && ApplyWindowCornerPreference(windowHandle, cornerPreference); + public static unsafe bool ApplyWindowCornerPreference( + IntPtr handle, + WindowCornerPreference cornerPreference + ) + { + if (handle == IntPtr.Zero) + { + return false; + } + + if (!PInvoke.IsWindow(new HWND(handle))) + { + return false; + } + + DWM_WINDOW_CORNER_PREFERENCE pvAttribute = UnsafeReflection.Cast(cornerPreference); + + return PInvoke.DwmSetWindowAttribute( + new HWND(handle), + DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, + &pvAttribute, + sizeof(int) + ) == HRESULT.S_OK; + } /// - /// Tries to set the corner preference of the selected window. + /// Tries to apply the color of the border. /// - /// Selected window handle. - /// Window corner preference. + /// The window. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(Window window, Color color) => + GetHandle(window, out IntPtr windowHandle) && ApplyBorderColor(windowHandle, color); + + /// + /// Tries to apply the color of the border. + /// + /// The window. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(Window window, int color) => + GetHandle(window, out IntPtr windowHandle) && ApplyBorderColor(windowHandle, color); + + /// + /// Tries to apply the color of the border. + /// + /// The handle. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(IntPtr handle, Color color) + { + return ApplyBorderColor(handle, (color.B << 16) | (color.G << 8) | color.R); + } + + /// + /// Tries to apply the color of the border. + /// + /// The handle. + /// The color. /// if invocation of native Windows function succeeds. - public static bool ApplyWindowCornerPreference(IntPtr handle, WindowCornerPreference cornerPreference) + public static unsafe bool ApplyBorderColor(IntPtr handle, int color) { if (handle == IntPtr.Zero) { return false; } - if (!User32.IsWindow(handle)) + if (!PInvoke.IsWindow(new HWND(handle))) { return false; } - int pvAttribute = (int)UnsafeReflection.Cast(cornerPreference); + return PInvoke.DwmSetWindowAttribute( + new HWND(handle), + DWMWINDOWATTRIBUTE.DWMWA_BORDER_COLOR, + &color, + sizeof(int) + ) == HRESULT.S_OK; + } - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute( - handle, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, - ref pvAttribute, - Marshal.SizeOf(typeof(int)) - ); + /// + /// Checks whether accent color on title bars and window borders is enabled in Windows settings. + /// + /// if accent color on title bars is enabled. + public static bool IsAccentColorOnTitleBarsEnabled() + { + try + { + using RegistryKey? key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\DWM"); - return true; + if (key?.GetValue("ColorPrevalence") is int value) + { + return value == 1; + } + } + catch + { + // Ignore registry access errors + } + + return false; } /// @@ -73,30 +145,28 @@ public static bool RemoveWindowDarkMode(Window? window) => /// /// Window handle. /// if invocation of native Windows function succeeds. - public static bool RemoveWindowDarkMode(IntPtr handle) + public static unsafe bool RemoveWindowDarkMode(IntPtr handle) { if (handle == IntPtr.Zero) { return false; } - if (!User32.IsWindow(handle)) + if (!PInvoke.IsWindow(new HWND(handle))) { return false; } - var pvAttribute = 0x0; // Disable - Dwmapi.DWMWINDOWATTRIBUTE dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; + BOOL pvAttribute = false; + DWMWINDOWATTRIBUTE dwAttribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; if (!Win32.Utilities.IsOSWindows11Insider1OrNewer) { - dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; + dwAttribute = DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; } - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))); - - return true; + return PInvoke.DwmSetWindowAttribute(new HWND(handle), dwAttribute, &pvAttribute, (uint)sizeof(BOOL)) + == HRESULT.S_OK; } /// @@ -112,30 +182,28 @@ public static bool ApplyWindowDarkMode(Window? window) => /// /// Window handle. /// if invocation of native Windows function succeeds. - public static bool ApplyWindowDarkMode(IntPtr handle) + public static unsafe bool ApplyWindowDarkMode(IntPtr handle) { if (handle == IntPtr.Zero) { return false; } - if (!User32.IsWindow(handle)) + if (!PInvoke.IsWindow(new HWND(handle))) { return false; } - var pvAttribute = 0x1; // Enable - Dwmapi.DWMWINDOWATTRIBUTE dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; + BOOL pvAttribute = true; + DWMWINDOWATTRIBUTE dwAttribute = DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE; if (!Win32.Utilities.IsOSWindows11Insider1OrNewer) { - dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; + dwAttribute = DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; } - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))); - - return true; + return PInvoke.DwmSetWindowAttribute(new HWND(handle), dwAttribute, &pvAttribute, (uint)sizeof(BOOL)) + == HRESULT.S_OK; } /// @@ -176,211 +244,48 @@ public static bool RemoveWindowTitlebarContents(IntPtr handle) return false; } - if (!User32.IsWindow(handle)) + if (!PInvoke.IsWindow(new HWND(handle))) { return false; } - var windowStyleLong = User32.GetWindowLong(handle, User32.GWL.GWL_STYLE); - windowStyleLong &= ~(int)User32.WS.SYSMENU; + var windowStyleLong = PInvoke.GetWindowLong(new HWND(handle), WINDOW_LONG_PTR_INDEX.GWL_STYLE); + windowStyleLong &= ~(int)WINDOW_STYLE.WS_SYSMENU; - IntPtr result = SetWindowLong(handle, User32.GWL.GWL_STYLE, windowStyleLong); + IntPtr result = SetWindowLong(handle, WINDOW_LONG_PTR_INDEX.GWL_STYLE, windowStyleLong); - return result.ToInt64() > 0x0; - } - - /// - /// Tries to apply selected backdrop type for window handle. - /// - /// Selected window handle. - /// Backdrop type. - /// if invocation of native Windows function succeeds. - public static bool ApplyWindowBackdrop(IntPtr handle, WindowBackdropType backgroundType) - { - if (handle == IntPtr.Zero) - { - return false; - } - - if (!User32.IsWindow(handle)) - { - return false; - } - - var backdropPvAttribute = (int)UnsafeReflection.Cast(backgroundType); - - if (backdropPvAttribute == (int)Dwmapi.DWMSBT.DWMSBT_DISABLE) - { - return false; - } - - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute( - handle, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, - ref backdropPvAttribute, - Marshal.SizeOf(typeof(int)) - ); - - return true; - } - - /// - /// Tries to determine whether the provided has applied legacy backdrop effect. - /// - /// Window handle. - /// Background backdrop type. - public static bool IsWindowHasBackdrop(IntPtr handle, WindowBackdropType backdropType) - { - if (!User32.IsWindow(handle)) - { - return false; - } - - var pvAttribute = 0x0; - - _ = Dwmapi.DwmGetWindowAttribute( - handle, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, - ref pvAttribute, - Marshal.SizeOf(typeof(int)) - ); - - return pvAttribute == (int)UnsafeReflection.Cast(backdropType); - } - - /// - /// Tries to determine whether the provided has applied legacy Mica effect. - /// - /// Window to check. - public static bool IsWindowHasLegacyMica(Window? window) => - GetHandle(window, out IntPtr windowHandle) && IsWindowHasLegacyMica(windowHandle); - - /// - /// Tries to determine whether the provided handle has applied legacy Mica effect. - /// - /// Window handle. - public static bool IsWindowHasLegacyMica(IntPtr handle) - { - if (!User32.IsWindow(handle)) - { - return false; - } - - var pvAttribute = 0x0; - - _ = Dwmapi.DwmGetWindowAttribute( - handle, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, - ref pvAttribute, - Marshal.SizeOf(typeof(int)) - ); - - return pvAttribute == 0x1; - } - - /// - /// Tries to apply legacy Mica effect for the selected . - /// - /// The window to which the effect is to be applied. - /// if invocation of native Windows function succeeds. - public static bool ApplyWindowLegacyMicaEffect(Window? window) => - GetHandle(window, out IntPtr windowHandle) && ApplyWindowLegacyMicaEffect(windowHandle); - - /// - /// Tries to apply legacy Mica effect for the selected . - /// - /// Window handle. - /// if invocation of native Windows function succeeds. - public static bool ApplyWindowLegacyMicaEffect(IntPtr handle) - { - var backdropPvAttribute = 0x1; // Enable - - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute( - handle, - Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, - ref backdropPvAttribute, - Marshal.SizeOf(typeof(int)) - ); - - return true; - } - - /// - /// Tries to apply legacy Acrylic effect for the selected . - /// - /// The window to which the effect is to be applied. - /// if invocation of native Windows function succeeds. - public static bool ApplyWindowLegacyAcrylicEffect(Window? window) => - GetHandle(window, out IntPtr windowHandle) && ApplyWindowLegacyAcrylicEffect(windowHandle); - - /// - /// Tries to apply legacy Acrylic effect for the selected . - /// - /// Window handle - /// if invocation of native Windows function succeeds. - public static bool ApplyWindowLegacyAcrylicEffect(IntPtr handle) - { - var accentPolicy = new Interop.User32.ACCENT_POLICY - { - nAccentState = User32.ACCENT_STATE.ACCENT_ENABLE_ACRYLICBLURBEHIND, - nColor = 0x990000 & 0xFFFFFF, - }; - - int accentStructSize = Marshal.SizeOf(accentPolicy); - IntPtr accentPtr = Marshal.AllocHGlobal(accentStructSize); - - Marshal.StructureToPtr(accentPolicy, accentPtr, false); - - var data = new User32.WINCOMPATTRDATA - { - Attribute = User32.WCA.WCA_ACCENT_POLICY, - SizeOfData = accentStructSize, - Data = accentPtr, - }; - - _ = User32.SetWindowCompositionAttribute(handle, ref data); - - Marshal.FreeHGlobal(accentPtr); - - return true; + return result.ToInt64() > 0; } /// /// Tries to get currently selected Window accent color. /// - public static Color GetDwmColor() + public static Color GetAccentColor() { try { - Dwmapi.DwmGetColorizationParameters(out Dwmapi.DWMCOLORIZATIONPARAMS dwmParams); - var values = BitConverter.GetBytes(dwmParams.clrColor); - - return Color.FromArgb(255, values[2], values[1], values[0]); - } - catch - { - var colorizationColorValue = Registry.GetValue( + var accentColorValue = Registry.GetValue( @"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", - "ColorizationColor", + "AccentColor", null ); - if (colorizationColorValue is not null) + if (accentColorValue is not null) { - try - { - var colorizationColor = (uint)(int)colorizationColorValue; - var values = BitConverter.GetBytes(colorizationColor); - - return Color.FromArgb(255, values[2], values[1], values[0]); - } - catch { } + var accentColor = (uint)(int)accentColorValue; + var values = BitConverter.GetBytes(accentColor); + + return Color.FromArgb(255, values[0], values[1], values[2]); } } + catch + { + // Ignored. + } - return GetDefaultWindowsAccentColor(); + // Windows default accent color + // https://learn.microsoft.com/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-themes-windowcolor#values + return Color.FromArgb(0xff, 0x00, 0x78, 0xd7); } /// @@ -388,25 +293,25 @@ public static Color GetDwmColor() /// /// Window handle. /// Taskbar flag. - internal static bool SetTaskbarState(IntPtr hWnd, ShObjIdl.TBPFLAG taskbarFlag) + internal static bool SetTaskbarState(IntPtr hWnd, TBPFLAG taskbarFlag) { if (hWnd == IntPtr.Zero) { return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } - if (new ShObjIdl.CTaskbarList() is not ShObjIdl.ITaskbarList4 taskbarList) + if (new TaskbarList() is not ITaskbarList4 taskbarList) { return false; } taskbarList.HrInit(); - taskbarList.SetProgressState(hWnd, taskbarFlag); + taskbarList.SetProgressState(new HWND(hWnd), taskbarFlag); return true; } @@ -419,154 +324,64 @@ internal static bool SetTaskbarState(IntPtr hWnd, ShObjIdl.TBPFLAG taskbarFlag) /// Current progress value. /// Maximum progress value. /// True if successful updated, otherwise false. - internal static bool SetTaskbarValue(IntPtr hWnd, ShObjIdl.TBPFLAG taskbarFlag, int current, int total) + internal static bool SetTaskbarValue(IntPtr hWnd, TBPFLAG taskbarFlag, int current, int total) { if (hWnd == IntPtr.Zero) { return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } /* TODO: Get existing taskbar class */ - if (new ShObjIdl.CTaskbarList() is not ShObjIdl.ITaskbarList4 taskbarList) + if (new TaskbarList() is not ITaskbarList4 taskbarList) { return false; } taskbarList.HrInit(); - taskbarList.SetProgressState(hWnd, taskbarFlag); - - if (taskbarFlag is not ShObjIdl.TBPFLAG.TBPF_INDETERMINATE and not ShObjIdl.TBPFLAG.TBPF_NOPROGRESS) - { - taskbarList.SetProgressValue(hWnd, Convert.ToUInt64(current), Convert.ToUInt64(total)); - } - - return true; - } - - public static bool RemoveWindowCaption(Window window) - { - if (window is null) - { - return false; - } - - IntPtr windowHandle = new WindowInteropHelper(window).Handle; + taskbarList.SetProgressState(new HWND(hWnd), taskbarFlag); - return RemoveWindowCaption(windowHandle); - } - - public static bool RemoveWindowCaption(IntPtr hWnd) - { - if (hWnd == IntPtr.Zero) - { - return false; - } - - if (!User32.IsWindow(hWnd)) + if (taskbarFlag is not TBPFLAG.TBPF_INDETERMINATE and not TBPFLAG.TBPF_NOPROGRESS) { - return false; + taskbarList.SetProgressValue(new HWND(hWnd), Convert.ToUInt64(current), Convert.ToUInt64(total)); } - var wtaOptions = new UxTheme.WTA_OPTIONS() - { - dwFlags = UxTheme.WTNCA.NODRAWCAPTION, - dwMask = UxTheme.WTNCA.VALIDBITS, - }; - - UxTheme.SetWindowThemeAttribute( - hWnd, - UxTheme.WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT, - ref wtaOptions, - (uint)Marshal.SizeOf(typeof(UxTheme.WTA_OPTIONS)) - ); - return true; } - public static bool ExtendClientAreaIntoTitleBar(Window window) - { - if (window is null) - { - return false; - } - - IntPtr windowHandle = new WindowInteropHelper(window).Handle; - - return ExtendClientAreaIntoTitleBar(windowHandle); - } - - public static bool ExtendClientAreaIntoTitleBar(IntPtr hWnd) + public static unsafe bool RemoveWindowCaption(IntPtr hWnd) { - /* - * !! EXPERIMENTAl !! - * NOTE: WinRt has ExtendContentIntoTitlebar, but it needs some digging - */ - if (hWnd == IntPtr.Zero) { return false; } - if (!User32.IsWindow(hWnd)) + if (!PInvoke.IsWindow(new HWND(hWnd))) { return false; } - // #1 Remove titlebar elements - var wtaOptions = new UxTheme.WTA_OPTIONS() - { - dwFlags = UxTheme.WTNCA.NODRAWCAPTION | UxTheme.WTNCA.NODRAWICON | UxTheme.WTNCA.NOSYSMENU, - dwMask = UxTheme.WTNCA.VALIDBITS, - }; - - Interop.UxTheme.SetWindowThemeAttribute( - hWnd, - UxTheme.WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT, - ref wtaOptions, - (uint)Marshal.SizeOf(typeof(UxTheme.WTA_OPTIONS)) - ); - - DisplayDpi windowDpi = DpiHelper.GetWindowDpi(hWnd); - - // #2 Extend glass frame - Thickness deviceGlassThickness = DpiHelper.LogicalThicknessToDevice( - new Thickness(-1, -1, -1, -1), - windowDpi.DpiScaleX, - windowDpi.DpiScaleY - ); - - var dwmMargin = new UxTheme.MARGINS + var wtaOptions = new WTA_OPTIONS() { - // err on the side of pushing in glass an extra pixel. - cxLeftWidth = (int)Math.Ceiling(deviceGlassThickness.Left), - cxRightWidth = (int)Math.Ceiling(deviceGlassThickness.Right), - cyTopHeight = (int)Math.Ceiling(deviceGlassThickness.Top), - cyBottomHeight = (int)Math.Ceiling(deviceGlassThickness.Bottom), + dwFlags = PInvoke.WTNCA_NODRAWCAPTION, + dwMask = + PInvoke.WTNCA_NODRAWCAPTION + | PInvoke.WTNCA_NODRAWICON + | PInvoke.WTNCA_NOMIRRORHELP + | PInvoke.WTNCA_NOSYSMENU, }; - // #3 Extend client area - Interop.Dwmapi.DwmExtendFrameIntoClientArea(hWnd, ref dwmMargin); - - // #4 Clear rounding region - Interop.User32.SetWindowRgn(hWnd, IntPtr.Zero, Interop.User32.IsWindowVisible(hWnd)); - - return true; - } - - /// - /// Checks whether the DWM composition is enabled. - /// - public static bool IsCompositionEnabled() - { - _ = Dwmapi.DwmIsCompositionEnabled(out var isEnabled); - - return isEnabled == 0x1; + return PInvoke.SetWindowThemeAttribute( + new HWND(hWnd), + WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT, + &wtaOptions, + (uint)sizeof(WTA_OPTIONS) + ) == HRESULT.S_OK; } /// @@ -574,7 +389,7 @@ public static bool IsCompositionEnabled() /// public static bool IsValidWindow(IntPtr hWnd) { - return User32.IsWindow(hWnd); + return PInvoke.IsWindow(new HWND(hWnd)); } /// @@ -595,20 +410,10 @@ private static bool GetHandle(Window? window, out IntPtr windowHandle) return windowHandle != IntPtr.Zero; } - private static IntPtr SetWindowLong(IntPtr handle, User32.GWL nIndex, long windowStyleLong) - { - if (IntPtr.Size == 4) - { - return new IntPtr(User32.SetWindowLong(handle, (int)nIndex, (int)windowStyleLong)); - } - - return User32.SetWindowLongPtr(handle, (int)nIndex, checked((IntPtr)windowStyleLong)); - } - - private static Color GetDefaultWindowsAccentColor() + private static IntPtr SetWindowLong(IntPtr handle, WINDOW_LONG_PTR_INDEX nIndex, long windowStyleLong) { - // Windows default accent color - // https://learn.microsoft.com/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-themes-windowcolor#values - return Color.FromArgb(0xff, 0x00, 0x78, 0xd7); + return IntPtr.Size == 4 + ? new IntPtr(PInvoke.SetWindowLong(new HWND(handle), nIndex, (int)windowStyleLong)) + : PInvoke.SetWindowLongPtr(new HWND(handle), nIndex, (nint)windowStyleLong); } } diff --git a/src/Wpf.Ui/Interop/UnsafeReflection.cs b/src/Wpf.Ui/Interop/UnsafeReflection.cs index 354b9602b..068f2f7ad 100644 --- a/src/Wpf.Ui/Interop/UnsafeReflection.cs +++ b/src/Wpf.Ui/Interop/UnsafeReflection.cs @@ -7,6 +7,8 @@ and is intended for use on Windows systems only. This Source Code is partially based on the source code provided by the .NET Foundation. */ +using Windows.Win32.Graphics.Dwm; +using Windows.Win32.UI.Shell; using Wpf.Ui.Controls; using Wpf.Ui.TaskBar; @@ -18,61 +20,31 @@ namespace Wpf.Ui.Interop; internal static class UnsafeReflection { /// - /// Casts to . + /// Casts to . /// - public static Dwmapi.DWMSBT Cast(WindowBackdropType backgroundType) - { - return backgroundType switch - { - WindowBackdropType.Auto => Dwmapi.DWMSBT.DWMSBT_AUTO, - WindowBackdropType.Mica => Dwmapi.DWMSBT.DWMSBT_MAINWINDOW, - WindowBackdropType.Acrylic => Dwmapi.DWMSBT.DWMSBT_TRANSIENTWINDOW, - WindowBackdropType.Tabbed => Dwmapi.DWMSBT.DWMSBT_TABBEDWINDOW, - _ => Dwmapi.DWMSBT.DWMSBT_DISABLE, - }; - } - - /// - /// Casts to . - /// - public static Dwmapi.DWM_WINDOW_CORNER_PREFERENCE Cast(WindowCornerPreference cornerPreference) + public static DWM_WINDOW_CORNER_PREFERENCE Cast(WindowCornerPreference cornerPreference) { return cornerPreference switch { - WindowCornerPreference.Round => Dwmapi.DWM_WINDOW_CORNER_PREFERENCE.ROUND, - WindowCornerPreference.RoundSmall => Dwmapi.DWM_WINDOW_CORNER_PREFERENCE.ROUNDSMALL, - WindowCornerPreference.DoNotRound => Dwmapi.DWM_WINDOW_CORNER_PREFERENCE.DONOTROUND, - _ => Dwmapi.DWM_WINDOW_CORNER_PREFERENCE.DEFAULT, + WindowCornerPreference.Round => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND, + WindowCornerPreference.RoundSmall => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUNDSMALL, + WindowCornerPreference.DoNotRound => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DONOTROUND, + _ => DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_DEFAULT, }; } /// - /// Casts to . + /// Casts to . /// - public static ShObjIdl.TBPFLAG Cast(TaskBarProgressState taskBarProgressState) + public static TBPFLAG Cast(TaskBarProgressState taskBarProgressState) { return taskBarProgressState switch { - TaskBarProgressState.Indeterminate => ShObjIdl.TBPFLAG.TBPF_INDETERMINATE, - TaskBarProgressState.Error => ShObjIdl.TBPFLAG.TBPF_ERROR, - TaskBarProgressState.Paused => ShObjIdl.TBPFLAG.TBPF_PAUSED, - TaskBarProgressState.Normal => ShObjIdl.TBPFLAG.TBPF_NORMAL, - _ => Wpf.Ui.Interop.ShObjIdl.TBPFLAG.TBPF_NOPROGRESS, - }; - } - - /// - /// Casts to . - /// - public static TaskBarProgressState Cast(ShObjIdl.TBPFLAG progressState) - { - return progressState switch - { - ShObjIdl.TBPFLAG.TBPF_INDETERMINATE => TaskBarProgressState.Indeterminate, - ShObjIdl.TBPFLAG.TBPF_ERROR => TaskBarProgressState.Error, - ShObjIdl.TBPFLAG.TBPF_PAUSED => TaskBarProgressState.Paused, - ShObjIdl.TBPFLAG.TBPF_NORMAL => TaskBarProgressState.Normal, - _ => TaskBarProgressState.None, + TaskBarProgressState.Indeterminate => TBPFLAG.TBPF_INDETERMINATE, + TaskBarProgressState.Error => TBPFLAG.TBPF_ERROR, + TaskBarProgressState.Paused => TBPFLAG.TBPF_PAUSED, + TaskBarProgressState.Normal => TBPFLAG.TBPF_NORMAL, + _ => TBPFLAG.TBPF_NOPROGRESS, }; } } diff --git a/src/Wpf.Ui/Interop/User32.cs b/src/Wpf.Ui/Interop/User32.cs deleted file mode 100644 index 289c816da..000000000 --- a/src/Wpf.Ui/Interop/User32.cs +++ /dev/null @@ -1,1605 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. - - NOTE: - I split unmanaged code stuff into the NativeMethods library. - If you have suggestions for the code below, please submit your changes there. - https://github.com/lepoco/nativemethods */ - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop; - -// ReSharper disable IdentifierTypo -// ReSharper disable InconsistentNaming -#pragma warning disable SA1300 // Element should begin with upper-case letter -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable SA1401 // Fields should be private -#pragma warning disable CA1060 // Move pinvokes to native methods class - -/// -/// USER procedure declarations, constant definitions and macros. -/// -internal static class User32 -{ - /// - /// SetWindowPos options - /// - [Flags] - public enum SWP - { - ASYNCWINDOWPOS = 0x4000, - DEFERERASE = 0x2000, - DRAWFRAME = 0x0020, - FRAMECHANGED = DRAWFRAME, - HIDEWINDOW = 0x0080, - NOACTIVATE = 0x0010, - NOCOPYBITS = 0x0100, - NOMOVE = 0x0002, - NOOWNERZORDER = 0x0200, - NOREDRAW = 0x0008, - NOREPOSITION = NOOWNERZORDER, - NOSENDCHANGING = 0x0400, - NOSIZE = 0x0001, - NOZORDER = 0x0004, - SHOWWINDOW = 0x0040, - } - - /// - /// EnableMenuItem uEnable values, MF_* - /// - [Flags] - public enum MF : uint - { - /// - /// Possible return value for EnableMenuItem - /// - DOES_NOT_EXIST = unchecked((uint)-1), - ENABLED = 0, - BYCOMMAND = ENABLED, - GRAYED = 1, - DISABLED = 2, - } - - /// - /// Menu item element. - /// - public enum SC - { - SIZE = 0xF000, - MOVE = 0xF010, - MINIMIZE = 0xF020, - MAXIMIZE = 0xF030, - NEXTWINDOW = 0xF040, - PREVWINDOW = 0xF050, - CLOSE = 0xF060, - VSCROLL = 0xF070, - HSCROLL = 0xF080, - MOUSEMENU = 0xF090, - KEYMENU = 0xF100, - ARRANGE = 0xF110, - RESTORE = 0xF120, - TASKLIST = 0xF130, - SCREENSAVE = 0xF140, - HOTKEY = 0xF150, - DEFAULT = 0xF160, - MONITORPOWER = 0xF170, - CONTEXTHELP = 0xF180, - SEPARATOR = 0xF00F, - - /// - /// SCF_ISSECURE - /// - F_ISSECURE = 0x00000001, - ICON = MINIMIZE, - ZOOM = MAXIMIZE, - } - - /// - /// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes - /// - public enum WM_NCHITTEST - { - /// - /// Hit test returned error. - /// - HTERROR = unchecked(-2), - - /// - /// Hit test returned transparent. - /// - HTTRANSPARENT = unchecked(-1), - - /// - /// On the screen background or on a dividing line between windows. - /// - HTNOWHERE = 0, - - /// - /// In a client area. - /// - HTCLIENT = 1, - - /// - /// In a title bar. - /// - HTCAPTION = 2, - - /// - /// In a window menu or in a Close button in a child window. - /// - HTSYSMENU = 3, - - /// - /// In a size box (same as HTSIZE). - /// - HTGROWBOX = 4, - HTSIZE = HTGROWBOX, - - /// - /// In a menu. - /// - HTMENU = 5, - - /// - /// In a horizontal scroll bar. - /// - HTHSCROLL = 6, - - /// - /// In the vertical scroll bar. - /// - HTVSCROLL = 7, - - /// - /// In a Minimize button. - /// - HTMINBUTTON = 8, - - /// - /// In a Maximize button. - /// - HTMAXBUTTON = 9, - - // ZOOM = 9, - - /// - /// In the left border of a resizable window (the user can click the mouse to resize the window horizontally). - /// - HTLEFT = 10, - - /// - /// In the right border of a resizable window (the user can click the mouse to resize the window horizontally). - /// - HTRIGHT = 11, - - /// - /// In the upper-horizontal border of a window. - /// - HTTOP = 12, - - // From 10.0.22000.0\um\WinUser.h - HTTOPLEFT = 13, - HTTOPRIGHT = 14, - HTBOTTOM = 15, - HTBOTTOMLEFT = 16, - HTBOTTOMRIGHT = 17, - HTBORDER = 18, - HTREDUCE = HTMINBUTTON, - HTZOOM = HTMAXBUTTON, - HTSIZEFIRST = HTLEFT, - HTSIZELAST = HTBOTTOMRIGHT, - HTOBJECT = 19, - HTCLOSE = 20, - HTHELP = 21, - } - - /// - /// Window long flags. - /// - /// - [Flags] - public enum GWL - { - /// - /// Sets a new extended window style. - /// - GWL_EXSTYLE = -20, - - /// - /// Sets a new application instance handle. - /// - GWLP_HINSTANCE = -6, - - /// - /// Sets a new hWnd parent. - /// - GWLP_HWNDPARENT = -8, - - /// - /// Sets a new identifier of the child window. The window cannot be a top-level window. - /// - GWL_ID = -12, - - /// - /// Sets a new window style. - /// - GWL_STYLE = -16, - - /// - /// Sets the user data associated with the window. - /// This data is intended for use by the application that created the window. Its value is initially zero. - /// - GWL_USERDATA = -21, - - /// - /// Sets a new address for the window procedure. - /// You cannot change this attribute if the window does not belong to the same process as the calling thread. - /// - GWL_WNDPROC = -4, - - /// - /// Sets new extra information that is private to the application, such as handles or pointers. - /// - DWLP_USER = 0x8, - - /// - /// Sets the return value of a message processed in the dialog box procedure. - /// - DWLP_MSGRESULT = 0x0, - - /// - /// Sets the new address of the dialog box procedure. - /// - DWLP_DLGPROC = 0x4, - } - - /// - /// Window composition attributes. - /// - public enum WCA - { - WCA_UNDEFINED = 0, - WCA_NCRENDERING_ENABLED = 1, - WCA_NCRENDERING_POLICY = 2, - WCA_TRANSITIONS_FORCEDISABLED = 3, - WCA_ALLOW_NCPAINT = 4, - WCA_CAPTION_BUTTON_BOUNDS = 5, - WCA_NONCLIENT_RTL_LAYOUT = 6, - WCA_FORCE_ICONIC_REPRESENTATION = 7, - WCA_EXTENDED_FRAME_BOUNDS = 8, - WCA_HAS_ICONIC_BITMAP = 9, - WCA_THEME_ATTRIBUTES = 10, - WCA_NCRENDERING_EXILED = 11, - WCA_NCADORNMENTINFO = 12, - WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, - WCA_VIDEO_OVERLAY_ACTIVE = 14, - WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, - WCA_DISALLOW_PEEK = 16, - WCA_CLOAK = 17, - WCA_CLOAKED = 18, - WCA_ACCENT_POLICY = 19, - WCA_FREEZE_REPRESENTATION = 20, - WCA_EVER_UNCLOAKED = 21, - WCA_VISUAL_OWNER = 22, - WCA_HOLOGRAPHIC = 23, - WCA_EXCLUDED_FROM_DDA = 24, - WCA_PASSIVEUPDATEMODE = 25, - WCA_USEDARKMODECOLORS = 26, - WCA_CORNER_STYLE = 27, - WCA_PART_COLOR = 28, - WCA_DISABLE_MOVESIZE_FEEDBACK = 29, - WCA_LAST = 30, - } - - [Flags] - public enum ACCENT_FLAGS - { - DrawLeftBorder = 0x20, - DrawTopBorder = 0x40, - DrawRightBorder = 0x80, - DrawBottomBorder = 0x100, - DrawAllBorders = DrawLeftBorder | DrawTopBorder | DrawRightBorder | DrawBottomBorder, - } - - /// - /// DWM window accent state. - /// - public enum ACCENT_STATE - { - ACCENT_DISABLED = 0, - ACCENT_ENABLE_GRADIENT = 1, - ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, - ACCENT_ENABLE_BLURBEHIND = 3, - ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, - ACCENT_INVALID_STATE = 5, - } - - /// - /// WCA window accent policy. - /// - [StructLayout(LayoutKind.Sequential)] - public struct ACCENT_POLICY - { - public ACCENT_STATE nAccentState; - public uint nFlags; - public uint nColor; - public uint nAnimationId; - } - - [StructLayout(LayoutKind.Sequential)] - public struct WINCOMPATTRDATA - { - public WCA Attribute; - public IntPtr Data; - public int SizeOfData; - } - - /// - /// CS_* - /// - [Flags] - public enum CS : uint - { - VREDRAW = 0x0001, - HREDRAW = 0x0002, - DBLCLKS = 0x0008, - OWNDC = 0x0020, - CLASSDC = 0x0040, - PARENTDC = 0x0080, - NOCLOSE = 0x0200, - SAVEBITS = 0x0800, - BYTEALIGNCLIENT = 0x1000, - BYTEALIGNWINDOW = 0x2000, - GLOBALCLASS = 0x4000, - IME = 0x00010000, - DROPSHADOW = 0x00020000, - } - - /// - /// MSGFLT_*. New in Vista. Realiased in Windows 7. - /// - public enum MSGFLT - { - // Win7 versions of this enum: - - /// - /// Resets the window message filter for hWnd to the default. Any message allowed globally or process-wide will get through, but any message not included in those two categories, and which comes from a lower privileged process, will be blocked. - /// - RESET = 0, - - /// - /// Allows the message through the filter. This enables the message to be received by hWnd, regardless of the source of the message, even it comes from a lower privileged process. - /// - ALLOW = 1, - - /// - /// Blocks the message to be delivered to hWnd if it comes from a lower privileged process, unless the message is allowed process-wide by using the ChangeWindowMessageFilter function or globally. - /// - DISALLOW = 2, - - // Vista versions of this enum: - // ADD = 1, - // REMOVE = 2, - } - - /// - /// MSGFLTINFO. - /// - public enum MSGFLTINFO - { - NONE = 0, - ALREADYALLOWED_FORWND = 1, - ALREADYDISALLOWED_FORWND = 2, - ALLOWED_HIGHER = 3, - } - - /// - /// Win7 only. - /// - [StructLayout(LayoutKind.Sequential)] - public struct CHANGEFILTERSTRUCT - { - public uint cbSize; - public MSGFLTINFO ExtStatus; - } - - /// - /// Window message values, WM_* - /// - public enum WM - { - NULL = 0x0000, - CREATE = 0x0001, - DESTROY = 0x0002, - MOVE = 0x0003, - SIZE = 0x0005, - ACTIVATE = 0x0006, - SETFOCUS = 0x0007, - KILLFOCUS = 0x0008, - ENABLE = 0x000A, - SETREDRAW = 0x000B, - SETTEXT = 0x000C, - GETTEXT = 0x000D, - GETTEXTLENGTH = 0x000E, - PAINT = 0x000F, - CLOSE = 0x0010, - QUERYENDSESSION = 0x0011, - QUIT = 0x0012, - QUERYOPEN = 0x0013, - ERASEBKGND = 0x0014, - SYSCOLORCHANGE = 0x0015, - SHOWWINDOW = 0x0018, - CTLCOLOR = 0x0019, - WININICHANGE = 0x001A, - SETTINGCHANGE = WININICHANGE, - ACTIVATEAPP = 0x001C, - SETCURSOR = 0x0020, - MOUSEACTIVATE = 0x0021, - CHILDACTIVATE = 0x0022, - QUEUESYNC = 0x0023, - GETMINMAXINFO = 0x0024, - - WINDOWPOSCHANGING = 0x0046, - WINDOWPOSCHANGED = 0x0047, - - CONTEXTMENU = 0x007B, - STYLECHANGING = 0x007C, - STYLECHANGED = 0x007D, - DISPLAYCHANGE = 0x007E, - GETICON = 0x007F, - SETICON = 0x0080, - NCCREATE = 0x0081, - NCDESTROY = 0x0082, - NCCALCSIZE = 0x0083, - NCHITTEST = 0x0084, - NCPAINT = 0x0085, - NCACTIVATE = 0x0086, - GETDLGCODE = 0x0087, - SYNCPAINT = 0x0088, - NCMOUSEMOVE = 0x00A0, - NCLBUTTONDOWN = 0x00A1, - NCLBUTTONUP = 0x00A2, - NCLBUTTONDBLCLK = 0x00A3, - NCRBUTTONDOWN = 0x00A4, - NCRBUTTONUP = 0x00A5, - NCRBUTTONDBLCLK = 0x00A6, - NCMBUTTONDOWN = 0x00A7, - NCMBUTTONUP = 0x00A8, - NCMBUTTONDBLCLK = 0x00A9, - - SYSKEYDOWN = 0x0104, - SYSKEYUP = 0x0105, - SYSCHAR = 0x0106, - SYSDEADCHAR = 0x0107, - COMMAND = 0x0111, - SYSCOMMAND = 0x0112, - - MOUSEMOVE = 0x0200, - LBUTTONDOWN = 0x0201, - LBUTTONUP = 0x0202, - LBUTTONDBLCLK = 0x0203, - RBUTTONDOWN = 0x0204, - RBUTTONUP = 0x0205, - RBUTTONDBLCLK = 0x0206, - MBUTTONDOWN = 0x0207, - MBUTTONUP = 0x0208, - MBUTTONDBLCLK = 0x0209, - MOUSEWHEEL = 0x020A, - XBUTTONDOWN = 0x020B, - XBUTTONUP = 0x020C, - XBUTTONDBLCLK = 0x020D, - MOUSEHWHEEL = 0x020E, - PARENTNOTIFY = 0x0210, - - CAPTURECHANGED = 0x0215, - POWERBROADCAST = 0x0218, - DEVICECHANGE = 0x0219, - - ENTERSIZEMOVE = 0x0231, - EXITSIZEMOVE = 0x0232, - - IME_SETCONTEXT = 0x0281, - IME_NOTIFY = 0x0282, - IME_CONTROL = 0x0283, - IME_COMPOSITIONFULL = 0x0284, - IME_SELECT = 0x0285, - IME_CHAR = 0x0286, - IME_REQUEST = 0x0288, - IME_KEYDOWN = 0x0290, - IME_KEYUP = 0x0291, - - NCMOUSELEAVE = 0x02A2, - - TABLET_DEFBASE = 0x02C0, - - // WM_TABLET_MAXOFFSET = 0x20, - TABLET_ADDED = TABLET_DEFBASE + 8, - TABLET_DELETED = TABLET_DEFBASE + 9, - TABLET_FLICK = TABLET_DEFBASE + 11, - TABLET_QUERYSYSTEMGESTURESTATUS = TABLET_DEFBASE + 12, - - CUT = 0x0300, - COPY = 0x0301, - PASTE = 0x0302, - CLEAR = 0x0303, - UNDO = 0x0304, - RENDERFORMAT = 0x0305, - RENDERALLFORMATS = 0x0306, - DESTROYCLIPBOARD = 0x0307, - DRAWCLIPBOARD = 0x0308, - PAINTCLIPBOARD = 0x0309, - VSCROLLCLIPBOARD = 0x030A, - SIZECLIPBOARD = 0x030B, - ASKCBFORMATNAME = 0x030C, - CHANGECBCHAIN = 0x030D, - HSCROLLCLIPBOARD = 0x030E, - QUERYNEWPALETTE = 0x030F, - PALETTEISCHANGING = 0x0310, - PALETTECHANGED = 0x0311, - HOTKEY = 0x0312, - PRINT = 0x0317, - PRINTCLIENT = 0x0318, - APPCOMMAND = 0x0319, - THEMECHANGED = 0x031A, - - DWMCOMPOSITIONCHANGED = 0x031E, - DWMNCRENDERINGCHANGED = 0x031F, - DWMCOLORIZATIONCOLORCHANGED = 0x0320, - DWMWINDOWMAXIMIZEDCHANGE = 0x0321, - - GETTITLEBARINFOEX = 0x033F, - - DWMSENDICONICTHUMBNAIL = 0x0323, - DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, - - USER = 0x0400, - - /// - /// This is the hard-coded message value used by WinForms for Shell_NotifyIcon. - /// It's relatively safe to reuse. - /// - TRAYMOUSEMESSAGE = 0x800, // WM_USER + 1024 - APP = 0x8000, - } - - /// - /// WindowStyle values, WS_* - /// - [Flags] - public enum WS : long - { - OVERLAPPED = 0x00000000, - POPUP = 0x80000000, - CHILD = 0x40000000, - MINIMIZE = 0x20000000, - VISIBLE = 0x10000000, - DISABLED = 0x08000000, - CLIPSIBLINGS = 0x04000000, - CLIPCHILDREN = 0x02000000, - MAXIMIZE = 0x01000000, - BORDER = 0x00800000, - DLGFRAME = 0x00400000, - VSCROLL = 0x00200000, - HSCROLL = 0x00100000, - SYSMENU = 0x00080000, - THICKFRAME = 0x00040000, - GROUP = 0x00020000, - TABSTOP = 0x00010000, - - MINIMIZEBOX = GROUP, - MAXIMIZEBOX = TABSTOP, - - CAPTION = BORDER | DLGFRAME, - TILED = OVERLAPPED, - ICONIC = MINIMIZE, - SIZEBOX = THICKFRAME, - - OVERLAPPEDWINDOW = OVERLAPPED | CAPTION | SYSMENU | THICKFRAME | MINIMIZEBOX | MAXIMIZEBOX, - TILEDWINDOW = OVERLAPPEDWINDOW, - - POPUPWINDOW = POPUP | BORDER | SYSMENU, - CHILDWINDOW = CHILD, - } - - /// - /// Window style extended values, WS_EX_* - /// - [Flags] - public enum WS_EX : long - { - NONE = 0x00000000, - DLGMODALFRAME = 0x00000001, - NOPARENTNOTIFY = 0x00000004, - TOPMOST = 0x00000008, - ACCEPTFILES = 0x00000010, - TRANSPARENT = 0x00000020, - MDICHILD = 0x00000040, - TOOLWINDOW = 0x00000080, - WINDOWEDGE = 0x00000100, - CLIENTEDGE = 0x00000200, - CONTEXTHELP = 0x00000400, - RIGHT = 0x00001000, - LEFT = NONE, - RTLREADING = 0x00002000, - LTRREADING = NONE, - LEFTSCROLLBAR = 0x00004000, - RIGHTSCROLLBAR = NONE, - CONTROLPARENT = 0x00010000, - STATICEDGE = 0x00020000, - APPWINDOW = 0x00040000, - LAYERED = 0x00080000, - NOINHERITLAYOUT = 0x00100000, // Disable inheritence of mirroring by children - LAYOUTRTL = 0x00400000, // Right to left mirroring - COMPOSITED = 0x02000000, - NOACTIVATE = 0x08000000, - OVERLAPPEDWINDOW = WINDOWEDGE | CLIENTEDGE, - PALETTEWINDOW = WINDOWEDGE | TOOLWINDOW | TOPMOST, - } - - /// - /// SystemMetrics. SM_* - /// - public enum SM - { - CXSCREEN = 0, - CYSCREEN = 1, - CXVSCROLL = 2, - CYHSCROLL = 3, - CYCAPTION = 4, - CXBORDER = 5, - CYBORDER = 6, - CXFIXEDFRAME = 7, - CYFIXEDFRAME = 8, - CYVTHUMB = 9, - CXHTHUMB = 10, - CXICON = 11, - CYICON = 12, - CXCURSOR = 13, - CYCURSOR = 14, - CYMENU = 15, - CXFULLSCREEN = 16, - CYFULLSCREEN = 17, - CYKANJIWINDOW = 18, - MOUSEPRESENT = 19, - CYVSCROLL = 20, - CXHSCROLL = 21, - DEBUG = 22, - SWAPBUTTON = 23, - CXMIN = 28, - CYMIN = 29, - CXSIZE = 30, - CYSIZE = 31, - CXFRAME = 32, - CXSIZEFRAME = CXFRAME, - CYFRAME = 33, - CYSIZEFRAME = CYFRAME, - CXMINTRACK = 34, - CYMINTRACK = 35, - CXDOUBLECLK = 36, - CYDOUBLECLK = 37, - CXICONSPACING = 38, - CYICONSPACING = 39, - MENUDROPALIGNMENT = 40, - PENWINDOWS = 41, - DBCSENABLED = 42, - CMOUSEBUTTONS = 43, - SECURE = 44, - CXEDGE = 45, - CYEDGE = 46, - CXMINSPACING = 47, - CYMINSPACING = 48, - CXSMICON = 49, - CYSMICON = 50, - CYSMCAPTION = 51, - CXSMSIZE = 52, - CYSMSIZE = 53, - CXMENUSIZE = 54, - CYMENUSIZE = 55, - ARRANGE = 56, - CXMINIMIZED = 57, - CYMINIMIZED = 58, - CXMAXTRACK = 59, - CYMAXTRACK = 60, - CXMAXIMIZED = 61, - CYMAXIMIZED = 62, - NETWORK = 63, - CLEANBOOT = 67, - CXDRAG = 68, - CYDRAG = 69, - SHOWSOUNDS = 70, - CXMENUCHECK = 71, - CYMENUCHECK = 72, - SLOWMACHINE = 73, - MIDEASTENABLED = 74, - MOUSEWHEELPRESENT = 75, - XVIRTUALSCREEN = 76, - YVIRTUALSCREEN = 77, - CXVIRTUALSCREEN = 78, - CYVIRTUALSCREEN = 79, - CMONITORS = 80, - SAMEDISPLAYFORMAT = 81, - IMMENABLED = 82, - CXFOCUSBORDER = 83, - CYFOCUSBORDER = 84, - TABLETPC = 86, - MEDIACENTER = 87, - CXPADDEDBORDER = 92, - REMOTESESSION = 0x1000, - REMOTECONTROL = 0x2001, - } - - /// - /// ShowWindow options - /// - public enum SW - { - HIDE = 0, - SHOWNORMAL = 1, - NORMAL = SHOWNORMAL, - SHOWMINIMIZED = 2, - SHOWMAXIMIZED = 3, - MAXIMIZE = SHOWMAXIMIZED, - SHOWNOACTIVATE = 4, - SHOW = 5, - MINIMIZE = 6, - SHOWMINNOACTIVE = 7, - SHOWNA = 8, - RESTORE = 9, - SHOWDEFAULT = 10, - FORCEMINIMIZE = 11, - } - - [StructLayout(LayoutKind.Sequential)] - public class WINDOWPLACEMENT - { - public int length = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); - public int flags; - public SW showCmd; - public WinDef.POINT ptMinPosition; - public WinDef.POINT ptMaxPosition; - public WinDef.RECT rcNormalPosition; - } - - /// - /// Contains window class information. It is used with the and GetClassInfoEx functions. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct WNDCLASSEX - { - /// - /// The size, in bytes, of this structure. Set this member to sizeof(WNDCLASSEX). Be sure to set this member before calling the GetClassInfoEx function. - /// - public int cbSize; - - /// - /// The class style(s). This member can be any combination of the Class Styles. - /// - public CS style; - - /// - /// A pointer to the window procedure. You must use the CallWindowProc function to call the window procedure. For more information, see WindowProc. - /// - public WndProc lpfnWndProc; - - /// - /// The number of extra bytes to allocate following the window-class structure. The system initializes the bytes to zero. - /// - public int cbClsExtra; - - /// - /// The number of extra bytes to allocate following the window instance. The system initializes the bytes to zero. If an application uses WNDCLASSEX to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA. - /// - public int cbWndExtra; - - /// - /// A handle to the instance that contains the window procedure for the class. - /// - public IntPtr hInstance; - - /// - /// A handle to the class icon. This member must be a handle to an icon resource. If this member is NULL, the system provides a default icon. - /// - public IntPtr hIcon; - - /// - /// A handle to the class cursor. This member must be a handle to a cursor resource. If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window. - /// - public IntPtr hCursor; - - /// - /// A handle to the class background brush. This member can be a handle to the brush to be used for painting the background, or it can be a color value. - /// - public IntPtr hbrBackground; - - /// - /// Pointer to a null-terminated character string that specifies the resource name of the class menu, as the name appears in the resource file. If you use an integer to identify the menu, use the MAKEINTRESOURCE macro. If this member is NULL, windows belonging to this class have no default menu. - /// - [MarshalAs(UnmanagedType.LPWStr)] - public string lpszMenuName; - - /// - /// A pointer to a null-terminated string or is an atom. If this parameter is an atom, it must be a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpszClassName; the high-order word must be zero. - /// - [MarshalAs(UnmanagedType.LPWStr)] - public string lpszClassName; - - /// - /// A handle to a small icon that is associated with the window class. If this member is NULL, the system searches the icon resource specified by the hIcon member for an icon of the appropriate size to use as the small icon. - /// - public IntPtr hIconSm; - } - - /// - /// Delegate declaration that matches native WndProc signatures. - /// - public delegate IntPtr WndProc(IntPtr hWnd, WM uMsg, IntPtr wParam, IntPtr lParam); - - /// - /// Delegate declaration that matches native WndProc signatures. - /// - public delegate IntPtr WndProcHook(IntPtr hWnd, WM uMsg, IntPtr wParam, IntPtr lParam, ref bool handled); - - /// - /// Delegate declaration that matches managed WndProc signatures. - /// - public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); - - /// - /// The ReleaseDC function releases a device context (DC), freeing it for use by other applications. - /// The effect of the ReleaseDC function depends on the type of DC. It frees only common and window DCs. It has no effect on class or private DCs. - /// - /// A handle to the window whose DC is to be released. - /// A handle to the DC to be released. - /// The return value indicates whether the DC was released. If the DC was released, the return value is 1. If the DC was not released, the return value is zero. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern int ReleaseDC([In] IntPtr hWnd, [In] IntPtr hDC); - - /// - /// Calculates the required size of the window rectangle, based on the desired size of the client rectangle. - /// The window rectangle can then be passed to the CreateWindowEx function to create a window whose client area is the desired size. - /// - /// A pointer to a RECT structure that contains the coordinates of the top-left and bottom-right corners of the desired client area. - /// The window style of the window whose required size is to be calculated. Note that you cannot specify the WS_OVERLAPPED style. - /// Indicates whether the window has a menu. - /// The extended window style of the window whose required size is to be calculated. - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, CharSet = CharSet.Auto, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool AdjustWindowRectEx( - [In] ref Rect lpRect, - [In] WS dwStyle, - [In] [MarshalAs(UnmanagedType.Bool)] bool bMenu, - [In] WS_EX dwExStyle - ); - - /// - /// [Using the ChangeWindowMessageFilter function is not recommended, as it has process-wide scope. Instead, use the ChangeWindowMessageFilterEx function to control access to specific windows as needed. ChangeWindowMessageFilter may not be supported in future versions of Windows. - /// Adds or removes a message from the User Interface Privilege Isolation(UIPI) message filter. - /// - /// The message to add to or remove from the filter. - /// The action to be performed. One of the following values. - /// if successful; otherwise, . To get extended error information, call . - [DllImport(Libraries.User32, CharSet = CharSet.Auto, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ChangeWindowMessageFilter([In] WM message, [In] MSGFLT dwFlag); - - /// - /// Modifies the User Interface Privilege Isolation (UIPI) message filter for a specified window. - /// - /// A handle to the window whose UIPI message filter is to be modified. - /// The message that the message filter allows through or blocks. - /// The action to be performed. - /// Optional pointer to a structure. - /// If the function succeeds, it returns ; otherwise, it returns . To get extended error information, call . - [DllImport(Libraries.User32, CharSet = CharSet.Auto, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ChangeWindowMessageFilterEx( - [In] IntPtr hWnd, - [In] WM message, - [In] MSGFLT action, - [In, Out, Optional] ref CHANGEFILTERSTRUCT pChangeFilterStruct - ); - - /// - /// Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message. - /// Unicode declaration for - /// - /// A handle to the window whose window procedure is to receive the message. - /// The message to be posted. - /// Additional message-specific information. - /// Additional message-specific information.~ - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool PostMessageW( - [In, Optional] IntPtr hWnd, - [In] WM Msg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message. - /// ANSI declaration for - /// - /// A handle to the window whose window procedure is to receive the message. - /// The message to be posted. - /// Additional message-specific information. - /// Additional message-specific information.~ - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool PostMessageA( - [In, Optional] IntPtr hWnd, - [In] WM Msg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message. - /// - /// A handle to the window whose window procedure is to receive the message. - /// The message to be posted. - /// Additional message-specific information. - /// Additional message-specific information.~ - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool PostMessage( - [In, Optional] IntPtr hWnd, - [In] WM Msg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message. - /// - /// A handle to the window whose window procedure will receive the message. - /// The message to be sent. - /// Additional message-specific information. - /// Additional message-specific information.~ - /// The return value specifies the result of the message processing; it depends on the message sent. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern int SendMessage( - [In] IntPtr hWnd, - [In] WM wMsg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Creates an overlapped, pop-up, or child window with an extended window style; otherwise, - /// this function is identical to the CreateWindow function. For more information about - /// creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow. - /// - /// The extended window style of the window being created. - /// A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. - /// The window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar. - /// The style of the window being created. This parameter can be a combination of the window style values, plus the control styles indicated in the Remarks section. - /// The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates. - /// The initial vertical position of the window. For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, in screen coordinates. - /// The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT. - /// The height, in device units, of the window. For overlapped windows, nHeight is the window's height, in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, the system ignores nHeight. - /// A handle to the parent or owner window of the window being created. To create a child window or an owned window, supply a valid window handle. This parameter is optional for pop-up windows. - /// A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. - /// A handle to the instance of the module to be associated with the window. - /// Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns. - /// If the function succeeds, the return value is a handle to the new window. - [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern IntPtr CreateWindowExW( - [In] WS_EX dwExStyle, - [In, Optional] [MarshalAs(UnmanagedType.LPWStr)] string lpClassName, - [In, Optional] [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, - [In] WS dwStyle, - [In] int x, - [In] int y, - [In] int nWidth, - [In] int nHeight, - [In, Optional] IntPtr hWndParent, - [In, Optional] IntPtr hMenu, - [In, Optional] IntPtr hInstance, - [In, Optional] IntPtr lpParam - ); - - /// - /// Creates an overlapped, pop-up, or child window with an extended window style; otherwise, - /// this function is identical to the CreateWindow function. For more information about - /// creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow. - /// - /// The extended window style of the window being created. - /// A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. - /// The window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar. - /// The style of the window being created. This parameter can be a combination of the window style values, plus the control styles indicated in the Remarks section. - /// The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates. - /// The initial vertical position of the window. For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, in screen coordinates. - /// The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT. - /// The height, in device units, of the window. For overlapped windows, nHeight is the window's height, in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, the system ignores nHeight. - /// A handle to the parent or owner window of the window being created. To create a child window or an owned window, supply a valid window handle. This parameter is optional for pop-up windows. - /// A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. - /// A handle to the instance of the module to be associated with the window. - /// Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns. - /// If the function succeeds, the return value is a handle to the new window. - public static IntPtr CreateWindowEx( - [In] WS_EX dwExStyle, - [In] string lpClassName, - [In] string lpWindowName, - [In] WS dwStyle, - [In] int x, - [In] int y, - [In] int nWidth, - [In] int nHeight, - [In, Optional] IntPtr hWndParent, - [In, Optional] IntPtr hMenu, - [In, Optional] IntPtr hInstance, - [In, Optional] IntPtr lpParam - ) - { - IntPtr ret = CreateWindowExW( - dwExStyle, - lpClassName, - lpWindowName, - dwStyle, - x, - y, - nWidth, - nHeight, - hWndParent, - hMenu, - hInstance, - lpParam - ); - - if (ret == IntPtr.Zero) - { - // HRESULT.ThrowLastError(); - throw new Exception("Unable to create a window"); - } - - return ret; - } - - /// - /// Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function. - /// Unicode declaration for - /// - /// A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function. - /// If the function succeeds, the return value is a class atom that uniquely identifies the class being registered. - [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern short RegisterClassExW([In] ref WNDCLASSEX lpwcx); - - /// - /// Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function. - /// ANSI declaration for - /// - /// A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function. - /// If the function succeeds, the return value is a class atom that uniquely identifies the class being registered. - [DllImport(Libraries.User32, SetLastError = true)] - public static extern short RegisterClassExA([In] ref WNDCLASSEX lpwcx); - - /// - /// Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function. - /// - /// A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function. - /// If the function succeeds, the return value is a class atom that uniquely identifies the class being registered. - [DllImport(Libraries.User32, SetLastError = true)] - public static extern short RegisterClassEx([In] ref WNDCLASSEX lpwcx); - - /// - /// Calls the default window procedure to provide default processing for any window messages that an application does not process. - /// This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure. - /// Unicode declaration for - /// - /// A handle to the window procedure that received the message. - /// The message. - /// Additional message information. The content of this parameter depends on the value of the Msg parameter. - /// Additional message information. The content of this parameter depends on the value of the Msg parameter.~ - /// The return value is the result of the message processing and depends on the message. - [DllImport(Libraries.User32, CharSet = CharSet.Unicode)] - public static extern IntPtr DefWindowProcW( - [In] IntPtr hWnd, - [In] WM Msg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Calls the default window procedure to provide default processing for any window messages that an application does not process. - /// This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure. - /// ANSI declaration for - /// - /// A handle to the window procedure that received the message. - /// The message. - /// Additional message information. The content of this parameter depends on the value of the Msg parameter. - /// Additional message information. The content of this parameter depends on the value of the Msg parameter.~ - /// The return value is the result of the message processing and depends on the message. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr DefWindowProcA( - [In] IntPtr hWnd, - [In] WM Msg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Calls the default window procedure to provide default processing for any window messages that an application does not process. - /// This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure. - /// - /// A handle to the window procedure that received the message. - /// The message. - /// Additional message information. The content of this parameter depends on the value of the Msg parameter. - /// Additional message information. The content of this parameter depends on the value of the Msg parameter.~ - /// The return value is the result of the message processing and depends on the message. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr DefWindowProc( - [In] IntPtr hWnd, - [In] WM Msg, - [In] IntPtr wParam, - [In] IntPtr lParam - ); - - /// - /// Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory. - /// If you are retrieving a pointer or a handle, this function has been superseded by the function. - /// Unicode declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Unicode)] - public static extern long GetWindowLongW([In] IntPtr hWnd, [In] int nIndex); - - /// - /// Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory. - /// If you are retrieving a pointer or a handle, this function has been superseded by the function. - /// ANSI declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long GetWindowLongA([In] IntPtr hWnd, [In] int nIndex); - - /// - /// Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory. - /// If you are retrieving a pointer or a handle, this function has been superseded by the function. - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long GetWindowLong([In] IntPtr hWnd, [In] int nIndex); - - /// - /// Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory. - /// If you are retrieving a pointer or a handle, this function has been superseded by the function. - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long GetWindowLong([In] IntPtr hWnd, [In] GWL nIndex); - - /// - /// Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory. - /// Unicode declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr GetWindowLongPtrW([In] IntPtr hWnd, [In] int nIndex); - - /// - /// Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory. - /// ANSI declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr GetWindowLongPtrA([In] IntPtr hWnd, [In] int nIndex); - - /// - /// Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory. - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be retrieved. - /// If the function succeeds, the return value is the requested value. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr GetWindowLongPtr([In] IntPtr hWnd, [In] int nIndex); - - /// - /// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory. - /// Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function. - /// Unicode declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified 32-bit integer. - [DllImport(Libraries.User32, CharSet = CharSet.Unicode)] - public static extern long SetWindowLongW([In] IntPtr hWnd, [In] int nIndex, [In] long dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory. - /// Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function. - /// ANSI declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified 32-bit integer. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long SetWindowLongA([In] IntPtr hWnd, [In] int nIndex, [In] long dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory. - /// Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function. - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified 32-bit integer. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long SetWindowLong([In] IntPtr hWnd, [In] int nIndex, [In] long dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory. - /// Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function. - /// ANSI declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified 32-bit integer. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long SetWindowLong([In] IntPtr hWnd, [In] GWL nIndex, [In] long dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory. - /// Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function. - /// ANSI declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer. - /// New window style. - /// If the function succeeds, the return value is the previous value of the specified 32-bit integer. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern long SetWindowLong([In] IntPtr hWnd, [In] GWL nIndex, [In] WS dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory. - /// Unicode declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified offset. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr SetWindowLongPtrW([In] IntPtr hWnd, [In] int nIndex, [In] IntPtr dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory. - /// ANSI declaration for - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified offset. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr SetWindowLongPtrA([In] IntPtr hWnd, [In] int nIndex, [In] IntPtr dwNewLong); - - /// - /// Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory. - /// - /// A handle to the window and, indirectly, the class to which the window belongs. - /// The zero-based offset to the value to be set. - /// The replacement value. - /// If the function succeeds, the return value is the previous value of the specified offset. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern IntPtr SetWindowLongPtr([In] IntPtr hWnd, [In] int nIndex, [In] IntPtr dwNewLong); - - /// - /// Changes an attribute of the specified window. - /// - [DllImport(Libraries.User32, CharSet = CharSet.Auto, SetLastError = true)] - public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - - /// - /// Destroys an icon and frees any memory the icon occupied. - /// - /// A handle to the icon to be destroyed. The icon must not be in use. - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DestroyIcon([In] IntPtr handle); - - /// - /// Determines whether the specified window handle identifies an existing window. - /// - /// A handle to the window to be tested. - /// If the window handle identifies an existing window, the return value is nonzero. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsWindow([In] IntPtr hWnd); - - /// - /// Destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it. - /// - /// A handle to the window to be destroyed. - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, CharSet = CharSet.Auto, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DestroyWindow([In] IntPtr hWnd); - - /// - /// Retrieves the show state and the restored, minimized, and maximized positions of the specified window. - /// - /// A handle to the window. - /// A pointer to the structure that receives the show state and position information. Before calling GetWindowPlacement, set the length member to sizeof(WINDOWPLACEMENT). GetWindowPlacement fails if lpwndpl-> length is not set correctly. - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetWindowPlacement([In] IntPtr hWnd, [In] WINDOWPLACEMENT lpwndpl); - - /// - /// Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen. - /// - /// A handle to the window. - /// A pointer to a RECT structure that receives the screen coordinates of the upper-left and lower-right corners of the window. - /// If the function succeeds, the return value is nonzero. - [DllImport(Libraries.User32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetWindowRect([In] IntPtr hWnd, [Out] out Rect lpRect); - - /// - /// Determines the visibility state of the specified window. - /// - /// A handle to the window to be tested. - /// If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero. - [DllImport(Libraries.User32)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsWindowVisible([In] IntPtr hWnd); - - /// - /// Determines whether the specified window is enabled for mouse and keyboard input. - /// - /// A handle to the window to be tested. - /// If the window is enabled, the return value is nonzero. - [DllImport(Libraries.User32, ExactSpelling = true)] - internal static extern bool IsWindowEnabled(IntPtr hWnd); - - /// - /// The MonitorFromWindow function retrieves a handle to the display monitor that has the largest area of intersection with the bounding rectangle of a specified window. - /// - /// A handle to the window of interest. - /// Determines the function's return value if the window does not intersect any display monitor. - /// If the window intersects one or more display monitor rectangles, the return value is an HMONITOR handle to the display monitor that has the largest area of intersection with the window. - [DllImport(Libraries.User32)] - public static extern IntPtr MonitorFromWindow(IntPtr hWnd, uint dwFlags); - - /// - /// Retrieves the specified system metric or system configuration setting. - /// Note that all dimensions retrieved by GetSystemMetrics are in pixels. - /// - /// The system metric or configuration setting to be retrieved. This parameter can be one of the values. - /// Note that all SM_CX* values are widths and all SM_CY* values are heights. Also note that all settings designed to return Boolean data represent as any nonzero value, and as a zero value. - /// If the function succeeds, the return value is the requested system metric or configuration setting. - [DllImport(Libraries.User32)] - public static extern int GetSystemMetrics([In] SM nIndex); - - /// - /// Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages. - /// Unicode declaration for - /// - /// The message to be registered. - /// If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF. - [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern uint RegisterWindowMessageW([MarshalAs(UnmanagedType.LPWStr)] string lpString); - - /// - /// Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages. - /// ANSI declaration for - /// - /// The message to be registered. - /// If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF. - [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Auto)] - public static extern uint RegisterWindowMessageA([MarshalAs(UnmanagedType.LPWStr)] string lpString); - - /// - /// Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages. - /// - /// The message to be registered. - /// If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF. - [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Auto)] - public static extern uint RegisterWindowMessage([MarshalAs(UnmanagedType.LPWStr)] string lpString); - - /// - /// Activates a window. The window must be attached to the calling thread's message queue. - /// - /// A handle to the top-level window to be activated. - /// If the function succeeds, the return value is the handle to the window that was previously active. - [DllImport(Libraries.User32, SetLastError = true)] - public static extern IntPtr SetActiveWindow(IntPtr hWnd); - - /// - /// Brings the thread that created the specified window into the foreground and activates the window. - /// Keyboard input is directed to the window, and various visual cues are changed for the user. - /// The system assigns a slightly higher priority to the thread that created the foreground window than it does to other threads. - /// - /// A handle to the window that should be activated and brought to the foreground. - /// If the window was brought to the foreground, the return value is nonzero. - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SetForegroundWindow(IntPtr hWnd); - - /// - /// Retrieves the position of the mouse cursor, in screen coordinates. - /// - /// A pointer to a structure that receives the screen coordinates of the cursor. - /// Returns nonzero if successful or zero otherwise. To get extended error information, call . - [DllImport(Libraries.User32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetCursorPos([Out] out WinDef.POINT lpPoint); - - [DllImport(Libraries.User32, SetLastError = true)] - public static extern bool GetCursorPos(out System.Drawing.Point lpPoint); - - [DllImport(Libraries.User32)] - public static extern bool UnionRect(out WinDef.RECT rcDst, ref WinDef.RECT rc1, ref WinDef.RECT rc2); - - [DllImport(Libraries.User32, SetLastError = true)] - public static extern bool IntersectRect(ref WinDef.RECT rcDest, ref WinDef.RECT rc1, ref WinDef.RECT rc2); - - [DllImport(Libraries.User32)] - public static extern IntPtr GetShellWindow(); - - [DllImport(Libraries.User32, CharSet = CharSet.Unicode)] - public static extern int MapVirtualKey(int nVirtKey, int nMapType); - - [DllImport(Libraries.User32)] - public static extern int GetSysColor(int nIndex); - - [DllImport(Libraries.User32)] - public static extern IntPtr GetSystemMenu( - [In] IntPtr hWnd, - [In] [MarshalAs(UnmanagedType.Bool)] bool bRevert - ); - - [DllImport(Libraries.User32, EntryPoint = "EnableMenuItem")] - private static extern int _EnableMenuItem([In] IntPtr hMenu, [In] SC uIDEnableItem, [In] MF uEnable); - - /// - /// Enables, disables, or grays the specified menu item. - /// - /// A handle to the menu. - /// The menu item to be enabled, disabled, or grayed, as determined by the uEnable parameter. - /// Controls the interpretation of the uIDEnableItem parameter and indicate whether the menu item is enabled, disabled, or grayed. - /// The return value specifies the previous state of the menu item (it is either MF_DISABLED, MF_ENABLED, or MF_GRAYED). If the menu item does not exist, the return value is -1 (). - public static MF EnableMenuItem([In] IntPtr hMenu, [In] SC uIDEnableItem, [In] MF uEnable) - { - // Returns the previous state of the menu item, or -1 if the menu item does not exist. - int iRet = _EnableMenuItem(hMenu, uIDEnableItem, uEnable); - return (MF)iRet; - } - - [DllImport(Libraries.User32, EntryPoint = "SetWindowRgn", SetLastError = true)] - private static extern int _SetWindowRgn( - [In] IntPtr hWnd, - [In] IntPtr hRgn, - [In] [MarshalAs(UnmanagedType.Bool)] bool bRedraw - ); - - /// - /// The SetWindowRgn function sets the window region of a window. The window region determines the area within the window where the system permits drawing. The system does not display any portion of a window that lies outside of the window region. - /// - /// A handle to the window whose window region is to be set. - /// A handle to a region. The function sets the window region of the window to this region. - /// Specifies whether the system redraws the window after setting the window region. If bRedraw is , the system does so; otherwise, it does not. - /// Native method returned HRESULT. - public static void SetWindowRgn([In] IntPtr hWnd, [In] IntPtr hRgn, [In] bool bRedraw) - { - var err = _SetWindowRgn(hWnd, hRgn, bRedraw); - - if (err == 0) - { - throw new Win32Exception(); - } - } - - [DllImport(Libraries.User32, EntryPoint = "SetWindowPos", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool _SetWindowPos( - [In] IntPtr hWnd, - [In, Optional] IntPtr hWndInsertAfter, - [In] int x, - [In] int y, - [In] int cx, - [In] int cy, - [In] SWP uFlags - ); - - /// - /// Changes the size, position, and Z order of a child, pop-up, or top-level window. These windows are ordered according to their appearance on the screen. The topmost window receives the highest rank and is the first window in the Z order. - /// - /// A handle to the window. - /// A handle to the window to precede the positioned window in the Z order. - /// The new position of the left side of the window, in client coordinates. - /// The new position of the top of the window, in client coordinates. - /// The new width of the window, in pixels. - /// The new height of the window, in pixels. - /// The window sizing and positioning flags. - /// If the function succeeds, the return value is nonzero. - public static bool SetWindowPos( - [In] IntPtr hWnd, - [In, Optional] IntPtr hWndInsertAfter, - [In] int x, - [In] int y, - [In] int cx, - [In] int cy, - [In] SWP uFlags - ) - { - if (!_SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags)) - { - // If this fails it's never worth taking down the process. Let the caller deal with the error if they want. - return false; - } - - return true; - } - - /// - /// Sets the process-default DPI awareness to system-DPI awareness. This is equivalent to calling SetProcessDpiAwarenessContext with a DPI_AWARENESS_CONTEXT value of DPI_AWARENESS_CONTEXT_SYSTEM_AWARE. - /// - [DllImport(Libraries.User32)] - public static extern void SetProcessDPIAware(); - - /// - /// Sets various information regarding DWM window attributes. - /// - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern int SetWindowCompositionAttribute( - [In] IntPtr hWnd, - [In, Out] ref WINCOMPATTRDATA data - ); - - /// - /// Sets various information regarding DWM window attributes. - /// - [DllImport(Libraries.User32, CharSet = CharSet.Auto)] - public static extern int GetWindowCompositionAttribute( - [In] IntPtr hWnd, - [In, Out] ref WINCOMPATTRDATA data - ); - - /// - /// Returns the dots per inch (dpi) value for the specified window. - /// - /// The window that you want to get information about. - /// The DPI for the window, which depends on the DPI_AWARENESS of the window. - [DllImport(Libraries.User32, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)] - public static extern uint GetDpiForWindow([In] IntPtr hWnd); - - /// - /// Returns the dots per inch (dpi) value for the specified window. - /// - /// The window that you want to get information about. - /// The DPI for the window, which depends on the DPI_AWARENESS of the window. - [DllImport(Libraries.User32, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)] - public static extern uint GetDpiForWindow([In] HandleRef hwnd); -} - -#pragma warning restore SA1300 // Element should begin with upper-case letter -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter -#pragma warning restore SA1401 // Fields should be private -#pragma warning restore CA1060 // Move pinvokes to native methods class diff --git a/src/Wpf.Ui/Interop/UxTheme.cs b/src/Wpf.Ui/Interop/UxTheme.cs deleted file mode 100644 index ccc7cd130..000000000 --- a/src/Wpf.Ui/Interop/UxTheme.cs +++ /dev/null @@ -1,178 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. - - NOTE: - I split unmanaged code stuff into the NativeMethods library. - If you have suggestions for the code below, please submit your changes there. - https://github.com/lepoco/nativemethods */ - -using System.Runtime.InteropServices; -using System.Text; - -namespace Wpf.Ui.Interop; - -// ReSharper disable IdentifierTypo -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter -#pragma warning disable CA1060 // Move pinvokes to native methods class - -internal static class UxTheme -{ - /// - /// Returned by the GetThemeMargins function to define the margins of windows that have visual styles applied. - /// - public struct MARGINS - { - /// - /// Width of left border that retains its size. - /// - public int cxLeftWidth; - - /// - /// Width of right border that retains its size. - /// - public int cxRightWidth; - - /// - /// Height of top border that retains its size. - /// - public int cyTopHeight; - - /// - /// Height of bottom border that retains its size. - /// - public int cyBottomHeight; - } - - /// - /// Specifies the type of visual style attribute to set on a window. - /// - public enum WINDOWTHEMEATTRIBUTETYPE : uint - { - /// - /// Non-client area window attributes will be set. - /// - WTA_NONCLIENT = 1, - } - - /// - /// WindowThemeNonClientAttributes - /// - [Flags] - public enum WTNCA : uint - { - /// - /// Prevents the window caption from being drawn. - /// - NODRAWCAPTION = 0x00000001, - - /// - /// Prevents the system icon from being drawn. - /// - NODRAWICON = 0x00000002, - - /// - /// Prevents the system icon menu from appearing. - /// - NOSYSMENU = 0x00000004, - - /// - /// Prevents mirroring of the question mark, even in right-to-left (RTL) layout. - /// - NOMIRRORHELP = 0x00000008, - - /// - /// A mask that contains all the valid bits. - /// - VALIDBITS = NODRAWCAPTION | NODRAWICON | NOMIRRORHELP | NOSYSMENU, - } - - /// - /// Defines options that are used to set window visual style attributes. - /// - [StructLayout(LayoutKind.Explicit)] - public struct WTA_OPTIONS - { - // public static readonly uint Size = (uint)Marshal.SizeOf(typeof(WTA_OPTIONS)); - public const uint Size = 8; - - /// - /// A combination of flags that modify window visual style attributes. - /// Can be a combination of the WTNCA constants. - /// - [FieldOffset(0)] - public WTNCA dwFlags; - - /// - /// A bitmask that describes how the values specified in dwFlags should be applied. - /// If the bit corresponding to a value in dwFlags is 0, that flag will be removed. - /// If the bit is 1, the flag will be added. - /// - [FieldOffset(4)] - public WTNCA dwMask; - } - - /// - /// Sets attributes to control how visual styles are applied to a specified window. - /// - /// - /// Handle to a window to apply changes to. - /// - /// - /// Value of type WINDOWTHEMEATTRIBUTETYPE that specifies the type of attribute to set. - /// The value of this parameter determines the type of data that should be passed in the pvAttribute parameter. - /// Can be the following value: - /// WTA_NONCLIENT (Specifies non-client related attributes). - /// pvAttribute must be a pointer of type WTA_OPTIONS. - /// - /// - /// A pointer that specifies attributes to set. Type is determined by the value of the eAttribute value. - /// - /// - /// Specifies the size, in bytes, of the data pointed to by pvAttribute. - /// - [DllImport(Libraries.UxTheme, PreserveSig = false)] - public static extern void SetWindowThemeAttribute( - [In] IntPtr hWnd, - [In] WINDOWTHEMEATTRIBUTETYPE eAttribute, - [In] ref WTA_OPTIONS pvAttribute, - [In] uint cbAttribute - ); - - /// - /// Tests if a visual style for the current application is active. - /// - /// if a visual style is enabled, and windows with visual styles applied should call OpenThemeData to start using theme drawing services. - [DllImport(Libraries.UxTheme)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool IsThemeActive(); - - /// - /// Retrieves the name of the current visual style, and optionally retrieves the color scheme name and size name. - /// - /// Pointer to a string that receives the theme path and file name. - /// Value of type int that contains the maximum number of characters allowed in the theme file name. - /// Pointer to a string that receives the color scheme name. This parameter may be set to NULL. - /// Value of type int that contains the maximum number of characters allowed in the color scheme name. - /// Pointer to a string that receives the size name. This parameter may be set to NULL. - /// Value of type int that contains the maximum number of characters allowed in the size name. - /// HRESULT - [DllImport(Libraries.UxTheme, CharSet = CharSet.Unicode)] - public static extern int GetCurrentThemeName( - [Out] StringBuilder pszThemeFileName, - [In] int dwMaxNameChars, - [Out] StringBuilder pszColorBuff, - [In] int cchMaxColorChars, - [Out] StringBuilder pszSizeBuff, - [In] int cchMaxSizeChars - ); -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter -#pragma warning restore CA1060 // Move pinvokes to native methods class diff --git a/src/Wpf.Ui/Interop/WinDef/POINT.cs b/src/Wpf.Ui/Interop/WinDef/POINT.cs deleted file mode 100644 index 4aa0fd702..000000000 --- a/src/Wpf.Ui/Interop/WinDef/POINT.cs +++ /dev/null @@ -1,34 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. */ - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop.WinDef; - -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter - -/// -/// The POINT structure defines the x- and y-coordinates of a point. -/// -[StructLayout(LayoutKind.Sequential)] -public struct POINT -{ - /// - /// Specifies the x-coordinate of the point. - /// - public int x; - - /// - /// Specifies the y-coordinate of the point. - /// - public int y; -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter diff --git a/src/Wpf.Ui/Interop/WinDef/POINTL.cs b/src/Wpf.Ui/Interop/WinDef/POINTL.cs deleted file mode 100644 index d85aa49b5..000000000 --- a/src/Wpf.Ui/Interop/WinDef/POINTL.cs +++ /dev/null @@ -1,34 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. */ - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop.WinDef; - -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter - -/// -/// The structure defines the x- and y-coordinates of a point. -/// -[StructLayout(LayoutKind.Sequential)] -public struct POINTL -{ - /// - /// Specifies the x-coordinate of the point. - /// - public long x; - - /// - /// Specifies the y-coordinate of the point. - /// - public long y; -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter diff --git a/src/Wpf.Ui/Interop/WinDef/RECT.cs b/src/Wpf.Ui/Interop/WinDef/RECT.cs deleted file mode 100644 index 57ade04eb..000000000 --- a/src/Wpf.Ui/Interop/WinDef/RECT.cs +++ /dev/null @@ -1,155 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. */ - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop.WinDef; - -// ReSharper disable InconsistentNaming - -/// -/// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. -/// -[StructLayout(LayoutKind.Sequential)] -public struct RECT -{ - private int _left; - private int _top; - private int _right; - private int _bottom; - - /// - /// Gets or sets the x-coordinate of the upper-left corner of the rectangle. - /// - public int Left - { - readonly get { return _left; } - set { _left = value; } - } - - /// - /// Gets or sets the x-coordinate of the lower-right corner of the rectangle. - /// - public int Right - { - readonly get { return _right; } - set { _right = value; } - } - - /// - /// Gets or sets the y-coordinate of the upper-left corner of the rectangle. - /// - public int Top - { - readonly get { return _top; } - set { _top = value; } - } - - /// - /// Gets or sets the y-coordinate of the lower-right corner of the rectangle. - /// - public int Bottom - { - readonly get { return _bottom; } - set { _bottom = value; } - } - - /// - /// Gets the width of the rectangle. - /// - public readonly int Width - { - get { return _right - _left; } - } - - /// - /// Gets the height of the rectangle. - /// - public readonly int Height - { - get { return _bottom - _top; } - } - - /// - /// Gets the position of the rectangle. - /// - public POINT Position - { - get { return new POINT { x = _left, y = _top }; } - } - - /// - /// Gets the size of the rectangle. - /// - public SIZE Size - { - get { return new SIZE { cx = Width, cy = Height }; } - } - - /// - /// Sets offset of the rectangle. - /// - public void Offset(int dx, int dy) - { - _left += dx; - _top += dy; - _right += dx; - _bottom += dy; - } - - /// - /// Combines two RECTs. - /// - public static RECT Union(RECT rect1, RECT rect2) - { - return new RECT - { - Left = Math.Min(rect1.Left, rect2.Left), - Top = Math.Min(rect1.Top, rect2.Top), - Right = Math.Max(rect1.Right, rect2.Right), - Bottom = Math.Max(rect1.Bottom, rect2.Bottom), - }; - } - - /// - public override readonly bool Equals(object? obj) - { - if (obj is not RECT) - { - return false; - } - - try - { - var rc = (RECT)obj; - - return rc._bottom == _bottom && rc._left == _left && rc._right == _right && rc._top == _top; - } - catch (InvalidCastException) - { - return false; - } - } - - /// - public override readonly int GetHashCode() - { - return _top.GetHashCode() ^ _bottom.GetHashCode() ^ _left.GetHashCode() ^ _right.GetHashCode(); - } - - public static bool operator ==(RECT left, RECT right) - { - return left.Equals(right); - } - - public static bool operator !=(RECT left, RECT right) - { - return !(left == right); - } -} diff --git a/src/Wpf.Ui/Interop/WinDef/RECTL.cs b/src/Wpf.Ui/Interop/WinDef/RECTL.cs deleted file mode 100644 index 614f69671..000000000 --- a/src/Wpf.Ui/Interop/WinDef/RECTL.cs +++ /dev/null @@ -1,154 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. */ - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop.WinDef; - -// ReSharper disable InconsistentNaming - -/// -/// The RECTL structure defines a rectangle by the coordinates of its upper-left and lower-right corners. -/// -[StructLayout(LayoutKind.Sequential)] -public struct RECTL -{ - private long _left; - private long _top; - private long _right; - private long _bottom; - - /// - /// Gets or sets the x-coordinate of the upper-left corner of the rectangle. - /// - public long Left - { - readonly get { return _left; } - set { _left = value; } - } - - /// - /// Gets or sets the x-coordinate of the lower-right corner of the rectangle. - /// - public long Right - { - readonly get { return _right; } - set { _right = value; } - } - - /// - /// Gets or sets the y-coordinate of the upper-left corner of the rectangle. - /// - public long Top - { - readonly get { return _top; } - set { _top = value; } - } - - /// - /// Gets or sets the y-coordinate of the lower-right corner of the rectangle. - /// - public long Bottom - { - readonly get { return _bottom; } - set { _bottom = value; } - } - - /// - /// Gets the width of the rectangle. - /// - public readonly long Width - { - get { return _right - _left; } - } - - /// - /// Gets the height of the rectangle. - /// - public readonly long Height - { - get { return _bottom - _top; } - } - - /// - /// Gets the position of the rectangle. - /// - public POINTL Position - { - get { return new POINTL { x = _left, y = _top }; } - } - - /// - /// Gets the size of the rectangle. - /// - public SIZE Size - { - get { return new SIZE { cx = Width, cy = Height }; } - } - - /// - /// Sets offset of the rectangle. - /// - public void Offset(int dx, int dy) - { - _left += dx; - _top += dy; - _right += dx; - _bottom += dy; - } - - /// - /// Combines two RECTLs - /// - public static RECTL Union(RECTL rect1, RECTL rect2) - { - return new RECTL - { - Left = Math.Min(rect1.Left, rect2.Left), - Top = Math.Min(rect1.Top, rect2.Top), - Right = Math.Max(rect1.Right, rect2.Right), - Bottom = Math.Max(rect1.Bottom, rect2.Bottom), - }; - } - - /// - public override readonly bool Equals(object? obj) - { - if (obj is not RECTL) - { - return false; - } - - try - { - var rc = (RECTL)obj; - return rc._bottom == _bottom && rc._left == _left && rc._right == _right && rc._top == _top; - } - catch (InvalidCastException) - { - return false; - } - } - - /// - public override readonly int GetHashCode() - { - return _top.GetHashCode() ^ _bottom.GetHashCode() ^ _left.GetHashCode() ^ _right.GetHashCode(); - } - - public static bool operator ==(RECTL left, RECTL right) - { - return left.Equals(right); - } - - public static bool operator !=(RECTL left, RECTL right) - { - return !(left == right); - } -} diff --git a/src/Wpf.Ui/Interop/WinDef/SIZE.cs b/src/Wpf.Ui/Interop/WinDef/SIZE.cs deleted file mode 100644 index a0a2aa48d..000000000 --- a/src/Wpf.Ui/Interop/WinDef/SIZE.cs +++ /dev/null @@ -1,34 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. -// Copyright (C) Leszek Pomianowski and WPF UI Contributors. -// All Rights Reserved. - -/* This Source Code is partially based on reverse engineering of the Windows Operating System, - and is intended for use on Windows systems only. - This Source Code is partially based on the source code provided by the .NET Foundation. */ - -using System.Runtime.InteropServices; - -namespace Wpf.Ui.Interop.WinDef; - -// ReSharper disable InconsistentNaming -#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter - -/// -/// The SIZE structure defines the width and height of a rectangle. -/// -[StructLayout(LayoutKind.Sequential)] -public struct SIZE -{ - /// - /// Specifies the rectangle's width. The units depend on which function uses this structure. - /// - public long cx; - - /// - /// Specifies the rectangle's height. The units depend on which function uses this structure. - /// - public long cy; -} - -#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter diff --git a/src/Wpf.Ui/NativeMethods.txt b/src/Wpf.Ui/NativeMethods.txt new file mode 100644 index 000000000..caf4b418b --- /dev/null +++ b/src/Wpf.Ui/NativeMethods.txt @@ -0,0 +1,34 @@ +S_OK +DwmIsCompositionEnabled +DwmExtendFrameIntoClientArea +SetWindowThemeAttribute +GetDpiForWindow +IsWindowVisible +SetWindowLong +GetWindowLong +SetWindowRgn +GetWindowRect +GetSystemMetrics +IsWindow +DwmSetWindowAttribute +DwmGetWindowAttribute +ITaskbarList4 +TaskbarList +DWM_SYSTEMBACKDROP_TYPE +DWM_WINDOW_CORNER_PREFERENCE +DWMWA_COLOR_NONE +WINDOW_STYLE +WTA_OPTIONS +WTNCA_* +WM_* +HTBOTTOM* +HTCAPTION +HTCLOSE +HTHELP +HTLEFT* +HTMAXBUTTON +HTMINBUTTON +HTNOWHERE +HTRIGHT* +HTSYSMENU +HTTOP* \ No newline at end of file diff --git a/src/Wpf.Ui/Resources/Accent.xaml b/src/Wpf.Ui/Resources/Accent.xaml index 1cd063de3..cffdd82de 100644 --- a/src/Wpf.Ui/Resources/Accent.xaml +++ b/src/Wpf.Ui/Resources/Accent.xaml @@ -8,16 +8,16 @@ - #3379d9 + #0078d4 - #559ce4 + #0067c0 - #80b9ee + #003e92 - #add8ff + #001a68 @@ -31,17 +31,17 @@ - + + Color="{StaticResource SystemAccentColorSecondary}" /> - + Color="{StaticResource SystemAccentColorSecondary}" /> + diff --git a/src/Wpf.Ui/Resources/Theme/Dark.xaml b/src/Wpf.Ui/Resources/Theme/Dark.xaml index f49a36f62..702cca23b 100644 --- a/src/Wpf.Ui/Resources/Theme/Dark.xaml +++ b/src/Wpf.Ui/Resources/Theme/Dark.xaml @@ -331,9 +331,9 @@ - - - + + + @@ -341,7 +341,7 @@ - + @@ -358,14 +358,16 @@ - - - + + + + + - + @@ -389,8 +391,10 @@ - + + + @@ -460,10 +464,10 @@ - - - - + + + + @@ -551,7 +555,7 @@ - + @@ -573,7 +577,7 @@ - + @@ -647,15 +651,19 @@ + - + + - + + + diff --git a/src/Wpf.Ui/Resources/Theme/HC1.xaml b/src/Wpf.Ui/Resources/Theme/HC1.xaml index 7fa258835..08783909c 100644 --- a/src/Wpf.Ui/Resources/Theme/HC1.xaml +++ b/src/Wpf.Ui/Resources/Theme/HC1.xaml @@ -243,11 +243,13 @@ + + - + @@ -272,7 +274,9 @@ + + @@ -532,15 +536,19 @@ + + + + diff --git a/src/Wpf.Ui/Resources/Theme/HC2.xaml b/src/Wpf.Ui/Resources/Theme/HC2.xaml index 4710ee461..46a1e612c 100644 --- a/src/Wpf.Ui/Resources/Theme/HC2.xaml +++ b/src/Wpf.Ui/Resources/Theme/HC2.xaml @@ -242,11 +242,13 @@ + + - + @@ -271,7 +273,9 @@ + + @@ -531,15 +535,19 @@ + + + + diff --git a/src/Wpf.Ui/Resources/Theme/HCBlack.xaml b/src/Wpf.Ui/Resources/Theme/HCBlack.xaml index 27ac80fb6..aa8044888 100644 --- a/src/Wpf.Ui/Resources/Theme/HCBlack.xaml +++ b/src/Wpf.Ui/Resources/Theme/HCBlack.xaml @@ -242,11 +242,13 @@ + + - + @@ -271,7 +273,9 @@ + + @@ -531,15 +535,19 @@ + + + + diff --git a/src/Wpf.Ui/Resources/Theme/HCWhite.xaml b/src/Wpf.Ui/Resources/Theme/HCWhite.xaml index c18c05062..133fdd886 100644 --- a/src/Wpf.Ui/Resources/Theme/HCWhite.xaml +++ b/src/Wpf.Ui/Resources/Theme/HCWhite.xaml @@ -242,11 +242,13 @@ + + - + @@ -271,7 +273,9 @@ + + @@ -531,15 +535,19 @@ + + + + diff --git a/src/Wpf.Ui/Resources/Theme/Light.xaml b/src/Wpf.Ui/Resources/Theme/Light.xaml index 106a5ea3a..3bbeaa7d4 100644 --- a/src/Wpf.Ui/Resources/Theme/Light.xaml +++ b/src/Wpf.Ui/Resources/Theme/Light.xaml @@ -332,9 +332,9 @@ - - - + + + @@ -342,7 +342,7 @@ - + @@ -359,14 +359,16 @@ - - - + + + + + - + @@ -390,8 +392,10 @@ - + + + @@ -461,10 +465,10 @@ - - - - + + + + @@ -552,7 +556,7 @@ - + @@ -574,7 +578,7 @@ - + @@ -648,15 +652,19 @@ + - + + - + + + diff --git a/src/Wpf.Ui/UiApplication.cs b/src/Wpf.Ui/UiApplication.cs index 43b1e92f2..37f4c5a4f 100644 --- a/src/Wpf.Ui/UiApplication.cs +++ b/src/Wpf.Ui/UiApplication.cs @@ -10,6 +10,7 @@ namespace Wpf.Ui; /// public class UiApplication { + [ThreadStatic] private static UiApplication? _uiApplication; private readonly Application? _application; diff --git a/src/Wpf.Ui/Win32/Utilities.cs b/src/Wpf.Ui/Win32/Utilities.cs index 97e32c27f..8806c2f1b 100644 --- a/src/Wpf.Ui/Win32/Utilities.cs +++ b/src/Wpf.Ui/Win32/Utilities.cs @@ -8,6 +8,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.Foundation; namespace Wpf.Ui.Win32; @@ -86,9 +88,7 @@ public static bool IsCompositionEnabled return false; } - _ = Interop.Dwmapi.DwmIsCompositionEnabled(out var pfEnabled); - - return pfEnabled != 0; + return PInvoke.DwmIsCompositionEnabled(out BOOL enabled) == HRESULT.S_OK & enabled; } } diff --git a/src/Wpf.Ui/Wpf.Ui.csproj b/src/Wpf.Ui/Wpf.Ui.csproj index ad0903130..1b322d324 100644 --- a/src/Wpf.Ui/Wpf.Ui.csproj +++ b/src/Wpf.Ui/Wpf.Ui.csproj @@ -1,12 +1,14 @@ - + WPF-UI - net462;net472;net481;net6.0-windows;net8.0-windows;net9.0-windows + net10.0-windows;net9.0-windows;net8.0-windows;net481;net472;net462 true WPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly. true + preview true + System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute;System.Diagnostics.CodeAnalysis.UnscopedRefAttribute @@ -52,15 +54,12 @@ - - all - build; analyzers - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/TestedApplication.cs b/tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/TestedApplication.cs new file mode 100644 index 000000000..27816300d --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/TestedApplication.cs @@ -0,0 +1,85 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using FlaUI.Core; +using FlaUI.Core.Tools; +using FlaUI.UIA3; + +namespace Wpf.Ui.Gallery.IntegrationTests.Fixtures; + +/// +/// Class managing the lifecycle of the tested application implementing . +/// Uses for UI automation. +/// +public sealed class TestedApplication : IAsyncLifetime +{ + private const string ExecutableName = "Wpf.Ui.Gallery.exe"; + + private readonly AutomationBase automation = new UIA3Automation(); + + private Application? app; + + private Window? mainWindow; + + /// + /// Gets the wrapper for an application which should be automated. + /// + public Application? Application => app; + + /// + /// Gets the main window of the applications process. + /// + public Window? MainWindow => mainWindow ??= app?.GetMainWindow(automation); + + /// + public ValueTask InitializeAsync() + { + if (app is not null) + { + app.Close(); + app.Dispose(); + } + + string path = Path.Combine( + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, + ExecutableName + ); + + if (!File.Exists(path)) + { + throw new InvalidOperationException( + $"Unable to find the application executable at path \"{path}\"." + ); + } + + app = Application.Launch(path); + app.WaitWhileMainHandleIsMissing(TimeSpan.FromMinutes(1)); + + return ValueTask.CompletedTask; + } + + /// + public ValueTask DisposeAsync() + { + if (app is not null) + { + if (!app.HasExited) + { + app.Close(); + } + + // ReSharper disable once AccessToDisposedClosure + Retry.WhileFalse(() => app?.HasExited ?? true, TimeSpan.FromSeconds(2), ignoreException: true); + + app.Dispose(); + + app = null; + } + + automation?.Dispose(); + + return ValueTask.CompletedTask; + } +} diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/UiTest.cs b/tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/UiTest.cs new file mode 100644 index 000000000..84ebd93d2 --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/Fixtures/UiTest.cs @@ -0,0 +1,92 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using FlaUI.Core; +using FlaUI.Core.Conditions; +using FlaUI.Core.Input; +using FlaUI.Core.WindowsAPI; + +namespace Wpf.Ui.Gallery.IntegrationTests.Fixtures; + +/// +/// Base class for UI tests implementing to manage lifecycle. +/// +public abstract class UiTest : IAsyncLifetime +{ + private readonly TestedApplication app = new(); + + /// + /// Gets the wrapper for an application which should be automated. + /// + internal Application? Application => app.Application; + + /// + /// Gets the main window of the applications process. + /// + internal Window? MainWindow => app.MainWindow; + + /// + public ValueTask InitializeAsync() => app.InitializeAsync(); + + /// + public ValueTask DisposeAsync() => app.DisposeAsync(); + + /// + /// Finds the first descendant with the given automation id. + /// + /// The automation id. + /// The found element or null if no element was found. + protected AutomationElement? FindFirst(string automationId) => + app.MainWindow?.FindFirstDescendant(automationId); + + /// Finds the first descendant with the condition. + /// The condition method. + /// The found element or null if no element was found. + protected AutomationElement? FindFirst(Func conditionFunc) => + app.MainWindow?.FindFirstDescendant(conditionFunc); + + /// + /// Creates a Task that will complete after a time delay. + /// + /// The time delay in seconds. + /// A Task that represents the time delay + /// + /// After the specified time delay, the Task is completed in RanToCompletion state. + /// + protected Task Wait(int seconds) => Task.Delay(TimeSpan.FromSeconds(seconds)); + + /// + /// Simulate typing in text. This is slower than setting but raises more events. + /// + protected void Enter(string value) + { + if (string.IsNullOrEmpty(value)) + { + return; + } + + string[] source = value.Replace("\r\n", "\n").Split('\n'); + + Keyboard.Type(source[0]); + + foreach (string text in ((IEnumerable)source).Skip(1)) + { + Keyboard.Type(VirtualKeyShort.RETURN); + Keyboard.Type(text); + } + + global::FlaUI.Core.Input.Wait.UntilInputIsProcessed(); + } + + /// + /// Type the given key. + /// + protected void Press(VirtualKeyShort virtualKey) + { + Keyboard.Type(virtualKey); + + global::FlaUI.Core.Input.Wait.UntilInputIsProcessed(); + } +} diff --git a/tests/Wpf.Ui.Gallery.UnitTests/UnitTest1.cs b/tests/Wpf.Ui.Gallery.IntegrationTests/GlobalUsings.cs similarity index 59% rename from tests/Wpf.Ui.Gallery.UnitTests/UnitTest1.cs rename to tests/Wpf.Ui.Gallery.IntegrationTests/GlobalUsings.cs index dbdc4b17e..c19cb733d 100644 --- a/tests/Wpf.Ui.Gallery.UnitTests/UnitTest1.cs +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/GlobalUsings.cs @@ -3,10 +3,8 @@ // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. -namespace Wpf.Ui.Gallery.UnitTests; - -public class UnitTest1 -{ - [Fact] - public void Test1() { } -} +global using System.Reflection; +global using AwesomeAssertions; +global using FlaUI.Core.AutomationElements; +global using Wpf.Ui.FlaUI; +global using Wpf.Ui.Gallery.IntegrationTests.Fixtures; diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/NavigationTests.cs b/tests/Wpf.Ui.Gallery.IntegrationTests/NavigationTests.cs new file mode 100644 index 000000000..1b322767f --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/NavigationTests.cs @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using FlaUI.Core.Definitions; +using FlaUI.Core.WindowsAPI; + +namespace Wpf.Ui.Gallery.IntegrationTests; + +public sealed class NavigationTests : UiTest +{ + [Fact] + public async Task Settings_ShouldBeAvailable_ThroughAutoSuggestBox() + { + AutomationElement? autoSuggestBox = FindFirst("NavigationAutoSuggestBox"); + + autoSuggestBox + .Should() + .NotBeNull("because NavigationAutoSuggestBox should be present in the main window"); + + autoSuggestBox.As().Enter("Settings"); + + await Wait(1); + + FindFirst(c => c.ByText("About")) + .Should() + .NotBeNull("because Settings page should be displayed after clicking the Settings button"); + } + + [Fact] + public async Task Settings_ShouldBeAvailable_ThroughNavigation() + { + AutomationElement? settingsButton = FindFirst("NavigationFooterItems") + ?.FindFirstDescendant(c => c.ByText("Settings")); + + settingsButton.Should().NotBeNull("because NavigationView should be present in the main window"); + settingsButton.Click(); + + await Wait(1); + + FindFirst(c => c.ByText("About")) + .Should() + .NotBeNull("because Settings page should be displayed after clicking the Settings button"); + } +} diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/TitleBarTests.cs b/tests/Wpf.Ui.Gallery.IntegrationTests/TitleBarTests.cs new file mode 100644 index 000000000..79456cdab --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/TitleBarTests.cs @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +using System.Windows.Automation; +using WindowVisualState = FlaUI.Core.Definitions.WindowVisualState; + +namespace Wpf.Ui.Gallery.IntegrationTests; + +public sealed class TitleBarTests : UiTest +{ + [Fact] + public async Task CloseButton_ShouldCloseWindow_WhenClicked() + { + Button? closeButton = FindFirst("TitleBarCloseButton").AsButton(); + + closeButton.Should().NotBeNull("because CloseButton should be present in the main window title bar"); + closeButton.Click(moveMouse: false); + + await Wait(2); + + Application + ?.HasExited.Should() + .BeTrue("because the main window should be closed after clicking the close button"); + } + + [Fact] + public async Task MinimizeButton_ShouldHideWindow_WhenClicked() + { + Button? minimizeButton = FindFirst("TitleBarMinimizeButton").AsButton(); + + minimizeButton + .Should() + .NotBeNull("because MinimizeButton should be present in the main window title bar"); + minimizeButton.Click(moveMouse: false); + + await Wait(2); + + MainWindow + .Patterns.Window.Pattern.WindowVisualState.ValueOrDefault.Should() + .Be( + WindowVisualState.Minimized, + "because the main window should be minimized after clicking the minimize button" + ); + } + + [Fact] + public async Task MaximizeButton_ShouldExpandWindow_WhenClicked() + { + Button? maximizeButton = FindFirst("TitleBarMaximizeButton").AsButton(); + + maximizeButton + .Should() + .NotBeNull("because MaximizeButton should be present in the main window title bar"); + maximizeButton.Click(moveMouse: false); + + await Wait(2); + + MainWindow + .Patterns.Window.Pattern.WindowVisualState.ValueOrDefault.Should() + .Be( + WindowVisualState.Maximized, + "because the main window should be maximized after clicking the maximize button" + ); + } +} diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/WindowTests.cs b/tests/Wpf.Ui.Gallery.IntegrationTests/WindowTests.cs new file mode 100644 index 000000000..b27c75411 --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/WindowTests.cs @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and WPF UI Contributors. +// All Rights Reserved. + +namespace Wpf.Ui.Gallery.IntegrationTests; + +public sealed class WindowTests() : UiTest +{ + [Fact] + public void WindowTitle_ShouldMatchPredefinedOne() + { + string? title = MainWindow?.Title; + + title.Should().Be("WPF UI Gallery", "because the main window title should match the predefined one"); + } +} diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/Wpf.Ui.Gallery.IntegrationTests.csproj b/tests/Wpf.Ui.Gallery.IntegrationTests/Wpf.Ui.Gallery.IntegrationTests.csproj new file mode 100644 index 000000000..7956d330c --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/Wpf.Ui.Gallery.IntegrationTests.csproj @@ -0,0 +1,39 @@ + + + + net10.0-windows10.0.26100.0 + enable + Exe + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/tests/Wpf.Ui.Gallery.IntegrationTests/xunit.runner.json b/tests/Wpf.Ui.Gallery.IntegrationTests/xunit.runner.json new file mode 100644 index 000000000..2b64621b3 --- /dev/null +++ b/tests/Wpf.Ui.Gallery.IntegrationTests/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "culture": "invariant", + "parallelizeTestCollections": false, + "diagnosticMessages": true +} diff --git a/tests/Wpf.Ui.Gallery.UnitTests/Wpf.Ui.Gallery.UnitTests.csproj b/tests/Wpf.Ui.Gallery.UnitTests/Wpf.Ui.Gallery.UnitTests.csproj deleted file mode 100644 index e251be850..000000000 --- a/tests/Wpf.Ui.Gallery.UnitTests/Wpf.Ui.Gallery.UnitTests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net9.0-windows10.0.26100.0 - false - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj b/tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj index c2e06abd5..efa518b76 100644 --- a/tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj +++ b/tests/Wpf.Ui.UnitTests/Wpf.Ui.UnitTests.csproj @@ -1,7 +1,7 @@ - net9.0-windows + net10.0-windows false