diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7b29b75140..0e052d01ea 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,48 +2,34 @@ ## Disclosure -IMPORTANT: For any Markdown files generated by AI, always disclose that they were created with the assistance of AI. Add the following frontmatter key/value pair: +IMPORTANT: For any Markdown files generated by AI, always disclose that they were created with the assistance of AI. If missing, add the `ai-usage` frontmatter key/value pair: -```markdown -ai-usage: ai-generated -``` - -## Terminology +- When reviewing a PR not created by AI: -- Unless otherwise specified, all .NET content refers to modern .NET (not .NET Framework). + ```markdown + ai-usage: ai-assisted + ``` -## Writing Style +- When Copilot generates the article through GitHub without the use of a human: -Follow [Microsoft Writing Style Guide](https://learn.microsoft.com/en-us/style-guide/welcome/) with these specifics: + ```markdown + ai-usage: ai-generated + ``` -### Voice and Tone +- When using an IDE with a human guiding AI: -- Active voice, second person addressing reader directly. -- Conversational tone with contractions. -- Present tense for instructions/descriptions. -- Imperative mood for instructions ("Call the method" not "You should call the method"). -- Use "might" instead of "may" for possibility. -- Use "can" instead of "may" for permissible actions. -- Avoid "we"/"our" referring to documentation authors or product teams. + ```markdown + ai-usage: ai-assisted + ``` -### Structure and Format +## New articles -- Sentence case headings (no gerunds in titles). -- Be concise, break up long sentences. -- IMPORTANT: Oxford comma in lists. -- Number all ordered list items as "1." (not sequential numbering like "1.", "2.", "3.", etc.) -- IMPORTANT: Use bullets for unordered lists. -- VERY IMPORTANT: Ordered and unordered lists should use complete sentences with proper punctuation, ending with a period if it's more than three words. -- Avoid "etc." or "and so on" - provide complete lists or use "for example". -- No consecutive headings without content between them. +- New articles must follow a template in the /.github/projects/article-templates/ folder. +- If you don't know which template to use, ask. -### Formatting Conventions +## Terminology -- **Bold** for UI elements. -- `Code style` for file names, folders, custom types, non-localizable text. -- Raw URLs in angle brackets. -- Use relative links for files in this repo. -- Remove `https://learn.microsoft.com/en-us` from learn.microsoft.com links. +- Unless otherwise specified, all .NET content refers to modern .NET (not .NET Framework). ## API References @@ -66,11 +52,66 @@ For snippets >6 lines: 1. Create examples in both C# and Visual Basic unless the article referencing the snippet resides in the in the `csharp`, `fsharp`, and `visual-basic` language folders. 1. When you add code, use code comments sparingly because they don't get localized. You can use them to briefly clarify code-specific details (such as logic, parameters, or edge cases). Put any critical information and context in the markdown text of the referencing article. +## .NET Framework vs .NET differences + +When documenting differences between .NET Framework and .NET (like .NET 6 and newer), choose the appropriate system: + +### Use Tabs +Use tabbed content when the differences are code-based: + +```markdown +# [.NET](#tab/dotnet) + + + +# [.NET Framework](#tab/dotnetframework) + + + +--- +``` + +### Use Pivots +Use zone pivots when there are conceptual differences that can't easily be explained with tabs and a note. Pivots allow for more comprehensive explanations of different approaches or methodologies. + +To use zone pivots: + +1. Add `zone_pivot_groups: dotnet-version` to the article's frontmatter +2. Use zone pivot syntax in content: + +```markdown +::: zone pivot="dotnet" + +Your .NET content here + +::: zone-end + +::: zone pivot="dotnetframework" + +Your .NET Framework content here + +::: zone-end +``` + ## File Naming New Markdown files: lowercase with hyphens, omit filler words (the, a, etc.). +Examples: +- ✅ Good: `getting-started-with-entity-framework.md` +- ✅ Good: `configure-logging.md` +- ✅ Good: `dependency-injection-guidelines.md` +- ❌ Bad: `Getting-Started-With-The-Entity-Framework.md` +- ❌ Bad: `configure_logging.md` +- ❌ Bad: `DependencyInjectionGuidelines.md` + ## Special Cases -- Breaking changes: Include directions from `.github/prompts/breaking-change.md`. -- When you (Copilot) are assigned an issue in GitHub, after you've completed your work and the workflows (status checks) have run, check to make sure there are no build warnings under the OpenPublishing.Build status check. If there are, open the build report (under View Details) and resolve any build warnings you introduced. +### GitHub Issue Assignment (AI Workflow) +When assigned an issue in GitHub: +1. Complete your work +2. Wait for workflows (status checks) to run +3. Check for build warnings in the OpenPublishing.Build status check +4. If warnings exist: + - Click "View Details" to open the build report + - Resolve any build warnings you introduced diff --git a/.github/instructions/Markdown.WritingStyle.instructions.md b/.github/instructions/Markdown.WritingStyle.instructions.md new file mode 100644 index 0000000000..eb72f1e320 --- /dev/null +++ b/.github/instructions/Markdown.WritingStyle.instructions.md @@ -0,0 +1,83 @@ +--- +applyTo: 'dotnet-desktop-guide/**/*.md' +description: 'Follow these comprehensive writing style guidelines when creating or editing Markdown documentation. Apply active voice, conversational tone, Oxford commas, and specific formatting rules to ensure consistency and readability across all documentation.' +--- + +# Markdown Writing Style Instructions + +When writing or editing Markdown documentation, follow these style guidelines: + +## Voice and Tone Requirements + +ALWAYS write using: +- Active voice with second person ("you") +- Conversational tone with contractions +- Present tense for instructions and descriptions +- Imperative mood for instructions (write "Call the method" NOT "You should call the method") +- "might" for possibility (NOT "may") +- "can" for permissible actions (NOT "may") + +NEVER use: +- "we" or "our" when referring to documentation authors or product teams +- Jargon or overly complex technical language +- Weak phrases like "you can" or "there is/are/were" unless they add value + +ALWAYS: +- Write like you speak using everyday words +- Create a friendly, informal tone +- Start statements with verbs when possible + +## Structure and Format Rules + +### Headings and Content +- Use sentence case headings (capitalize only first word and proper nouns) +- Never use gerunds in titles +- Never place consecutive headings without content between them +- Lead with the most important information first +- Front-load keywords for scanning + +### Lists and Punctuation +- **CRITICAL: Use Oxford comma in ALL lists (item1, item2, and item3) - NO EXCEPTIONS** +- **MANDATORY: Number ordered lists using "1." for every item (NOT 1., 2., 3.) - ALWAYS USE "1."** +- **REQUIRED: Use bullets for unordered lists - NEVER use numbers for unordered content** +- **ESSENTIAL: Write complete sentences in lists with proper punctuation** +- **MUST: End list items with periods if more than three words - THIS IS NON-NEGOTIABLE** +- Skip end punctuation on titles, headings, and UI elements (3 words or fewer) + +### Spacing and Layout +- Add blank lines around Markdown elements (but don't add extra if they exist) +- Use only one space after periods, question marks, and colons +- Use no spaces around dashes (word—word) +- Break up long sentences for clarity + +### Prohibited Terms +- Never write "etc." or "and so on" - provide complete lists or use "for example" +- Use "for example" instead of "e.g." +- Use "that is" instead of "i.e." + +## Formatting Conventions + +Apply these formatting rules: +- **Bold text** for UI elements +- `Code style` for file names, folders, custom types, and non-localizable text +- Raw URLs in angle brackets +- Relative links for files in this repository +- Remove `https://learn.microsoft.com/en-us` from Microsoft Learn links + +## Word Choice Requirements + +### Verb Selection +- Choose simple verbs without modifiers +- Avoid weak verbs: "be," "have," "make," "do" +- Use precise verbs (write "tell" NOT "inform") + +### Conciseness Rules +- Use one word instead of multiple when possible (write "to" NOT "in order to") +- Choose words with one clear meaning (write "because" NOT "since" for causation) +- Omit unnecessary adverbs unless critical to meaning +- Use one term consistently for each concept + +### Contraction Guidelines +- Use common contractions: "it's," "you're," "that's," "don't" +- Avoid ambiguous contractions: "there'd," "it'll," "they'd" +- Never form contractions from noun + verb (avoid "Microsoft's developing") \ No newline at end of file diff --git a/.github/instructions/Snippets.Migrate.instructions.md b/.github/instructions/Snippets.Migrate.instructions.md new file mode 100644 index 0000000000..f863d39e57 --- /dev/null +++ b/.github/instructions/Snippets.Migrate.instructions.md @@ -0,0 +1,142 @@ +--- +description: Migrate code from the old ~/samples/snippets/ location to the relative ./snippets location. +--- + +# Migrate code snippets + +**IMPORTANT**: Unless otherwise asked to, **only** edit the article file in context. At the end of your operations you may ask for permission to edit other articles referencing the same snippets. + +## Repository structure for code snippets + +**IMPORTANT**: This repository has TWO different locations for code snippets: + +### Old location (legacy - to be migrated FROM) +- Path: `~/samples/snippets/` +- Example: `~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs` +- Status: Legacy, should be migrated to new location + +### New location (current standard - migrate TO) +- Path pattern: `./snippets/{doc-file}/[net-or-framework]/{code-language}/` +- Example: `./snippets/how-to-add-data-to-the-clipboard/net/csharp/form1.cs` + +**Path components explained:** +- `{doc-file}`: The markdown article filename WITHOUT the `.md` extension + - Example: For article `how-to-add-data-to-the-clipboard.md` → use `how-to-add-data-to-the-clipboard` +- `[net-or-framework]`: Choose based on target framework: + - `net`: For .NET (.NET 6 and newer) + - `framework`: For .NET Framework (4.8 and older) + - **Rule**: Only include this subfolder when the article demonstrates BOTH .NET and .NET Framework approaches +- `{code-language}`: + - `csharp`: For C# code + - `vb`: For Visual Basic code + +## Legacy code characteristics (migrate FROM these) + +**Location**: `~/samples/snippets/` folder +**Problems with legacy code:** +- Written for .NET Framework (outdated) +- Often incomplete or non-compilable +- May lack project files +- Uses outdated syntax and patterns + +**Legacy article references look like this:** +```markdown +[!code-{code-language}[description](~/samples/snippets/{path-to-file}#{snippet-identifier})] +``` + +## Current code requirements (migrate TO these) + +**Location**: `./snippets/{doc-file}/[framework]/{code-language}/` + +**Requirements for current code standards:** +- ✅ MUST be complete and compilable +- ✅ MUST include a project file +- ✅ MUST target appropriate .NET version (see targeting rules below) +- ✅ MUST provide BOTH C# and Visual Basic versions +- ✅ MUST use appropriate syntax for the target framework +- ✅ MUST use meaningful, descriptive snippet identifiers in CamelCase format + - **Examples** of good snippet identifiers: `BasicClipboardData`, `CustomDataFormat`, `ClipboardImageHandling` + - **Avoid** simplistic identifiers like `1`, `2`, `code1`, or `snippet1` + +**Current article references look like this:** +```markdown +:::code language="{code-language}" source="{file-path}" id="{snippet-identifier}"::: +``` + +**Framework targeting rules:** +- **Migration vs. Modernization**: + - **Migration**: Move code to new location with minimal changes + - **Modernization**: Update code to use latest .NET features and best practices +- **When to migrate only**: When article has `ms.service: dotnet-framework` frontmatter or is specifically about .NET Framework features +- **When to modernize**: When article demonstrates both .NET and .NET Framework, or when specifically requested +- **Default targeting**: + - For .NET Framework-specific articles: Keep targeting .NET Framework + - For general articles: Target latest .NET version (e.g., .NET 10) + - For mixed articles: Create separate snippets in `net/` and `framework/` subfolders + +## Migration steps (follow in order) + +**WHEN TO MIGRATE**: Migrate code when you encounter articles with references to `~/samples/snippets/` paths. + +**STEP-BY-STEP PROCESS:** + +### 1. Analyze existing code and article context +- **Find**: Locate the legacy snippet file in `~/samples/snippets/` +- **Check frontmatter**: Look for `ms.service: dotnet-framework` in the article's frontmatter +- **Determine scope**: + - If frontmatter has `ms.service: dotnet-framework` → this is likely a .NET Framework-specific article + - if frontmatter has `ms.service: dotnet-desktop` or similar → this is likely a dual-framework or general article + - If article demonstrates both .NET and .NET Framework → prepare for dual targeting + - If article is general purpose → consider targeting current .NET +- **Identify**: Determine the programming language (C# or Visual Basic) +- **Extract**: Note the snippet identifier used in the article reference + +### 2. Create new folder structure +- **Pattern**: `./snippets/{doc-file}/[net-or-framework]/{code-language}/` +- **Example**: For article `clipboard-operations.md` → create `./snippets/clipboard-operations/net/csharp/` +- **Decision tree for framework folder**: + - Article has `ms.service: dotnet-framework` frontmatter → use `./snippets/{doc-file}/framework/{code-language}/` + - Article shows ONLY current .NET examples → use `./snippets/{doc-file}/{code-language}/` (omit framework folder) + - Article shows BOTH .NET and .NET Framework → create both `./snippets/{doc-file}/net/{code-language}/` and `./snippets/{doc-file}/framework/{code-language}/` + +### 3. Migrate and update code +- **Copy**: Copy only the snippet code (and any supporting code to compile the snippet) to the new location +- **Update approach**: + - **For .NET Framework articles**: Migrate with minimal changes, keep .NET Framework targeting + - **For dual-framework articles**: Create both versions with appropriate targeting and update frontmatter to `ms.service: dotnet-desktop` + - **For general articles**: Update to target current .NET only if specifically requested or if article demonstrates modernization +- **Complete**: Ensure code is fully functional and compilable +- **Project file**: Create or update project file with appropriate target framework + +### 4. Create both language versions +- **Requirement**: MUST provide both C# and Visual Basic versions +- **C# path**: `./snippets/{doc-file}/[framework]/csharp/` +- **VB path**: `./snippets/{doc-file}/[framework]/vb/` + +### 5. Update article references +- **Replace**: Change from legacy `[!code-...]` format to modern `:::code...:::` format +- **Before**: `[!code-csharp[description](~/samples/snippets/path/file.cs#snippet1)]` +- **After**: `:::code language="csharp" source="./snippets/doc-name/net/csharp/file.cs" id="BasicClipboardData":::` +- **Note**: Use meaningful CamelCase identifiers instead of simple numbers + +### 6. Validate +- **Build**: Ensure all code compiles successfully +- **Test**: Verify snippet references work in the article +- **Clean**: Remove unused legacy files (if no other articles reference them) + +### 7. Delete +- **Identify**: + - Check if the old snippet file is used by any other articles + - Some articles may use a relative path to the `samples` folder, so simply search for links to `samples/snippets/...` + - Be confident that all legacy snippets use a `samples/snippets` folder structure +- **Delete**: If old snippet is no longer used by any article, delete it. + +## Common mistakes to avoid + +- ❌ **Don't** assume all code needs to be modernized - check article context first +- ❌ **Don't** modernize .NET Framework-specific articles unless specifically requested +- ❌ **Don't** ignore the `ms.service: dotnet-framework` frontmatter indicator +- ❌ **Don't** forget to create both C# and VB versions +- ❌ **Don't** mix up the framework targeting (net vs framework) +- ❌ **Don't** forget to update ALL article references to the migrated code +- ❌ **Don't** leave incomplete or non-compilable code diff --git a/.github/instructions/Snippets.Push.instructions.md b/.github/instructions/Snippets.Push.instructions.md new file mode 100644 index 0000000000..b9a9028c3d --- /dev/null +++ b/.github/instructions/Snippets.Push.instructions.md @@ -0,0 +1,121 @@ +--- +description: Push inline code block snippets out of articles into standalone files with proper project structure. +--- + +# Push inline code snippets to files + +**IMPORTANT**: Unless otherwise asked to, **only** edit the article file in context. At the end of your operations you may ask for permission to edit other articles that might benefit from the same snippet extraction. + +## Quick Reference + +**WHEN TO PUSH:** Code >6 lines, complete/compilable examples, or when specifically requested +**FOLDER PATTERN:** `./snippets/{doc-file}/[net-or-framework]/{csharp|vb}/` +**PROJECT CREATION:** Always use `dotnet new {winforms|wpf|console|classlib}` commands to create a new project for the code language +**LANGUAGES:** Create both C# and VB versions +**SNIPPET IDs:** Use CamelCase region markers like `` +**ARTICLE REFS:** Replace with `:::code language="csharp" source="./path" id="SnippetId":::` + +## When to push snippets out of articles + +**PUSH SNIPPETS WHEN:** +- Code blocks are longer than 6 lines or the rest of the article is using them +- Code demonstrates complete, compilable examples +- Code should not be reused across multiple articles +- Code represents a complete application or significant functionality +- User specifically requests snippet extraction + +**KEEP INLINE WHEN:** +- Code blocks are 6 lines or shorter +- Code shows configuration snippets (XAML, JSON, XML) +- Code demonstrates simple one-liner examples +- Code is pseudo-code or conceptual examples + +## Target folder structure + +**IMPORTANT**: Follow a folder structure based on the article and code language: + +### New snippet location (standard) +- Path pattern: `./snippets/{doc-file}/[net-or-framework]/{code-language}/` +- Example: `./snippets/create-windows-forms-app/net/csharp/` + +**Path components explained:** +- `{doc-file}`: The markdown article filename WITHOUT the `.md` extension + - Example: For article `create-windows-forms-app.md` → use `create-windows-forms-app` +- `[net-or-framework]`: Choose based on target framework: + - `net`: For .NET (.NET 6 and newer) + - `framework`: For .NET Framework (4.8 and older) + - **Rule**: Only include this subfolder when the article demonstrates BOTH .NET and .NET Framework approaches +- `{code-language}`: + - `csharp`: For C# code + - `vb`: For Visual Basic code + +## Framework targeting and project types + +**Determine target framework:** +- Check article frontmatter `ms.service` value: + - `dotnet-framework` → .NET Framework 4.8 + - `dotnet-desktop` → Current .NET (e.g., .NET 10) +- Examine code patterns and article content + +**Create appropriate project with `dotnet new`:** + +| Project Type | Indicators | .NET Command | .NET Framework Command | +|--------------|------------|--------------|------------------------| +| **Windows Forms** | `System.Windows.Forms`, `Form`, `/winforms/` path | `dotnet new winforms` | `dotnet new winforms --framework net48` | +| **WPF** | `System.Windows`, `Window`, XAML, `/wpf/` path | `dotnet new wpf` | `dotnet new wpf --framework net48` | +| **Console** | `Console.WriteLine`, simple examples, no UI | `dotnet new console` | `dotnet new console --framework net48` | +| **Class Library** | Reusable components, no entry point | `dotnet new classlib` | `dotnet new classlib --framework net48` | + +## Push process + +### 1. Analyze and prepare +- Locate code blocks >6 lines or complete examples +- Determine project type from code patterns and article location +- Check framework targeting from frontmatter +- Create folder structure: `./snippets/{doc-file}/[net-or-framework]/{csharp|vb}/` + +### 2. Create projects and extract code +- Run appropriate `dotnet new` command in each language folder, **don't** specify an output folder with `-o`. Specify a meaningful project name with `-n` if possible +- Copy and complete code to make it compilable +- Add missing using statements, namespaces, class declarations +- Modernize code patterns if targeting current .NET +- Test compilation with `dotnet build` + +### 3. Add snippet references and update article +- Add CamelCase region markers: `// ` and `// ` +- Use same identifiers across C# and VB versions +- Replace inline code with snippet references: + ```markdown + :::code language="csharp" source="./snippets/doc-name/net/csharp/File.cs" id="ButtonClick"::: + :::code language="vb" source="./snippets/doc-name/net/vb/File.vb" id="ButtonClick"::: + ``` +- DO NOT use language tabs, simply put them side-by-side +- Verify all paths and references are correct + +### 4. Make sure frontmatter specifies a language when required + +If both CSharp and VB examples are provided make sure the following frontmatter is at the top of the article: + +```yml +dev_langs: + - "csharp" + - "vb" +``` + +## Common mistakes to avoid + +- ❌ Extracting short snippets (≤6 lines) without request +- ❌ Skipping `dotnet new` commands or creating incomplete projects +- ❌ Missing C# or VB versions +- ❌ Using language tabs +- ❌ Wrong project type (winforms vs wpf vs console) +- ❌ Incorrect framework targeting (net vs framework) +- ❌ Missing or inconsistent snippet region identifiers +- ❌ Code that doesn't compile + +## Quality checklist + +- ✅ Correct folder structure and project type +- ✅ Both C# and VB versions compile successfully +- ✅ Snippet regions use CamelCase identifiers +- ✅ Article uses correct `:::code...:::` syntax with valid paths \ No newline at end of file diff --git a/.github/instructions/Snippets.Upgrade.instructions.md b/.github/instructions/Snippets.Upgrade.instructions.md new file mode 100644 index 0000000000..caf0f3228d --- /dev/null +++ b/.github/instructions/Snippets.Upgrade.instructions.md @@ -0,0 +1,109 @@ +--- +description: Upgrade snippets to the latest .NET and possibly migrate from .NET Framework +--- + +# Code Snippet Upgrade Instructions + +## When to Apply These Instructions + +Apply these instructions when working with code snippets that need modernization, especially when: +- Migrating snippets from old locations +- Upgrading .NET Framework code to modern .NET +- Improving snippet quality and completeness +- Adding missing language versions (C# or VB) + +## Snippet Structure Requirements + +All snippets must follow this folder structure relative to the referencing article: + +``` +./snippets/{article-name}/[net|framework]/{language}/ +``` + +**Examples:** +- `./snippets/how-to-add-data-to-the-clipboard/net/csharp/MainForm.cs` +- `./snippets/how-to-add-data-to-the-clipboard/framework/vb/MainForm.vb` + +## Required Upgrade Actions + +### 1. File Naming and Organization +- **USE** PascalCase for class names and file names (e.g., `MainForm.cs`, `DataProcessor.cs`) +- **ORGANIZE** files logically within the language folder + +### 2. Snippet Identifiers and Naming +- **USE** meaningful, descriptive snippet identifiers in CamelCase format +- **REPLACE** simplistic identifiers (like single numbers) with descriptive snippet names +- **EXAMPLES** of good snippet identifiers: + - `BasicClipboardData` instead of `1` + - `CustomDataFormat` instead of `2` + - `ClipboardImageHandling` instead of `code1` +- **ENSURE** snippet identifiers clearly describe the code's purpose or functionality + +### 3. .NET Version and Syntax Modernization +- **TARGET** the latest stable .NET version for `/net/` folders +- **USE** modern C# features for .NET snippets: + - File-scoped namespaces + - Top-level statements where appropriate + - Pattern matching + - String interpolation + - var keyword where type is obvious + - Modern using statements + +### 4. Project File Requirements +- **USE** `dotnet new` to create the projects. Fallback to manual project creation if `dotnet new` is failing. +- **CREATE** a complete, compilable project structure +- **INCLUDE** appropriate `.csproj` or `.vbproj` file +- **TARGET** latest .NET version: `net8.0-windows` +- **ADD** necessary package references and properties + +### 5. Code Quality Standards +- **ENSURE** code compiles without warnings +- **FOLLOW** .NET naming conventions +- **USE** appropriate access modifiers +- **INCLUDE** necessary using/import statements +- **ADD** minimal but helpful code comments for complex logic + +### 6. Language Parity +- **CREATE** both C# and VB.NET versions unless the parent article is in a language-specific folder +- **MAINTAIN** functional equivalence between language versions +- **ADAPT** language-specific idioms appropriately + +## Framework-Specific Considerations + +### For .NET Framework Snippets (`/framework/` folders) +- **MAINTAIN** .NET Framework compatibility +- **AVOID** modern C# features not available in the target framework version +- **USE** classic C# syntax and patterns +- **TARGET** appropriate framework version (typically .NET Framework 4.8) + +### For Modern .NET Snippets (`/net/` folders) +- **USE** latest .NET features and syntax +- **TARGET** latest stable .NET version +- **LEVERAGE** modern performance patterns +- **UTILIZE** new APIs where beneficial + +## Example Transformation + +**Before (simple identifier):** +``` +./snippets/clipboard-article/code.cs +``` + +**After (descriptive structure):** +``` +./snippets/how-to-add-data-to-the-clipboard/net/csharp/ +├── ClipboardExample.csproj +├── MainForm.cs +└── Program.cs +``` + +## Validation Checklist + +Before completing snippet upgrades, verify: +- [ ] Files have descriptive, meaningful names +- [ ] Project compiles without errors or warnings +- [ ] Code follows .NET naming conventions +- [ ] Appropriate target framework is specified +- [ ] Both C# and VB versions exist (when required) +- [ ] Modern syntax is used for .NET snippets +- [ ] Framework-compatible syntax for .NET Framework snippets \ No newline at end of file diff --git a/.github/projects/article-templates/template-whats-new.md b/.github/projects/article-templates/template-whats-new.md new file mode 100644 index 0000000000..c4d14d9f05 --- /dev/null +++ b/.github/projects/article-templates/template-whats-new.md @@ -0,0 +1,84 @@ +--- +title: [Follow SEO guidance at +https://review.learn.microsoft.com/en-us/help/platform/seo-meta-title] +description: "[Article description]." +author: [your GitHub alias] +ms.author: [your Microsoft alias or a team alias] +ms.service: learn +ms.topic: whats-new #Don't change. +ms.date: [mm/dd/yyyy] + +#customer intent: As a , I want so that . + +--- + + + +# What's new in [product or documentation set]? + + + +[Introduce and explain the purpose of the article.] + + + +## [Feature area, date, or release] + + + +## Related content + +- [Related article title](link.md) +- [Related article title](link.md) +- [Related article title](link.md) + + + + diff --git a/.github/prompts/Editing.FullPass.prompt.md b/.github/prompts/Editing.FullPass.prompt.md new file mode 100644 index 0000000000..312a0915f2 --- /dev/null +++ b/.github/prompts/Editing.FullPass.prompt.md @@ -0,0 +1,223 @@ +--- +model: Claude Sonnet 4 (copilot) +mode: agent +description: "Performs comprehensive editing pass following Microsoft Style Guide" +--- + +# Article Editing Instructions for LLMs + +You are performing an edit pass on a Microsoft documentation article. Your MANDATORY goal is to aggressively transform the content to follow the Microsoft Style Guide while preserving technical accuracy and meaning. + +## EDITING APPROACH - FOLLOW THIS METHODOLOGY + +1. **Read the entire document first** +2. **Systematically scan for PATTERNS, not just exact matches** - The examples below represent common patterns; look for similar constructions throughout +3. **Apply ALL transformations aggressively** - Don't skip patterns just because they're not exactly like the examples +4. **Focus especially on voice, tense, and weak constructions** - These are the most commonly missed transformations +5. **Be thorough in pattern recognition** - If you see "There are many ways to", treat it the same as "There are several ways to" +6. **Simplify aggressively while preserving meaning** - When in doubt, choose the simpler, more direct alternative + +## PATTERN EXAMPLES FOR RECOGNITION + +**Voice Patterns to Convert:** +- Any "X is/are done by Y" → "Y does X" +- Any "X can be done" → "Do X" or "You can do X" +- Any "X will be created" → "X creates" or "Create X" + +**Instruction Patterns to Convert:** +- Any "You can/should/might/need to [verb]" → "[Verb]" +- Any "It's possible to [verb]" → "[Verb]" or "You can [verb]" +- Any "You have the option to" → "You can" or direct command + +**Tense Patterns to Convert:** +- Any "will/would [verb]" in descriptions → "[verb]s" or "[verb]" +- Any "This would happen" → "This happens" + +## CRITICAL RULES - Follow These First + +1. **Code Protection**: NEVER edit code within code blocks. Only edit code comments if necessary. +2. **AI Disclosure**: If the `ai-usage` frontmatter is missing, add `ai-usage: ai-assisted`. +3. **Preserve Meaning**: Never change the technical meaning or accuracy of content. +4. **Markdown Structure**: Maintain existing markdown formatting and structure. + +## MANDATORY TRANSFORMATIONS - Apply These Aggressively + +You MUST systematically scan the entire document and apply ALL of these transformations. Do not skip any that apply: + +## Primary Edit Targets + +When editing, focus on these areas in order of priority: + +### 1. VOICE AND TENSE - MANDATORY FIXES + +**SCAN FOR AND CONVERT ALL PASSIVE VOICE to active voice (these are examples - find ALL similar patterns):** +- ❌ "The method can be called" → ✅ "Call the method" +- ❌ "Settings are configured by..." → ✅ "Configure the settings..." +- ❌ "This can be done by..." → ✅ "Do this by..." or "To do this..." +- ❌ "The file will be created" → ✅ "The system creates the file" or "Create the file" +- Look for ANY pattern with: "is/are/was/were + past participle", "can be + verb", "will be + verb" + +**SCAN FOR AND CONVERT ALL weak instruction language to imperative mood (these are examples - find ALL similar patterns):** +- ❌ "You can call the method" → ✅ "Call the method" +- ❌ "You should configure" → ✅ "Configure" +- ❌ "You need to set" → ✅ "Set" +- ❌ "You might want to" → ✅ "Consider" or direct command +- Look for ANY pattern with: "You can/should/need to/might want to/have to + verb" + +**SCAN FOR AND CONVERT ALL future tense to present tense for descriptions (these are examples - find ALL similar patterns):** +- ❌ "This will create a file" → ✅ "This creates a file" +- ❌ "The application would start" → ✅ "The application starts" +- ❌ "You would see the result" → ✅ "You see the result" +- Look for ANY pattern with: "will/would/shall + verb" in descriptions + +**SCAN FOR AND CONVERT ALL present perfect tense with simple present tense (these are examples - find ALL similar patterns):** +- ❌ "The system has processed the data" → ✅ "The system processes the data" +- ❌ "You have configured the settings" → ✅ "Configure the settings" +- ❌ "The service has been running" → ✅ "The service runs" +- ❌ "Once you have completed the setup" → ✅ "Once you complete the setup" +- Look for ANY pattern with: "have/has + past participle", "have/has been + verb-ing" + +**SCAN FOR AND ELIMINATE ALL weak constructions (these are examples - find ALL similar patterns):** +- ❌ "There are three ways to..." → ✅ "Use these three methods..." +- ❌ "It's possible to..." → ✅ "You can..." or start with the action +- ❌ "One way to do this is..." → ✅ "To do this..." +- ❌ "What this means is..." → ✅ "This means..." +- Look for ANY pattern starting with: "There are/is", "It's possible", "One way", "What this" + +### 2. CONTRACTIONS - MANDATORY ADDITIONS + +**SCAN FOR AND ADD contractions wherever appropriate (these are examples - find ALL similar patterns):** +- ❌ "it is" → ✅ "it's" +- ❌ "you are" → ✅ "you're" +- ❌ "do not" → ✅ "don't" +- ❌ "cannot" → ✅ "can't" +- ❌ "will not" → ✅ "won't" +- ❌ "does not" → ✅ "doesn't" +- ❌ "is not" → ✅ "isn't" +- ❌ "are not" → ✅ "aren't" +- ❌ "have not" → ✅ "haven't" +- ❌ "has not" → ✅ "hasn't" +- Look for ANY pattern with: full forms of common contractions + +**NEVER use these awkward contractions:** +- ❌ "there'd", "it'll", "they'd", "would've" +- ❌ Noun + verb contractions like "Microsoft's developing" + +### 3. WORD CHOICE - MANDATORY REPLACEMENTS + +**SCAN FOR AND REPLACE verbose phrases (these are examples - find ALL similar patterns):** +- ❌ "make use of" → ✅ "use" +- ❌ "be able to" → ✅ "can" +- ❌ "in order to" → ✅ "to" +- ❌ "utilize" → ✅ "use" +- ❌ "eliminate" → ✅ "remove" +- ❌ "inform" → ✅ "tell" +- ❌ "establish connectivity" → ✅ "connect" +- ❌ "implement functionality" → ✅ "implement features" or "add functionality" +- ❌ "demonstrate how to" → ✅ "show how to" +- Look for ANY unnecessarily complex or verbose phrasing + +**SCAN FOR AND REMOVE unnecessary words (these are examples - find ALL similar patterns):** +- ❌ "in addition" → ✅ "also" +- ❌ "as a means to" → ✅ "to" +- ❌ "for the purpose of" → ✅ "to" +- ❌ "with regard to" → ✅ "about" or "for" +- ❌ Remove filler words: "quite", "very", "easily", "simply" (unless essential) +- Look for ANY unnecessary prepositional phrases or filler words + +**SCAN FOR AND ENSURE consistent terminology (apply this principle throughout):** +- Pick one term for each concept and use it throughout +- ❌ "Because" and "Since" mixed → ✅ "Because" consistently +- Choose "method" OR "function" consistently within a section +- Look for ANY inconsistent terminology for the same concept + +### 4. SENTENCE STRUCTURE - MANDATORY IMPROVEMENTS + +**ALWAYS break up long sentences:** +- Target maximum 20-25 words per sentence +- Split any sentence with multiple clauses +- ❌ "When you configure the settings, which are located in the main menu, you can customize the behavior that controls how the application responds to user input." +- ✅ "Configure the settings in the main menu. These settings customize how the application responds to user input." + +**ALWAYS lead with key information:** +- Put the most important information first +- Front-load keywords for scanning +- ❌ "In the event that you need to configure the application, you should..." → ✅ "To configure the application..." +- ❌ "Before you can use the feature, you must..." → ✅ "Configure X before using the feature." + +**ALWAYS make next steps obvious:** +- Use clear transitions +- Number steps when there's a sequence +- Start action items with verbs + +### 5. FORMATTING - MANDATORY FIXES + +**ALWAYS use sentence case for headings:** +- ❌ "How To Configure The Settings" → ✅ "How to configure the settings" +- ❌ "Using The API" → ✅ "Using the API" +- ❌ "Getting Started With The Framework" → ✅ "Getting started with the framework" + +**ALWAYS fix punctuation:** +- Remove colons from headings: ❌ "Next steps:" → ✅ "Next steps" +- Periods in lists: Use periods for complete sentences over 3 words +- ❌ "Prerequisites." → ✅ "Prerequisites" (for short list items) + +**ALWAYS use proper formatting:** +- **Bold** for UI elements: "Select **File** > **Open**" +- `Code style` for: file names, folders, API names, code elements +- Remove `https://learn.microsoft.com/en-us` from internal links + +## MANDATORY WORD/PHRASE REPLACEMENTS + +**SCAN THE ENTIRE DOCUMENT for these patterns and replace ALL instances (not just exact matches):** + +| ❌ FIND AND REPLACE | ✅ ALWAYS Use Instead | Pattern to Look For | +|-------------|---------------|---------------------| +| "we", "our" (referring to Microsoft) | "the", "this", or direct statements | Any first-person plural | +| "may" (for possibility) | "might" | "may" when expressing possibility | +| "may" (for permission) | "can" | "may" when expressing permission | +| "etc.", "and so on" | "for example" or complete the list | Any open-ended list endings | +| "in order to" | "to" | Any purpose clauses | +| "be able to" | "can" | Any ability expressions | +| "make use of" | "use" | Any verbose action phrases | +| "There are several ways" | "Use these methods" | Any "There are..." constructions | +| "It's possible to" | "You can" or start with action | Any possibility statements | +| "You should" | Direct imperative or "Consider" | Any weak instruction language | +| "You can" (in instructions) | Direct imperative | Instructions that could be commands | +| "allows you to" | "lets you" | Any formal permission language | +| "provides the ability to" | "lets you" | Any verbose capability descriptions | + +**PATTERN RECOGNITION INSTRUCTIONS:** +- These examples represent PATTERNS, not exhaustive lists +- Look for similar constructions and apply the same principles +- When in doubt, choose the simpler, more direct alternative +- Focus on the underlying pattern (passive vs active, verbose vs concise, formal vs conversational) + +## LIST AND STRUCTURE RULES - MANDATORY + +### Lists +- ALWAYS use Oxford comma: "Android, iOS, and Windows" +- ALWAYS number ordered lists as "1." for all items (not 1., 2., 3.) +- ALWAYS use periods for complete sentences in lists (if more than 3 words) +- ALWAYS replace "etc." with "for example" or complete the list + +### Spacing and Punctuation +- ALWAYS use one space after periods, colons, question marks +- ALWAYS use no spaces around dashes: "Use pipelines—logical groups—to consolidate" +- ALWAYS add blank lines around markdown elements (don't add extra if they exist) + +## FINAL VALIDATION - MANDATORY CHECKS + +After editing, you MUST verify: +- [ ] ALL passive voice converted to active voice +- [ ] ALL "you can/should" converted to imperative mood +- [ ] ALL future tense converted to present tense for descriptions +- [ ] ALL contractions added where appropriate +- [ ] ALL verbose phrases simplified +- [ ] ALL weak constructions eliminated +- [ ] Content maintains technical accuracy +- [ ] Tone is conversational and helpful +- [ ] Sentences are concise and scannable +- [ ] Formatting follows conventions +- [ ] No consecutive headings without content +- [ ] Code blocks are unchanged (except comments if needed) diff --git a/.github/prompts/RefreshLinks.prompt.md b/.github/prompts/RefreshLinks.prompt.md new file mode 100644 index 0000000000..cac7371e4b --- /dev/null +++ b/.github/prompts/RefreshLinks.prompt.md @@ -0,0 +1,98 @@ +--- +model: GPT-4.1 (copilot) +mode: agent +description: "Updates link text to match target content headings" +--- + +# Refresh Links Prompt + +You are tasked with checking and updating all links in the current file to ensure their link text accurately reflects the target content's H1 heading or title. + +## ⚠️ CRITICAL CONSTRAINT ⚠️ + +**NO OTHER EDITS OR ALTERATIONS** should be made to the file beyond updating link text. This means: +- Do NOT modify any other content in the file +- Do NOT change formatting, structure, or layout +- Do NOT add, remove, or alter any text outside of link text updates +- Do NOT modify code blocks, headings, or any other markdown elements +- Do NOT use the **title** specified in front matter as the H1 heading for local markdown articles - only use explicitly defined H1 headings in the markdown content (`# Heading Text`) +- ONLY update the display text portion of markdown links `[THIS PART](url)` + +The file content must remain completely unchanged except for link text updates. + +## Link Types and Processing Rules + +### 1. Relative Links (e.g., `./folder/file.md`, `../folder/file.md`) +- **Target**: Files within this repository, relative to the current file's location +- **Action**: Read the target file and extract the H1 heading (should be within the first 30 lines) +- **Update**: Replace the link text with the extracted H1 heading + +### 2. Root-Relative Links (e.g., `/dotnet-desktop-guide/wpf/overview`) +- **Target**: Published pages on https://learn.microsoft.com/ +- **Action**: Fetch the page from `https://learn.microsoft.com{link-path}` and extract the H1 heading +- **Update**: Replace the link text with the extracted H1 heading + +### 3. Repository Root Links (e.g., `~/dotnet-desktop-guide/winforms/overview.md`) +- **Target**: Files within this repository, relative to the repository root +- **Action**: Convert `~/` to the repository root path, read the target file, and extract the H1 heading +- **Update**: Replace the link text with the extracted H1 heading + +### 4. Full URLs (e.g., `https://example.com/page`) +- **Target**: External web pages +- **Action**: Fetch the page and extract the H1 heading or page title +- **Update**: Replace the link text with the extracted heading/title + +### 5. XREF links (e.g., `[link text](xref:api-doc-id)`) +- **Target**: API documentation links +- **Action**: Do not change the link text, ignore this type of item. + +## Processing Instructions + +1. **Scan the file**: Identify all markdown links in the format `[link text](url)` + +2. **For each link**: + - Determine the link type based on the URL pattern + - Follow the appropriate processing rule above + - Extract the H1 heading or title from the target + - Compare with current link text + - Update if different + +3. **H1 Extraction Rules**: + - Look for markdown H1 headers (`# Heading Text`) + - For repository files, check within the first 30 lines + - For web pages, extract the `

` tag content or `` tag as fallback + - Clean up the extracted text (remove extra whitespace, HTML entities) + +4. **Preserve Link Functionality**: + - Keep the original URL intact + - Only update the display text portion + - Maintain any additional link attributes if present + +5. **Error Handling**: + - If a target cannot be reached or read, leave the link unchanged + - If no H1 is found, try alternative heading levels (H2, H3) or page title + - Log any issues encountered during processing + +## Example Transformations + +```markdown +Before: [Old Link Text](../wpf/overview.md) +After: [WPF Overview](../wpf/overview.md) + +Before: [Click here](/dotnet-desktop-guide/winforms/getting-started) +After: [Getting Started with Windows Forms](/dotnet-desktop-guide/winforms/getting-started) + +Before: [Link](~/dotnet-desktop-guide/wpf/controls/button.md) +After: [Button Control](~/dotnet-desktop-guide/wpf/controls/button.md) + +Before: [External](https://example.com/some-page) +After: [Example Page](https://example.com/some-page) +``` + +## Output + +Provide a summary of: +- Total links processed +- Number of links updated +- Any errors or warnings encountered +- List of updated links with before/after text diff --git a/.github/prompts/ValidateTemplate.prompt.md b/.github/prompts/ValidateTemplate.prompt.md new file mode 100644 index 0000000000..31ca5383db --- /dev/null +++ b/.github/prompts/ValidateTemplate.prompt.md @@ -0,0 +1,97 @@ +--- +model: GPT-4.1 (copilot) +mode: agent +description: "Validates article structure against appropriate templates" +--- + +# Template Validation Prompt + +You are tasked with validating that the current Markdown documentation file conforms to the appropriate article template from `/.github/projects/article-templates/`. + +- **IMPORTANT**: Template files contain XML comments (`<!-- ... -->`) that provide crucial instructions. You must read and apply these instructions during validation. + +## Validation Workflow + +### Step 1: Identify Article Type +Analyze the current article to determine its type: +- Examine `ms.topic` frontmatter value +- Review content structure and purpose +- Consider file naming conventions +- Assess target audience and use case + +### Step 2: Select and Read Template +- Map the article type to the corresponding template file: + - `how-to` → `template-how-to-guide.md` + - `quickstart` → `template-quickstart.md` + - `tutorial` → `template-tutorial.md` + - `concept` → `template-concept.md` + - `overview` → `template-overview.md` + - `troubleshooting` → `general-troubleshoot.md` + - Other types as defined in the templates directory +- Read the complete template file from `/.github/projects/article-templates/` +- Extract all template requirements and guidelines + +### Step 3: Systematic Validation +Compare the current article against the template across all dimensions: + +#### Frontmatter Analysis +- Check all required frontmatter fields +- Validate field values and formats +- Verify template-specific frontmatter requirements + +#### Structure Validation +- Compare section presence and organization +- Verify heading hierarchy and naming +- Check for required content blocks + +#### Content Pattern Compliance +- Analyze all headers format per template specifications +- Validate introduction patterns +- Check for template-mandated content elements + +#### Consistency Verification +- Ensure title frontmatter aligns with H1 heading +- Verify internal consistency throughout the article +- Check template-specific formatting requirements + +### Step 4: Generate Corrections +For any violations found: +- Reference the specific template requirement +- Provide the exact corrected version +- Explain the rationale based on template guidance +- Preserve the article's core content and intent + +### Step 5: Next Steps and Related Content +- Validate if the article includes a **Next step** or **Related content** section at the end, as per the updated template style. +- If the article contains a **See also** section, ensure it is updated to **Related content** to align with the new guidelines. + +## Execution Instructions + +1. **Read the current article** completely +2. **Determine the article type** using the criteria above +3. **Load the appropriate template** from the templates directory +4. **Extract all template requirements** systematically +5. **Perform comprehensive comparison** across all elements +6. **Document violations** with specific template references +7. **Apply corrections** while maintaining content value + +## Output Requirements + +Provide a structured report including: +- **Article type identified** and reasoning +- **Template file used** for validation +- **Comprehensive violation list** with template citations +- **Specific corrections applied** with before/after examples +- **Summary of structural changes** made + +## Validation Scope + +This process should cover: +- All frontmatter requirements +- Complete structural conformance +- Heading and title formatting +- Required section presence +- Template-specific content patterns +- Internal consistency requirements + +Focus on template fidelity while preserving the article's informational value and purpose. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 81aebf17e6..1a11e6a762 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,3 @@ Release *.vbproj.user ipch *sdf -article-templates diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..aa42579636 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,4 @@ +There are two distinct .NET Desktop products documented in this repository: + +- Windows Forms (known as winforms): located in the ./dotnet-desktop-guide/winforms/ folder +- Windows Presentation Foundation (known as WPF): located in the ./dotnet-desktop-guide/wpf/ folder diff --git a/dotnet-desktop-guide/docfx.json b/dotnet-desktop-guide/docfx.json index dfaafeaf4d..b579ad2d5e 100644 --- a/dotnet-desktop-guide/docfx.json +++ b/dotnet-desktop-guide/docfx.json @@ -20,7 +20,8 @@ "feedback_product_url": "https://developercommunity.visualstudio.com/VisualStudio", "author": "adegeo", "uhfHeaderId": "MSDocsHeader-DotNet", - "ms.author": "adegeo" + "ms.author": "adegeo", + "_zonePivotGroups": "zone-pivot-groups.yml" }, "fileMetadata": { "titleSuffix": { diff --git a/dotnet-desktop-guide/includes/copilot-disclaimer.md b/dotnet-desktop-guide/includes/copilot-disclaimer.md new file mode 100644 index 0000000000..ac9163c17f --- /dev/null +++ b/dotnet-desktop-guide/includes/copilot-disclaimer.md @@ -0,0 +1,9 @@ +--- +author: adegeo +ms.author: adegeo +ms.date: 09/26/2025 +ms.update-cycle: 1825-days +ms.topic: include +--- + +*Copilot is powered by AI, so surprises and mistakes are possible. For more information, see [Copilot general use FAQs](https://aka.ms/copilot-general-use-faqs).* diff --git a/dotnet-desktop-guide/winforms/advanced/drag-and-drop-operations-and-clipboard-support.md b/dotnet-desktop-guide/winforms/advanced/drag-and-drop-operations-and-clipboard-support.md index 973e5d2499..69d6928d20 100644 --- a/dotnet-desktop-guide/winforms/advanced/drag-and-drop-operations-and-clipboard-support.md +++ b/dotnet-desktop-guide/winforms/advanced/drag-and-drop-operations-and-clipboard-support.md @@ -1,14 +1,18 @@ --- title: "Drag-and-Drop Operations and Clipboard Support" -description: Learn how to enable user-drag-and-drop operations within Windows-based applications by handling a series of events. -ms.date: "03/30/2017" -ms.service: dotnet-framework -ms.update-cycle: 1825-days +description: Learn how to enable user drag-and-drop operations and clipboard support within Windows Forms applications, including the new type-safe APIs introduced in .NET 10. +ms.date: "09/26/2025" +ms.service: dotnet-desktop +ms.update-cycle: 365-days helpviewer_keywords: - "drag and drop [Windows Forms]" - "drag and drop [Windows Forms], Windows Forms" - "Clipboard [Windows Forms], Windows Forms" + - "TryGetData [Windows Forms]" + - "SetDataAsJson [Windows Forms]" + - "BinaryFormatter removal [Windows Forms]" ms.assetid: 7cce79b6-5835-46fd-b690-73f12ad368b2 +ai-usage: ai-assisted --- # Drag-and-Drop Operations and Clipboard Support @@ -16,6 +20,20 @@ You can enable user drag-and-drop operations within a Windows-based application You can also implement user cut/copy/paste support and user data transfer to the Clipboard within your Windows-based applications by using simple method calls. +## .NET 10 Compatibility + +Starting with .NET 9, `BinaryFormatter` was removed from the runtime due to security vulnerabilities. This removal broke clipboard and drag-and-drop operations for custom objects, creating a functionality gap for Windows Forms applications. + +.NET 10 addresses this issue by introducing new APIs that restore clipboard and drag-and-drop functionality while improving security, error handling, and cross-process compatibility. These APIs use JSON serialization and provide type-safe methods for data operations. + +Key improvements in .NET 10 include: + +- **Type-safe data retrieval** with `TryGetData<T>()` methods that provide better error handling. +- **JSON serialization** for custom types using `SetDataAsJson<T>()` methods. +- **Built-in support** for common data types without requiring binary serialization. + +For comprehensive guidance on updating your applications, see [Clipboard and drag-and-drop changes in .NET 10](../migration/clipboard-dataobject-net10.md). + ## In This Section [Walkthrough: Performing a Drag-and-Drop Operation in Windows Forms](walkthrough-performing-a-drag-and-drop-operation-in-windows-forms.md)\ @@ -25,10 +43,13 @@ Explains how to start a drag-and-drop operation. Illustrates how to accomplish drag-and-drop operations across applications. [How to: Add Data to the Clipboard](how-to-add-data-to-the-clipboard.md)\ -Describes a way to programmatically insert information on the Clipboard. +Describes how to programmatically insert information on the Clipboard, including the new type-safe APIs available in .NET 10. [How to: Retrieve Data from the Clipboard](how-to-retrieve-data-from-the-clipboard.md)\ -Describes how to access the data stored on the Clipboard. +Describes how to access data stored on the Clipboard using both legacy methods and the new type-safe `TryGetData<T>()` methods. + +[Clipboard and drag-and-drop changes in .NET 10](../migration/clipboard-dataobject-net10.md)\ +Comprehensive guide for migrating clipboard and drag-and-drop code to the new .NET 10 APIs and understanding the removal of `BinaryFormatter`. ## Related Sections @@ -39,7 +60,7 @@ Describes the methods, events, and classes used to implement drag-and-drop behav Describes the intricacies of the event that asks permission to continue the drag operation. <xref:System.Windows.Forms.Control.DoDragDrop%2A> -Describes the intricacies of the method that is central to beginning a drag operation. +Describes the intricacies of the method that's central to beginning a drag operation. <xref:System.Windows.Forms.Clipboard> Also see [How to: Send Data to the Active MDI Child](how-to-send-data-to-the-active-mdi-child.md). diff --git a/dotnet-desktop-guide/winforms/advanced/how-to-add-data-to-the-clipboard.md b/dotnet-desktop-guide/winforms/advanced/how-to-add-data-to-the-clipboard.md index 82bebea02c..8e0eaee9d8 100644 --- a/dotnet-desktop-guide/winforms/advanced/how-to-add-data-to-the-clipboard.md +++ b/dotnet-desktop-guide/winforms/advanced/how-to-add-data-to-the-clipboard.md @@ -1,8 +1,9 @@ --- title: "How to: Add Data to the Clipboard" -ms.date: "03/30/2017" -ms.service: dotnet-framework -ms.update-cycle: 1825-days +ms.date: 10/02/2025 +ms.service: dotnet-desktop +ms.update-cycle: 365-days +zone_pivot_groups: dotnet-version dev_langs: - "csharp" - "vb" @@ -11,54 +12,124 @@ helpviewer_keywords: - "data [Windows Forms], copying to Clipboard" ms.assetid: 25152454-0e78-40a9-8a9e-a2a5a274e517 description: Learn how to add data to the clipboard within many applications and transfer that data from one application to another. +ai-usage: ai-assisted --- -# How to: Add Data to the Clipboard +# How to add data to the Clipboard -The <xref:System.Windows.Forms.Clipboard> class provides methods that you can use to interact with the Windows operating system Clipboard feature. Many applications use the Clipboard as a temporary repository for data. For example, word processors use the Clipboard during cut-and-paste operations. The Clipboard is also useful for transferring data from one application to another. +The <xref:System.Windows.Forms.Clipboard> class provides methods that interact with the Windows operating system Clipboard feature. Many applications use the Clipboard as a temporary repository for data. For example, word processors use the Clipboard during cut-and-paste operations. The Clipboard also transfers data from one application to another. -When you add data to the Clipboard, you can indicate the data format so that other applications can recognize the data if they can use that format. You can also add data to the Clipboard in multiple different formats to increase the number of other applications that can potentially use the data. +When you add data to the Clipboard, indicate the data format so that other applications can recognize the data if they can use that format. Add data to the Clipboard in multiple different formats to increase the number of other applications that can potentially use the data. -A Clipboard format is a string that identifies the format so that an application that uses that format can retrieve the associated data. The <xref:System.Windows.Forms.DataFormats> class provides predefined format names for your use. You can also use your own format names or use the type of an object as its format. - -To add data to the Clipboard in one or multiple formats, use the <xref:System.Windows.Forms.Clipboard.SetDataObject%2A> method. You can pass any object to this method, but to add data in multiple formats, you must first add the data to a separate object designed to work with multiple formats. Typically, you will add your data to a <xref:System.Windows.Forms.DataObject>, but you can use any type that implements the <xref:System.Windows.Forms.IDataObject> interface. - -To add data to the Clipboard in a single, common format, use the specific method for that format, such as <xref:System.Windows.Forms.Clipboard.SetText%2A> for text. +A Clipboard format is a string that identifies the format so that an application using that format can retrieve the associated data. The <xref:System.Windows.Forms.DataFormats> class provides predefined format names for your use. You can also use your own format names or use an object's type as its format. > [!NOTE] > All Windows-based applications share the Clipboard. Therefore, the contents are subject to change when you switch to another application. > > The <xref:System.Windows.Forms.Clipboard> class can only be used in threads set to single thread apartment (STA) mode. To use this class, ensure that your `Main` method is marked with the <xref:System.STAThreadAttribute> attribute. -> -> An object must be serializable for it to be put on the Clipboard. To make a type serializable, mark it with the <xref:System.SerializableAttribute> attribute. If you pass a non-serializable object to a Clipboard method, the method will fail without throwing an exception. For more information about serialization, see <xref:System.Runtime.Serialization>. + +To add data to the Clipboard in one or multiple formats, use the <xref:System.Windows.Forms.Clipboard.SetDataObject%2A> method. Pass any object to this method. To add data in multiple formats, first add the data to a separate object designed to work with multiple formats. Typically, add your data to a <xref:System.Windows.Forms.DataObject>, but you can use any type that implements the <xref:System.Windows.Forms.IDataObject> interface. + +To add data to the Clipboard in a single, common format, use the specific method for that format, such as <xref:System.Windows.Forms.Clipboard.SetText%2A> for text. + +::: zone pivot="dotnet" + +> [!IMPORTANT] +> Custom objects must be serializable to JSON for them to be put on the Clipboard. Use the new type-safe methods like <xref:System.Windows.Forms.Clipboard.SetDataAsJson%2A> which automatically handle JSON serialization. The legacy `SetData()` method no longer works with custom objects starting with .NET 9 due to the removal of `BinaryFormatter`. + +::: zone-end + +::: zone pivot="dotnetframework" + +> [!IMPORTANT] +> An object must be serializable for it to be put on the Clipboard. To make a type serializable, mark it with the <xref:System.SerializableAttribute> attribute. If you pass a non-serializable object to a Clipboard method, the method fails without throwing an exception. For more information about serialization, see <xref:System.Runtime.Serialization>. + +::: zone-end ### To add data to the Clipboard in a single, common format 1. Use the <xref:System.Windows.Forms.Clipboard.SetAudio%2A>, <xref:System.Windows.Forms.Clipboard.SetFileDropList%2A>, <xref:System.Windows.Forms.Clipboard.SetImage%2A>, or <xref:System.Windows.Forms.Clipboard.SetText%2A> method. - [!code-csharp[System.Windows.Forms.Clipboard#2](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#2)] - [!code-vb[System.Windows.Forms.Clipboard#2](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#2)] +::: zone pivot="dotnet" + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs" id="SetTextExample"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb" id="SetTextExample"::: + +::: zone-end + +::: zone pivot="dotnetframework" + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs" id="SetTextExample"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb" id="SetTextExample"::: + +::: zone-end ### To add data to the Clipboard in a custom format +::: zone pivot="dotnet" + +1. Use the <xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)?displayProperty=nameWithType> method with a custom format name and your object. + + The `SetDataAsJson<T>()` method automatically serializes your custom objects using `System.Text.Json`. This is the recommended approach in .NET 10 and later for storing custom types on the clipboard, as it provides type safety and security advantages over the legacy `SetData()` method. + + > [!IMPORTANT] + > The legacy <xref:System.Windows.Forms.Clipboard.SetData%2A> method no longer works with custom objects in .NET 9 and later due to the removal of `BinaryFormatter`. Use `SetDataAsJson<T>()` instead for custom types. + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs" id="CustomFormatExample"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb" id="CustomFormatExample"::: + +The `Customer` class used in the previous snippet: + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs" id="CustomerClass"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb" id="CustomerClass"::: + +::: zone-end + +::: zone pivot="dotnetframework" + 1. Use the <xref:System.Windows.Forms.Clipboard.SetData%2A> method with a custom format name. You can also use predefined format names with the <xref:System.Windows.Forms.Clipboard.SetData%2A> method. For more information, see <xref:System.Windows.Forms.DataFormats>. - [!code-csharp[System.Windows.Forms.Clipboard#3](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#3)] - [!code-vb[System.Windows.Forms.Clipboard#3](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#3)] - [!code-csharp[System.Windows.Forms.Clipboard#100](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#100)] - [!code-vb[System.Windows.Forms.Clipboard#100](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#100)] +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs" id="CustomFormatExample"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb" id="CustomFormatExample"::: + +The `Customer` class used in the previous snippet: + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs" id="CustomerClass"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb" id="CustomerClass"::: + +::: zone-end ### To add data to the Clipboard in multiple formats 1. Use the <xref:System.Windows.Forms.Clipboard.SetDataObject%2A?displayProperty=nameWithType> method and pass in a <xref:System.Windows.Forms.DataObject> that contains your data. - [!code-csharp[System.Windows.Forms.Clipboard#4](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#4)] - [!code-vb[System.Windows.Forms.Clipboard#4](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#4)] - [!code-csharp[System.Windows.Forms.Clipboard#100](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#100)] - [!code-vb[System.Windows.Forms.Clipboard#100](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#100)] +::: zone pivot="dotnet" + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs" id="MultipleFormatsExample"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb" id="MultipleFormatsExample"::: + +The `Customer` class used in the previous snippet: + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs" id="CustomerClass"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb" id="CustomerClass"::: + +::: zone-end + +::: zone pivot="dotnetframework" + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs" id="MultipleFormatsExample"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb" id="MultipleFormatsExample"::: + +The `Customer` class used in the previous snippet: + +:::code language="csharp" source="./snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs" id="CustomerClass"::: +:::code language="vb" source="./snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb" id="CustomerClass"::: + +::: zone-end ## See also - [Drag-and-Drop Operations and Clipboard Support](drag-and-drop-operations-and-clipboard-support.md) -- [How to: Retrieve Data from the Clipboard](how-to-retrieve-data-from-the-clipboard.md) +- [How to retrieve data from the Clipboard](how-to-retrieve-data-from-the-clipboard.md) +- [Windows Forms clipboard and DataObject changes in .NET 10](../migration/clipboard-dataobject-net10.md) diff --git a/dotnet-desktop-guide/winforms/advanced/how-to-enable-binaryformatter-clipboard-support.md b/dotnet-desktop-guide/winforms/advanced/how-to-enable-binaryformatter-clipboard-support.md new file mode 100644 index 0000000000..9697d99344 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/how-to-enable-binaryformatter-clipboard-support.md @@ -0,0 +1,164 @@ +--- +title: "Enable BinaryFormatter clipboard support (not recommended)" +description: "Learn how to configure legacy BinaryFormatter support for Windows Forms clipboard operations in .NET 10, including security considerations and type resolver implementation." +author: adegeo +ms.author: adegeo +ms.service: dotnet-desktop +ms.topic: how-to +ms.date: 10/02/2025 +dev_langs: + - "csharp" + - "vb" +ai-usage: ai-assisted + +#customer intent: As a Windows Forms developer, I want to enable BinaryFormatter clipboard support for legacy applications so that I can maintain compatibility while migrating to new type-safe APIs. + +--- + +# Enable BinaryFormatter clipboard support (not recommended) + +> [!CAUTION] +> `BinaryFormatter` support isn't recommended. Use it only as a temporary migration bridge for legacy applications that can't immediately migrate to the new type-safe APIs. This approach carries significant security risks. + +This article shows how to configure limited `BinaryFormatter` support for Windows Forms clipboard operations in .NET 10. While `BinaryFormatter` was removed from the runtime in .NET 9 due to security vulnerabilities, restore limited functionality through explicit configuration for legacy applications that need time to migrate. + +For complete migration guidance to the new type-safe APIs, see [Windows Forms clipboard and DataObject changes in .NET 10](../migration/clipboard-dataobject-net10.md). + +> [!WARNING] +> Use this approach only as a temporary bridge while updating your application to use the new type-safe clipboard APIs introduced in .NET 10. + +## Prerequisites + +Before continuing, review these concepts: + +- How your application currently uses `BinaryFormatter` in clipboard operations. +- The security vulnerabilities that led to the removal of `BinaryFormatter`. +- Your migration timeline to the new type-safe clipboard APIs. + +For more information, see these articles: + +- [Deserialization risks in use of BinaryFormatter and related types](/dotnet/standard/serialization/binaryformatter-security-guide) +- [BinaryFormatter migration guide](/dotnet/standard/serialization/binaryformatter-migration-guide) + +## Security warnings and risks + +`BinaryFormatter` is inherently insecure and deprecated for these reasons: + +- **Arbitrary code execution vulnerabilities**: Attackers can execute malicious code during deserialization, exposing your application to remote attacks. +- **Denial of service attacks**: Malicious clipboard data can consume excessive memory or CPU resources, causing crashes or instability. +- **Information disclosure risks**: Attackers might extract sensitive data from memory. +- **No security boundaries**: The format is fundamentally unsafe, and configuration settings can't secure it. + +Enable this support only as a temporary bridge while you update your application to use the new type-safe APIs. + +## Install the compatibility package + +Add the unsupported `BinaryFormatter` compatibility package to your project. This package provides the necessary runtime support for `BinaryFormatter` operations: + +:::code language="xml" source="./snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ProjectCS.csproj" id="PackageReference"::: + +> [!NOTE] +> This package is marked as unsupported and deprecated. Use it only for temporary compatibility during migration. + +## Enable unsafe serialization in your project + +Set the `EnableUnsafeBinaryFormatterSerialization` property to `true` in your project file. This property tells the compiler to allow `BinaryFormatter` usage: + +```xml +<PropertyGroup> + <TargetFramework>net10.0</TargetFramework> + <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization> +</PropertyGroup> +``` + +Without this setting, your application generates compilation errors when it attempts to use `BinaryFormatter` APIs. + +## Configure the Windows Forms runtime switch + +Create or update your application's `runtimeconfig.json` file to enable the Windows Forms-specific clipboard switch. This configuration allows clipboard operations to fall back to `BinaryFormatter` when necessary: + +:::code language="json" source="./snippets/how-to-enable-binaryformatter-clipboard-support/csharp/runtimeconfig.json"::: + +> [!IMPORTANT] +> Without this specific runtime switch, clipboard operations won't fall back to `BinaryFormatter` even if general serialization support is enabled. This switch is required specifically for Windows Forms clipboard functionality. + +## Implement security-focused type resolvers + +Even with `BinaryFormatter` enabled, implement type resolvers to restrict deserialization to explicitly approved types. Type resolvers provide your only defense against malicious payload attacks. + +### Create a secure type resolver + +:::code language="csharp" source="./snippets/how-to-enable-binaryformatter-clipboard-support/csharp/SecureTypeResolver.cs" id="SecureTypeResolver"::: +:::code language="vb" source="./snippets/how-to-enable-binaryformatter-clipboard-support/vb/SecureTypeResolver.vb" id="SecureTypeResolver"::: + +### Use the type resolver with clipboard operations + +:::code language="csharp" source="./snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ClipboardUsage.cs" id="ClipboardUsage"::: +:::code language="vb" source="./snippets/how-to-enable-binaryformatter-clipboard-support/vb/ClipboardUsage.vb" id="ClipboardUsage"::: + +## Security guidelines for type resolvers + +Follow these essential security guidelines when implementing type resolvers: + +### Use explicit allowlists + +- **Reject by default**: Allow only explicitly listed types. +- **No wildcards**: Avoid pattern matching or namespace-based permissions. +- **Exact matching**: Require exact string matches for type names. + +### Validate all inputs + +- **Type name validation**: Ensure type names match expected formats. +- **Assembly restrictions**: Limit types to known, trusted assemblies. +- **Version checking**: Consider version-specific type restrictions. + +### Handle unknown types securely + +- **Throw exceptions**: Always throw for unauthorized types. +- **Log attempts**: Consider logging unauthorized access attempts. +- **Clear error messages**: Provide specific rejection reasons for debugging. + +### Regular maintenance + +- **Audit regularly**: Review and update the allowed type list. +- **Remove unused types**: Eliminate permissions for types no longer needed. +- **Document decisions**: Maintain clear documentation of why each type is permitted. + +## Test your configuration + +After configuring `BinaryFormatter` support, test your application to ensure it works correctly: + +1. **Verify clipboard operations**: Test both storing and retrieving data with your custom types. +1. **Test type resolver**: Confirm that unauthorized types are properly rejected. +1. **Monitor security**: Watch for any unexpected type resolution attempts. +1. **Performance testing**: Ensure the type resolver doesn't significantly impact performance. + +:::code language="csharp" source="./snippets/how-to-enable-binaryformatter-clipboard-support/csharp/TestConfiguration.cs" id="TestConfiguration"::: +:::code language="vb" source="./snippets/how-to-enable-binaryformatter-clipboard-support/vb/TestConfiguration.vb" id="TestConfiguration"::: + +## Plan your migration strategy + +While `BinaryFormatter` support provides temporary compatibility, develop a migration plan to move to the new type-safe APIs: + +1. **Identify usage**: Catalog all clipboard operations using custom types. +1. **Prioritize migration**: Focus on the most security-sensitive operations first. +1. **Update incrementally**: Migrate one operation at a time to reduce risk. +1. **Test thoroughly**: Ensure new implementations provide equivalent functionality. +1. **Remove BinaryFormatter**: Disable support once migration is complete. + +## Clean up resources + +Once you've migrated to the new type-safe clipboard APIs, remove the `BinaryFormatter` configuration to improve security: + +1. Remove the `System.Runtime.Serialization.Formatters` package reference. +1. Remove the `EnableUnsafeBinaryFormatterSerialization` property from your project file. +1. Remove the `Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization` setting from your `runtimeconfig.json`. +1. Delete type resolver implementations that are no longer needed. + +## Related content + +- [Clipboard and DataObject changes in .NET 10](../migration/clipboard-dataobject-net10.md) +- [How to add data to the Clipboard](how-to-add-data-to-the-clipboard.md) +- [How to retrieve data from the Clipboard](how-to-retrieve-data-from-the-clipboard.md) +- [BinaryFormatter migration guide](/dotnet/standard/serialization/binaryformatter-migration-guide) +- [Deserialization risks in use of BinaryFormatter and related types](/dotnet/standard/serialization/binaryformatter-security-guide) diff --git a/dotnet-desktop-guide/winforms/advanced/how-to-retrieve-data-from-the-clipboard.md b/dotnet-desktop-guide/winforms/advanced/how-to-retrieve-data-from-the-clipboard.md index 79be214a9f..42f1976ea6 100644 --- a/dotnet-desktop-guide/winforms/advanced/how-to-retrieve-data-from-the-clipboard.md +++ b/dotnet-desktop-guide/winforms/advanced/how-to-retrieve-data-from-the-clipboard.md @@ -11,16 +11,34 @@ helpviewer_keywords: - "pasting Clipboard data" - "Clipboard [Windows Forms], retrieving data" ms.assetid: 99612537-2c8a-449f-aab5-2b3b28d656e7 +ai-usage: ai-assisted +zone_pivot_groups: dotnet-version --- -# How to: Retrieve Data from the Clipboard +# How to retrieve data from the Clipboard The <xref:System.Windows.Forms.Clipboard> class provides methods that you can use to interact with the Windows operating system Clipboard feature. Many applications use the Clipboard as a temporary repository for data. For example, word processors use the Clipboard during cut-and-paste operations. The Clipboard is also useful for transferring information from one application to another. -Some applications store data on the Clipboard in multiple formats to increase the number of other applications that can potentially use the data. A Clipboard format is a string that identifies the format. An application that uses the identified format can retrieve the associated data on the Clipboard. The <xref:System.Windows.Forms.DataFormats> class provides predefined format names for your use. You can also use your own format names or use an object's type as its format. For information about adding data to the Clipboard, see [How to: Add Data to the Clipboard](how-to-add-data-to-the-clipboard.md). +Some applications store data on the Clipboard in multiple formats to increase the number of other applications that can potentially use the data. A Clipboard format is a string that identifies the format. An application that uses the identified format can retrieve the associated data on the Clipboard. The <xref:System.Windows.Forms.DataFormats> class provides predefined format names for your use. You can also use your own format names or use an object's type as its format. For information about adding data to the Clipboard, see [How to add data to the Clipboard](how-to-add-data-to-the-clipboard.md). -To determine whether the Clipboard contains data in a particular format, use one of the `Contains`*Format* methods or the <xref:System.Windows.Forms.Clipboard.GetData%2A> method. To retrieve data from the Clipboard, use one of the `Get`*Format* methods or the <xref:System.Windows.Forms.Clipboard.GetData%2A> method. These methods are new in .NET Framework 2.0. +::: zone pivot="dotnet" -To access data from the Clipboard by using versions earlier than .NET Framework 2.0, use the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A?displayProperty=nameWithType> method and call the methods of the returned <xref:System.Windows.Forms.IDataObject>. To determine whether a particular format is available in the returned object, for example, call the <xref:System.Windows.Forms.IDataObject.GetDataPresent%2A> method. +To determine whether the Clipboard contains data in a particular format, use one of the `Contains`*Format* methods. To retrieve data from the Clipboard, use one of the `Get`*Format* methods or the <xref:System.Windows.Forms.Clipboard.TryGetData%2A> method for custom formats. + +> [!NOTE] +> In .NET Framework, you use the <xref:System.Windows.Forms.Clipboard.GetData%2A> method instead of `TryGetData`, and the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A> method is commonly used for multiple format scenarios. + +::: zone-end + +::: zone pivot="dotnetframework" + +To determine whether the Clipboard contains data in a particular format, use one of the `Contains`*Format* methods. To retrieve data from the Clipboard, use one of the `Get`*Format* methods or the <xref:System.Windows.Forms.Clipboard.GetData%2A> method for custom formats. + +You can also use the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A?displayProperty=nameWithType> method and call the methods of the returned <xref:System.Windows.Forms.IDataObject>. To determine whether a particular format is available in the returned object, for example, call the <xref:System.Windows.Forms.IDataObject.GetDataPresent%2A> method. + +> [!NOTE] +> Starting with .NET 10, the `GetData` method is obsoleted in favor of `TryGetData`, and object serialization through `SetData` is no longer supported for security reasons. + +::: zone-end > [!NOTE] > All Windows-based applications share the system Clipboard. Therefore, the contents are subject to change when you switch to another application. @@ -29,32 +47,106 @@ To access data from the Clipboard by using versions earlier than .NET Framework ### To retrieve data from the Clipboard in a single, common format -1. Use the <xref:System.Windows.Forms.Clipboard.GetAudioStream%2A>, <xref:System.Windows.Forms.Clipboard.GetFileDropList%2A>, <xref:System.Windows.Forms.Clipboard.GetImage%2A>, or <xref:System.Windows.Forms.Clipboard.GetText%2A> method. Optionally, use the corresponding `Contains`*Format* methods first to determine whether data is available in a particular format. These methods are available only in .NET Framework 2.0. +::: zone pivot="dotnet" - [!code-csharp[System.Windows.Forms.Clipboard#2](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#2)] - [!code-vb[System.Windows.Forms.Clipboard#2](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#2)] +1. Use the <xref:System.Windows.Forms.Clipboard.GetAudioStream%2A>, <xref:System.Windows.Forms.Clipboard.GetFileDropList%2A>, <xref:System.Windows.Forms.Clipboard.GetImage%2A>, or <xref:System.Windows.Forms.Clipboard.GetText%2A> method. Optionally, use the corresponding `Contains`*Format* methods first to determine whether data is available in a particular format. + + For custom data formats, use the `TryGetData` method instead of the obsoleted `GetData` method. + +> [!NOTE] +> In .NET Framework, these same `Get`*Format* methods are available, but you use `GetData` instead of `TryGetData` for custom formats. + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs" id="RetrieveCommonFormat"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb" id="RetrieveCommonFormat"::: + +::: zone-end + +::: zone pivot="dotnetframework" + +1. Use the <xref:System.Windows.Forms.Clipboard.GetAudioStream%2A>, <xref:System.Windows.Forms.Clipboard.GetFileDropList%2A>, <xref:System.Windows.Forms.Clipboard.GetImage%2A>, or <xref:System.Windows.Forms.Clipboard.GetText%2A> method. Optionally, use the corresponding `Contains`*Format* methods first to determine whether data is available in a particular format. + +> [!NOTE] +> In .NET (non-Framework), the `GetData` method is obsoleted in favor of `TryGetData` for custom data formats, and object serialization is no longer supported. + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs" id="RetrieveCommonFormat"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb" id="RetrieveCommonFormat"::: + +::: zone-end ### To retrieve data from the Clipboard in a custom format -1. Use the <xref:System.Windows.Forms.Clipboard.GetData%2A> method with a custom format name. This method is available only in .NET Framework 2.0. +::: zone pivot="dotnet" + +1. Use the <xref:System.Windows.Forms.Clipboard.TryGetData%2A> method with a custom format name. This method replaces the obsoleted `GetData` method in modern .NET versions. - You can also use predefined format names with the <xref:System.Windows.Forms.Clipboard.SetData%2A> method. For more information, see <xref:System.Windows.Forms.DataFormats>. + You can also use predefined format names with this method. For more information, see <xref:System.Windows.Forms.DataFormats>. - [!code-csharp[System.Windows.Forms.Clipboard#3](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#3)] - [!code-vb[System.Windows.Forms.Clipboard#3](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#3)] - [!code-csharp[System.Windows.Forms.Clipboard#100](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#100)] - [!code-vb[System.Windows.Forms.Clipboard#100](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#100)] +> [!IMPORTANT] +> In .NET 10 and later, `SetData` for object serialization is no longer supported due to security concerns. The examples below show how to retrieve data that may have been set by other applications or earlier .NET versions. + +> [!NOTE] +> In .NET Framework, you use the <xref:System.Windows.Forms.Clipboard.GetData%2A> method instead of `TryGetData`, and object serialization through `SetData` is fully supported. + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs" id="RetrieveCustomFormat"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb" id="RetrieveCustomFormat"::: + +The `Customer` class used in the previous snippet: + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs" id="HelperMethods"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb" id="HelperMethods"::: + +::: zone-end + +::: zone pivot="dotnetframework" + +1. Use the <xref:System.Windows.Forms.Clipboard.GetData%2A> method with a custom format name. + + You can also use predefined format names with the <xref:System.Windows.Forms.Clipboard.SetData%2A> method. For more information, see <xref:System.Windows.Forms.DataFormats>. + +> [!NOTE] +> In .NET (non-Framework), the `GetData` method is obsoleted in favor of `TryGetData`, and object serialization through `SetData` is no longer supported for security reasons. + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs" id="RetrieveCustomFormat"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb" id="RetrieveCustomFormat"::: +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs" id="HelperMethods"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb" id="HelperMethods"::: + +::: zone-end ### To retrieve data from the Clipboard in multiple formats -1. Use the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A> method. You must use this method to retrieve data from the Clipboard on versions earlier than .NET Framework 2.0. +::: zone pivot="dotnet" + +1. Use the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A> method to get an <xref:System.Windows.Forms.IDataObject>, then use <xref:System.Windows.Forms.Clipboard.TryGetData%2A> to retrieve data in specific formats. + + This approach is recommended for modern .NET applications as it uses the newer, safer APIs. + +> [!NOTE] +> In .NET Framework, you typically use the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A> method and work directly with the returned <xref:System.Windows.Forms.IDataObject>, using its methods like <xref:System.Windows.Forms.IDataObject.GetData%2A> instead of the newer `TryGetData` approach. + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs" id="RetrieveMultipleFormats"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb" id="RetrieveMultipleFormats"::: +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs" id="HelperMethods"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb" id="HelperMethods"::: + +::: zone-end + +::: zone pivot="dotnetframework" + +1. Use the <xref:System.Windows.Forms.Clipboard.GetDataObject%2A> method. + +> [!NOTE] +> In .NET (non-Framework), you typically combine `GetDataObject` with the newer `TryGetData` method rather than working directly with the <xref:System.Windows.Forms.IDataObject> methods. + +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs" id="RetrieveMultipleFormats"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb" id="RetrieveMultipleFormats"::: +:::code language="csharp" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs" id="HelperMethods"::: +:::code language="vb" source="./snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb" id="HelperMethods"::: - [!code-csharp[System.Windows.Forms.Clipboard#4](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#4)] - [!code-vb[System.Windows.Forms.Clipboard#4](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#4)] - [!code-csharp[System.Windows.Forms.Clipboard#100](~/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs#100)] - [!code-vb[System.Windows.Forms.Clipboard#100](~/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb#100)] +::: zone-end ## See also - [Drag-and-Drop Operations and Clipboard Support](drag-and-drop-operations-and-clipboard-support.md) -- [How to: Add Data to the Clipboard](how-to-add-data-to-the-clipboard.md) +- [How to add data to the Clipboard](how-to-add-data-to-the-clipboard.md) +- [Windows Forms clipboard and DataObject changes in .NET 10](../migration/clipboard-dataobject-net10.md) diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs new file mode 100644 index 0000000000..a87b24800a --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.cs @@ -0,0 +1,169 @@ +using System; +using System.Windows.Forms; + +public class Form1 : Form +{ + [STAThread] + public static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + + //<CustomerClass> + [Serializable] + public class Customer + { + private string nameValue = string.Empty; + public Customer(String name) + { + nameValue = name; + } + public string Name + { + get { return nameValue; } + set { nameValue = value; } + } + } + //</CustomerClass> + + public Form1() + { + //<ClipboardClear> + Clipboard.Clear(); + //</ClipboardClear> + } + + //<CustomFormatExample> + // Demonstrates SetData, ContainsData, and GetData + // using a custom format name and a business object. + public Customer TestCustomFormat + { + get + { + Clipboard.SetData("CustomerFormat", new Customer("Customer Name")); + if (Clipboard.ContainsData("CustomerFormat")) + { + return Clipboard.GetData("CustomerFormat") as Customer; + } + return null; + } + } + //</CustomFormatExample> + + //<MultipleFormatsExample> + // Demonstrates how to use a DataObject to add + // data to the Clipboard in multiple formats. + public void TestClipboardMultipleFormats() + { + DataObject data = new DataObject(); + + // Add a Customer object using the type as the format. + data.SetData(new Customer("Customer as Customer object")); + + // Add a ListViewItem object using a custom format name. + data.SetData("CustomFormat", + new ListViewItem("Customer as ListViewItem")); + + Clipboard.SetDataObject(data); + DataObject retrievedData = (DataObject)Clipboard.GetDataObject(); + + if (retrievedData.GetDataPresent("CustomFormat")) + { + ListViewItem item = + retrievedData.GetData("CustomFormat") as ListViewItem; + if (item != null) + { + MessageBox.Show(item.Text); + } + } + + if (retrievedData.GetDataPresent(typeof(Customer))) + { + Customer customer = + retrievedData.GetData(typeof(Customer)) as Customer; + if (customer != null) + { + MessageBox.Show(customer.Name); + } + } + } + //</MultipleFormatsExample> + + //<GenericSetDataExample> + // Demonstrates SetData, ContainsData, and GetData. + public Object SwapClipboardFormattedData(String format, Object data) + { + Object returnObject = null; + if (Clipboard.ContainsData(format)) + { + returnObject = Clipboard.GetData(format); + Clipboard.SetData(format, data); + } + return returnObject; + } + //</GenericSetDataExample> + + //<SetTextExample> + //<SetAudioExample> + // Demonstrates SetAudio, ContainsAudio, and GetAudioStream. + public System.IO.Stream SwapClipboardAudio( + System.IO.Stream replacementAudioStream) + { + System.IO.Stream returnAudioStream = null; + if (Clipboard.ContainsAudio()) + { + returnAudioStream = Clipboard.GetAudioStream(); + Clipboard.SetAudio(replacementAudioStream); + } + return returnAudioStream; + } + //</SetAudioExample> + + //<SetFileDropListExample> + // Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList + public System.Collections.Specialized.StringCollection + SwapClipboardFileDropList( + System.Collections.Specialized.StringCollection replacementList) + { + System.Collections.Specialized.StringCollection returnList = null; + if (Clipboard.ContainsFileDropList()) + { + returnList = Clipboard.GetFileDropList(); + Clipboard.SetFileDropList(replacementList); + } + return returnList; + } + //</SetFileDropListExample> + + //<SetImageExample> + // Demonstrates SetImage, ContainsImage, and GetImage. + public System.Drawing.Image SwapClipboardImage( + System.Drawing.Image replacementImage) + { + System.Drawing.Image returnImage = null; + if (Clipboard.ContainsImage()) + { + returnImage = Clipboard.GetImage(); + Clipboard.SetImage(replacementImage); + } + return returnImage; + } + //</SetImageExample> + + //<SetHtmlTextExample> + // Demonstrates SetText, ContainsText, and GetText. + public String SwapClipboardHtmlText(String replacementHtmlText) + { + String returnHtmlText = null; + if (Clipboard.ContainsText(TextDataFormat.Html)) + { + returnHtmlText = Clipboard.GetText(TextDataFormat.Html); + Clipboard.SetText(replacementHtmlText, TextDataFormat.Html); + } + return returnHtmlText; + } + //</SetHtmlTextExample> + //</SetTextExample> +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.csproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.csproj new file mode 100644 index 0000000000..d161c73f49 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/csharp/ClipboardOperations.csproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net48</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb new file mode 100644 index 0000000000..386dc9e977 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vb @@ -0,0 +1,193 @@ +Imports System.Windows.Forms + +Module Program + <STAThread> + Sub Main() + Application.EnableVisualStyles() + Application.SetCompatibleTextRenderingDefault(False) + Application.Run(New Form1()) + End Sub +End Module + +Public Class Form1 + Inherits Form + + '<CustomerClass> + <Serializable()> Public Class Customer + + Private nameValue As String = String.Empty + + Public Sub New(ByVal name As String) + nameValue = name + End Sub + + Public Property Name() As String + Get + Return nameValue + End Get + Set(ByVal value As String) + nameValue = value + End Set + End Property + + End Class + '</CustomerClass> + + Public Sub New() + '<ClipboardClear> + Clipboard.Clear() + '</ClipboardClear> + End Sub + + '<CustomFormatExample> + ' Demonstrates SetData, ContainsData, and GetData + ' using a custom format name and a business object. + Public ReadOnly Property TestCustomFormat() As Customer + Get + Clipboard.SetData("CustomerFormat", New Customer("Customer Name")) + + If Clipboard.ContainsData("CustomerFormat") Then + Return CType(Clipboard.GetData("CustomerFormat"), Customer) + End If + + Return Nothing + End Get + End Property + '</CustomFormatExample> + + '<MultipleFormatsExample> + ' Demonstrates how to use a DataObject to add + ' data to the Clipboard in multiple formats. + Public Sub TestClipboardMultipleFormats() + + Dim data As New DataObject() + + ' Add a Customer object using the type as the format. + data.SetData(New Customer("Customer as Customer object")) + + ' Add a ListViewItem object using a custom format name. + data.SetData("CustomFormat", _ + New ListViewItem("Customer as ListViewItem")) + + Clipboard.SetDataObject(data) + Dim retrievedData As DataObject = _ + CType(Clipboard.GetDataObject(), DataObject) + + If (retrievedData.GetDataPresent("CustomFormat")) Then + + Dim item As ListViewItem = _ + TryCast(retrievedData.GetData("CustomFormat"), ListViewItem) + + If item IsNot Nothing Then + MessageBox.Show(item.Text) + End If + + End If + + If retrievedData.GetDataPresent(GetType(Customer)) Then + + Dim customer As Customer = _ + CType(retrievedData.GetData(GetType(Customer)), Customer) + + If customer IsNot Nothing Then + + MessageBox.Show(customer.Name) + End If + + End If + + End Sub + '</MultipleFormatsExample> + + '<GenericSetDataExample> + ' Demonstrates SetData, ContainsData, and GetData. + Public Function SwapClipboardFormattedData( _ + ByVal format As String, ByVal data As Object) As Object + + Dim returnObject As Object = Nothing + + If (Clipboard.ContainsData(format)) Then + returnObject = Clipboard.GetData(format) + Clipboard.SetData(format, data) + End If + + Return returnObject + + End Function + '</GenericSetDataExample> + + '<SetTextExample> + '<SetAudioExample> + ' Demonstrates SetAudio, ContainsAudio, and GetAudioStream. + Public Function SwapClipboardAudio( _ + ByVal replacementAudioStream As System.IO.Stream) _ + As System.IO.Stream + + Dim returnAudioStream As System.IO.Stream = Nothing + + If (Clipboard.ContainsAudio()) Then + returnAudioStream = Clipboard.GetAudioStream() + Clipboard.SetAudio(replacementAudioStream) + End If + + Return returnAudioStream + + End Function + '</SetAudioExample> + + '<SetFileDropListExample> + ' Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList + Public Function SwapClipboardFileDropList(ByVal replacementList _ + As System.Collections.Specialized.StringCollection) _ + As System.Collections.Specialized.StringCollection + + Dim returnList As System.Collections.Specialized.StringCollection _ + = Nothing + + If Clipboard.ContainsFileDropList() Then + + returnList = Clipboard.GetFileDropList() + Clipboard.SetFileDropList(replacementList) + End If + + Return returnList + + End Function + '</SetFileDropListExample> + + '<SetImageExample> + ' Demonstrates SetImage, ContainsImage, and GetImage. + Public Function SwapClipboardImage( _ + ByVal replacementImage As System.Drawing.Image) _ + As System.Drawing.Image + + Dim returnImage As System.Drawing.Image = Nothing + + If Clipboard.ContainsImage() Then + returnImage = Clipboard.GetImage() + Clipboard.SetImage(replacementImage) + End If + + Return returnImage + End Function + '</SetImageExample> + + '<SetHtmlTextExample> + ' Demonstrates SetText, ContainsText, and GetText. + Public Function SwapClipboardHtmlText( _ + ByVal replacementHtmlText As String) As String + + Dim returnHtmlText As String = Nothing + + If (Clipboard.ContainsText(TextDataFormat.Html)) Then + returnHtmlText = Clipboard.GetText(TextDataFormat.Html) + Clipboard.SetText(replacementHtmlText, TextDataFormat.Html) + End If + + Return returnHtmlText + + End Function + '</SetHtmlTextExample> + '</SetTextExample> + +End Class \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vbproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vbproj new file mode 100644 index 0000000000..d161c73f49 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/framework/vb/ClipboardOperations.vbproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net48</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.Designer.cs new file mode 100644 index 0000000000..1867f62da3 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.Designer.cs @@ -0,0 +1,130 @@ +namespace ClipboardOperations +{ + partial class ClipboardImageTest + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + pictureBox1 = new PictureBox(); + pictureBox2 = new PictureBox(); + pictureBox3 = new PictureBox(); + btnSetImage1 = new Button(); + btnSetImage2 = new Button(); + btnGetFromClipboard = new Button(); + ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); + ((System.ComponentModel.ISupportInitialize)pictureBox2).BeginInit(); + ((System.ComponentModel.ISupportInitialize)pictureBox3).BeginInit(); + SuspendLayout(); + // + // pictureBox1 + // + pictureBox1.BorderStyle = BorderStyle.FixedSingle; + pictureBox1.Location = new Point(20, 20); + pictureBox1.Name = "pictureBox1"; + pictureBox1.Size = new Size(150, 100); + pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; + pictureBox1.TabIndex = 0; + pictureBox1.TabStop = false; + // + // pictureBox2 + // + pictureBox2.BorderStyle = BorderStyle.FixedSingle; + pictureBox2.Location = new Point(20, 150); + pictureBox2.Name = "pictureBox2"; + pictureBox2.Size = new Size(150, 100); + pictureBox2.SizeMode = PictureBoxSizeMode.Zoom; + pictureBox2.TabIndex = 1; + pictureBox2.TabStop = false; + // + // pictureBox3 + // + pictureBox3.BorderStyle = BorderStyle.FixedSingle; + pictureBox3.Location = new Point(300, 85); + pictureBox3.Name = "pictureBox3"; + pictureBox3.Size = new Size(150, 100); + pictureBox3.SizeMode = PictureBoxSizeMode.Zoom; + pictureBox3.TabIndex = 2; + pictureBox3.TabStop = false; + // + // btnSetImage1 + // + btnSetImage1.Location = new Point(190, 50); + btnSetImage1.Name = "btnSetImage1"; + btnSetImage1.Size = new Size(90, 30); + btnSetImage1.TabIndex = 3; + btnSetImage1.Text = "Set Image 1"; + btnSetImage1.UseVisualStyleBackColor = true; + btnSetImage1.Click += btnSetImage1_Click; + // + // btnSetImage2 + // + btnSetImage2.Location = new Point(190, 180); + btnSetImage2.Name = "btnSetImage2"; + btnSetImage2.Size = new Size(90, 30); + btnSetImage2.TabIndex = 4; + btnSetImage2.Text = "Set Image 2"; + btnSetImage2.UseVisualStyleBackColor = true; + btnSetImage2.Click += btnSetImage2_Click; + // + // btnGetFromClipboard + // + btnGetFromClipboard.Location = new Point(470, 115); + btnGetFromClipboard.Name = "btnGetFromClipboard"; + btnGetFromClipboard.Size = new Size(120, 30); + btnGetFromClipboard.TabIndex = 5; + btnGetFromClipboard.Text = "Get from Clipboard"; + btnGetFromClipboard.UseVisualStyleBackColor = true; + btnGetFromClipboard.Click += btnGetFromClipboard_Click; + // + // ClipboardImageTest + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(620, 280); + Controls.Add(btnGetFromClipboard); + Controls.Add(btnSetImage2); + Controls.Add(btnSetImage1); + Controls.Add(pictureBox3); + Controls.Add(pictureBox2); + Controls.Add(pictureBox1); + Name = "ClipboardImageTest"; + Text = "Clipboard Image Test"; + ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); + ((System.ComponentModel.ISupportInitialize)pictureBox2).EndInit(); + ((System.ComponentModel.ISupportInitialize)pictureBox3).EndInit(); + ResumeLayout(false); + } + + #endregion + + private PictureBox pictureBox1; + private PictureBox pictureBox2; + private PictureBox pictureBox3; + private Button btnSetImage1; + private Button btnSetImage2; + private Button btnGetFromClipboard; + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.cs new file mode 100644 index 0000000000..c5e980501a --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using System.IO; + +namespace ClipboardOperations +{ + public partial class ClipboardImageTest : Form + { + public ClipboardImageTest() + { + InitializeComponent(); + LoadImages(); + } + + private void LoadImages() + { + try + { + // Load image1.png into pictureBox1 + if (File.Exists("image1.png")) + { + pictureBox1.Image = Image.FromFile("image1.png"); + } + else + { + // Create a simple colored rectangle if image doesn't exist + pictureBox1.Image = CreateTestImage(Color.Red, "Image 1"); + } + + // Load image2.png into pictureBox2 + if (File.Exists("image2.png")) + { + pictureBox2.Image = Image.FromFile("image2.png"); + } + else + { + // Create a simple colored rectangle if image doesn't exist + pictureBox2.Image = CreateTestImage(Color.Blue, "Image 2"); + } + } + catch (Exception ex) + { + MessageBox.Show($"Error loading images: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private Image CreateTestImage(Color backgroundColor, string text) + { + Bitmap bitmap = new Bitmap(150, 100); + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.FillRectangle(new SolidBrush(backgroundColor), 0, 0, 150, 100); + g.DrawString(text, SystemFonts.DefaultFont, Brushes.White, 10, 40); + } + return bitmap; + } + + private void btnSetImage1_Click(object sender, EventArgs e) + { + try + { + if (pictureBox1.Image != null) + { + Clipboard.SetImage(pictureBox1.Image); + MessageBox.Show("Image 1 copied to clipboard!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show("No image to copy!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + catch (Exception ex) + { + MessageBox.Show($"Error copying image to clipboard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnSetImage2_Click(object sender, EventArgs e) + { + try + { + if (pictureBox2.Image != null) + { + Clipboard.SetImage(pictureBox2.Image); + MessageBox.Show("Image 2 copied to clipboard!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show("No image to copy!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + catch (Exception ex) + { + MessageBox.Show($"Error copying image to clipboard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnGetFromClipboard_Click(object sender, EventArgs e) + { + try + { + if (Clipboard.ContainsImage()) + { + Image clipboardImage = Clipboard.GetImage(); + pictureBox3.Image = clipboardImage; + MessageBox.Show("Image retrieved from clipboard!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show("No image found in clipboard!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); + pictureBox3.Image = null; + } + } + catch (Exception ex) + { + MessageBox.Show($"Error getting image from clipboard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.resx b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.resx new file mode 100644 index 0000000000..8b2ff64a11 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardImageTest.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.Designer.cs new file mode 100644 index 0000000000..914e56cb1a --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.Designer.cs @@ -0,0 +1,141 @@ +namespace ClipboardExample +{ + partial class ClipboardMultipleFormatsTest + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + btnTestMultipleFormats = new Button(); + btnClearClipboard = new Button(); + btnCheckClipboard = new Button(); + lblStatus = new Label(); + txtClipboardInfo = new TextBox(); + lblTitle = new Label(); + lblDescription = new Label(); + SuspendLayout(); + // + // btnTestMultipleFormats + // + btnTestMultipleFormats.Location = new Point(30, 80); + btnTestMultipleFormats.Name = "btnTestMultipleFormats"; + btnTestMultipleFormats.Size = new Size(150, 30); + btnTestMultipleFormats.TabIndex = 0; + btnTestMultipleFormats.Text = "Test Multiple Formats"; + btnTestMultipleFormats.UseVisualStyleBackColor = true; + btnTestMultipleFormats.Click += btnTestMultipleFormats_Click; + // + // btnClearClipboard + // + btnClearClipboard.Location = new Point(200, 80); + btnClearClipboard.Name = "btnClearClipboard"; + btnClearClipboard.Size = new Size(120, 30); + btnClearClipboard.TabIndex = 1; + btnClearClipboard.Text = "Clear Clipboard"; + btnClearClipboard.UseVisualStyleBackColor = true; + btnClearClipboard.Click += btnClearClipboard_Click; + // + // btnCheckClipboard + // + btnCheckClipboard.Location = new Point(340, 80); + btnCheckClipboard.Name = "btnCheckClipboard"; + btnCheckClipboard.Size = new Size(120, 30); + btnCheckClipboard.TabIndex = 2; + btnCheckClipboard.Text = "Check Clipboard"; + btnCheckClipboard.UseVisualStyleBackColor = true; + btnCheckClipboard.Click += btnCheckClipboard_Click; + // + // lblStatus + // + lblStatus.AutoSize = true; + lblStatus.Font = new Font("Segoe UI", 9F, FontStyle.Bold); + lblStatus.Location = new Point(30, 130); + lblStatus.Name = "lblStatus"; + lblStatus.Size = new Size(47, 15); + lblStatus.TabIndex = 3; + lblStatus.Text = "Ready."; + // + // txtClipboardInfo + // + txtClipboardInfo.Location = new Point(30, 160); + txtClipboardInfo.Multiline = true; + txtClipboardInfo.Name = "txtClipboardInfo"; + txtClipboardInfo.ReadOnly = true; + txtClipboardInfo.ScrollBars = ScrollBars.Vertical; + txtClipboardInfo.Size = new Size(430, 150); + txtClipboardInfo.TabIndex = 4; + txtClipboardInfo.Text = "Click 'Check Clipboard' to see available formats."; + // + // lblTitle + // + lblTitle.AutoSize = true; + lblTitle.Font = new Font("Segoe UI", 12F, FontStyle.Bold); + lblTitle.Location = new Point(30, 20); + lblTitle.Name = "lblTitle"; + lblTitle.Size = new Size(246, 21); + lblTitle.TabIndex = 5; + lblTitle.Text = "Clipboard Multiple Formats Test"; + // + // lblDescription + // + lblDescription.AutoSize = true; + lblDescription.Location = new Point(30, 50); + lblDescription.Name = "lblDescription"; + lblDescription.Size = new Size(398, 15); + lblDescription.TabIndex = 6; + lblDescription.Text = "Test adding multiple data formats to clipboard and retrieving them."; + // + // ClipboardMultipleFormatsTest + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(494, 341); + Controls.Add(lblDescription); + Controls.Add(lblTitle); + Controls.Add(txtClipboardInfo); + Controls.Add(lblStatus); + Controls.Add(btnCheckClipboard); + Controls.Add(btnClearClipboard); + Controls.Add(btnTestMultipleFormats); + MaximizeBox = false; + MinimumSize = new Size(510, 380); + Name = "ClipboardMultipleFormatsTest"; + StartPosition = FormStartPosition.CenterScreen; + Text = "Clipboard Multiple Formats Test"; + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private Button btnTestMultipleFormats; + private Button btnClearClipboard; + private Button btnCheckClipboard; + private Label lblStatus; + private TextBox txtClipboardInfo; + private Label lblTitle; + private Label lblDescription; + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.cs new file mode 100644 index 0000000000..9e34ab0973 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.cs @@ -0,0 +1,125 @@ +using System.Collections.Specialized; + +namespace ClipboardExample; + +public partial class ClipboardMultipleFormatsTest : Form +{ + public ClipboardMultipleFormatsTest() + { + InitializeComponent(); + } + + // Customer class used in custom clipboard format examples. + public class Customer + { + public string Name { get; set; } + + public Customer(string name) + { + Name = name; + } + } + + // Demonstrates how to use a DataObject to add + // data to the Clipboard in multiple formats. + public void TestClipboardMultipleFormats() + { + DataObject data = new(); + + Customer customer = new("Customer #2112"); + ListViewItem listViewItem = new($"Customer as ListViewItem {customer.Name}"); + + // Add a Customer object using the type as the format. + data.SetDataAsJson(customer); + + // Add a ListViewItem object using a custom format name. + data.SetDataAsJson("ListViewItemFormat", listViewItem.Text); + + Clipboard.SetDataObject(data); + + // Retrieve the data from the Clipboard. + DataObject retrievedData = (DataObject)Clipboard.GetDataObject()!; + + if (retrievedData.GetDataPresent("ListViewItemFormat")) + { + if (retrievedData.TryGetData("ListViewItemFormat", out String item)) + { + ListViewItem recreatedListViewItem = new(item); + MessageBox.Show($"Data contains ListViewItem with text of {recreatedListViewItem.Text}"); + } + } + + if (retrievedData.GetDataPresent(typeof(Customer))) + { + if (retrievedData.TryGetData(out Customer newCustomer)) + { + MessageBox.Show($"Data contains Customer with name of {newCustomer.Name}"); + } + } + } + + private void btnTestMultipleFormats_Click(object sender, EventArgs e) + { + try + { + TestClipboardMultipleFormats(); + lblStatus.Text = "Multiple formats test completed successfully!"; + lblStatus.ForeColor = Color.Green; + } + catch (Exception ex) + { + lblStatus.Text = $"Error: {ex.Message}"; + lblStatus.ForeColor = Color.Red; + } + } + + private void btnClearClipboard_Click(object sender, EventArgs e) + { + try + { + Clipboard.Clear(); + lblStatus.Text = "Clipboard cleared."; + lblStatus.ForeColor = Color.Blue; + } + catch (Exception ex) + { + lblStatus.Text = $"Error clearing clipboard: {ex.Message}"; + lblStatus.ForeColor = Color.Red; + } + } + + private void btnCheckClipboard_Click(object sender, EventArgs e) + { + try + { + var dataObject = Clipboard.GetDataObject(); + if (dataObject != null) + { + var formats = dataObject.GetFormats(); + if (formats.Length > 0) + { + txtClipboardInfo.Text = "Available formats in clipboard:\r\n" + string.Join("\r\n", formats); + lblStatus.Text = $"Found {formats.Length} format(s) in clipboard."; + lblStatus.ForeColor = Color.Blue; + } + else + { + txtClipboardInfo.Text = "No data formats found in clipboard."; + lblStatus.Text = "Clipboard is empty."; + lblStatus.ForeColor = Color.Gray; + } + } + else + { + txtClipboardInfo.Text = "Clipboard data object is null."; + lblStatus.Text = "Clipboard is empty."; + lblStatus.ForeColor = Color.Gray; + } + } + catch (Exception ex) + { + lblStatus.Text = $"Error checking clipboard: {ex.Message}"; + lblStatus.ForeColor = Color.Red; + } + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.resx b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.resx new file mode 100644 index 0000000000..459ff7366d --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardMultipleFormatsTest.resx @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardOperations.csproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardOperations.csproj new file mode 100644 index 0000000000..6621be8cc8 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardOperations.csproj @@ -0,0 +1,35 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <Nullable>enable</Nullable> + <UseWindowsForms>true</UseWindowsForms> + <ImplicitUsings>enable</ImplicitUsings> + </PropertyGroup> + + <ItemGroup> + <Compile Update="Properties\Resources.Designer.cs"> + <DesignTime>True</DesignTime> + <AutoGen>True</AutoGen> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> + </ItemGroup> + + <ItemGroup> + <EmbeddedResource Update="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> + + <ItemGroup> + <None Update="image1.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + <None Update="image2.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> + </ItemGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.Designer.cs new file mode 100644 index 0000000000..61e523e249 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.Designer.cs @@ -0,0 +1,282 @@ +namespace ClipboardExample +{ + partial class ClipboardTextTest + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.lblText = new System.Windows.Forms.Label(); + this.txtText = new System.Windows.Forms.TextBox(); + this.btnSetText = new System.Windows.Forms.Button(); + this.lblUnicodeText = new System.Windows.Forms.Label(); + this.txtUnicodeText = new System.Windows.Forms.TextBox(); + this.btnSetUnicodeText = new System.Windows.Forms.Button(); + this.lblRtf = new System.Windows.Forms.Label(); + this.txtRtf = new System.Windows.Forms.TextBox(); + this.btnSetRtf = new System.Windows.Forms.Button(); + this.lblHtml = new System.Windows.Forms.Label(); + this.txtHtml = new System.Windows.Forms.TextBox(); + this.btnSetHtml = new System.Windows.Forms.Button(); + this.lblCommaSeparatedValue = new System.Windows.Forms.Label(); + this.txtCommaSeparatedValue = new System.Windows.Forms.TextBox(); + this.btnSetCommaSeparatedValue = new System.Windows.Forms.Button(); + this.lblOutput = new System.Windows.Forms.Label(); + this.txtOutput = new System.Windows.Forms.RichTextBox(); + this.btnGetClipboard = new System.Windows.Forms.Button(); + this.btnClearClipboard = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // lblText + // + this.lblText.AutoSize = true; + this.lblText.Location = new System.Drawing.Point(12, 15); + this.lblText.Name = "lblText"; + this.lblText.Size = new System.Drawing.Size(31, 15); + this.lblText.TabIndex = 0; + this.lblText.Text = "Text:"; + // + // txtText + // + this.txtText.Location = new System.Drawing.Point(12, 33); + this.txtText.Name = "txtText"; + this.txtText.Size = new System.Drawing.Size(300, 23); + this.txtText.TabIndex = 1; + this.txtText.Text = "Plain text example"; + // + // btnSetText + // + this.btnSetText.Location = new System.Drawing.Point(318, 33); + this.btnSetText.Name = "btnSetText"; + this.btnSetText.Size = new System.Drawing.Size(75, 23); + this.btnSetText.TabIndex = 2; + this.btnSetText.Text = "Set Text"; + this.btnSetText.UseVisualStyleBackColor = true; + this.btnSetText.Click += new System.EventHandler(this.btnSetText_Click); + // + // lblUnicodeText + // + this.lblUnicodeText.AutoSize = true; + this.lblUnicodeText.Location = new System.Drawing.Point(12, 65); + this.lblUnicodeText.Name = "lblUnicodeText"; + this.lblUnicodeText.Size = new System.Drawing.Size(79, 15); + this.lblUnicodeText.TabIndex = 3; + this.lblUnicodeText.Text = "Unicode Text:"; + // + // txtUnicodeText + // + this.txtUnicodeText.Location = new System.Drawing.Point(12, 83); + this.txtUnicodeText.Name = "txtUnicodeText"; + this.txtUnicodeText.Size = new System.Drawing.Size(300, 23); + this.txtUnicodeText.TabIndex = 4; + this.txtUnicodeText.Text = "Unicode text example: ñ, é, ü, 中文"; + // + // btnSetUnicodeText + // + this.btnSetUnicodeText.Location = new System.Drawing.Point(318, 83); + this.btnSetUnicodeText.Name = "btnSetUnicodeText"; + this.btnSetUnicodeText.Size = new System.Drawing.Size(75, 23); + this.btnSetUnicodeText.TabIndex = 5; + this.btnSetUnicodeText.Text = "Set Unicode"; + this.btnSetUnicodeText.UseVisualStyleBackColor = true; + this.btnSetUnicodeText.Click += new System.EventHandler(this.btnSetUnicodeText_Click); + // + // lblRtf + // + this.lblRtf.AutoSize = true; + this.lblRtf.Location = new System.Drawing.Point(12, 115); + this.lblRtf.Name = "lblRtf"; + this.lblRtf.Size = new System.Drawing.Size(29, 15); + this.lblRtf.TabIndex = 6; + this.lblRtf.Text = "RTF:"; + // + // txtRtf + // + this.txtRtf.Location = new System.Drawing.Point(12, 133); + this.txtRtf.Name = "txtRtf"; + this.txtRtf.Size = new System.Drawing.Size(300, 23); + this.txtRtf.TabIndex = 7; + this.txtRtf.Text = "{\\rtf1\\ansi\\deff0 {\\fonttbl {\\f0 Times New Roman;}} \\f0\\fs24 Hello \\b World\\b0 !}"; + // + // btnSetRtf + // + this.btnSetRtf.Location = new System.Drawing.Point(318, 133); + this.btnSetRtf.Name = "btnSetRtf"; + this.btnSetRtf.Size = new System.Drawing.Size(75, 23); + this.btnSetRtf.TabIndex = 8; + this.btnSetRtf.Text = "Set RTF"; + this.btnSetRtf.UseVisualStyleBackColor = true; + this.btnSetRtf.Click += new System.EventHandler(this.btnSetRtf_Click); + // + // lblHtml + // + this.lblHtml.AutoSize = true; + this.lblHtml.Location = new System.Drawing.Point(12, 165); + this.lblHtml.Name = "lblHtml"; + this.lblHtml.Size = new System.Drawing.Size(39, 15); + this.lblHtml.TabIndex = 9; + this.lblHtml.Text = "HTML:"; + // + // txtHtml + // + this.txtHtml.Location = new System.Drawing.Point(12, 183); + this.txtHtml.Name = "txtHtml"; + this.txtHtml.Size = new System.Drawing.Size(300, 23); + this.txtHtml.TabIndex = 10; + this.txtHtml.Text = "<html><body><b>Hello</b> <i>World</i>!</body></html>"; + // + // btnSetHtml + // + this.btnSetHtml.Location = new System.Drawing.Point(318, 183); + this.btnSetHtml.Name = "btnSetHtml"; + this.btnSetHtml.Size = new System.Drawing.Size(75, 23); + this.btnSetHtml.TabIndex = 11; + this.btnSetHtml.Text = "Set HTML"; + this.btnSetHtml.UseVisualStyleBackColor = true; + this.btnSetHtml.Click += new System.EventHandler(this.btnSetHtml_Click); + // + // lblCommaSeparatedValue + // + this.lblCommaSeparatedValue.AutoSize = true; + this.lblCommaSeparatedValue.Location = new System.Drawing.Point(12, 215); + this.lblCommaSeparatedValue.Name = "lblCommaSeparatedValue"; + this.lblCommaSeparatedValue.Size = new System.Drawing.Size(32, 15); + this.lblCommaSeparatedValue.TabIndex = 12; + this.lblCommaSeparatedValue.Text = "CSV:"; + // + // txtCommaSeparatedValue + // + this.txtCommaSeparatedValue.Location = new System.Drawing.Point(12, 233); + this.txtCommaSeparatedValue.Name = "txtCommaSeparatedValue"; + this.txtCommaSeparatedValue.Size = new System.Drawing.Size(300, 23); + this.txtCommaSeparatedValue.TabIndex = 13; + this.txtCommaSeparatedValue.Text = "Name,Age,City\\r\\nJohn,25,\"New York\"\\r\\nJane,30,London"; + // + // btnSetCommaSeparatedValue + // + this.btnSetCommaSeparatedValue.Location = new System.Drawing.Point(318, 233); + this.btnSetCommaSeparatedValue.Name = "btnSetCommaSeparatedValue"; + this.btnSetCommaSeparatedValue.Size = new System.Drawing.Size(75, 23); + this.btnSetCommaSeparatedValue.TabIndex = 14; + this.btnSetCommaSeparatedValue.Text = "Set CSV"; + this.btnSetCommaSeparatedValue.UseVisualStyleBackColor = true; + this.btnSetCommaSeparatedValue.Click += new System.EventHandler(this.btnSetCommaSeparatedValue_Click); + // + // lblOutput + // + this.lblOutput.AutoSize = true; + this.lblOutput.Location = new System.Drawing.Point(12, 275); + this.lblOutput.Name = "lblOutput"; + this.lblOutput.Size = new System.Drawing.Size(101, 15); + this.lblOutput.TabIndex = 15; + this.lblOutput.Text = "Clipboard Output:"; + // + // txtOutput + // + this.txtOutput.Location = new System.Drawing.Point(12, 293); + this.txtOutput.Name = "txtOutput"; + this.txtOutput.ReadOnly = true; + this.txtOutput.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; + this.txtOutput.Size = new System.Drawing.Size(381, 120); + this.txtOutput.TabIndex = 16; + this.txtOutput.Text = ""; + // + // btnGetClipboard + // + this.btnGetClipboard.Location = new System.Drawing.Point(12, 419); + this.btnGetClipboard.Name = "btnGetClipboard"; + this.btnGetClipboard.Size = new System.Drawing.Size(100, 30); + this.btnGetClipboard.TabIndex = 17; + this.btnGetClipboard.Text = "Get Clipboard"; + this.btnGetClipboard.UseVisualStyleBackColor = true; + this.btnGetClipboard.Click += new System.EventHandler(this.btnGetClipboard_Click); + // + // btnClearClipboard + // + this.btnClearClipboard.Location = new System.Drawing.Point(118, 419); + this.btnClearClipboard.Name = "btnClearClipboard"; + this.btnClearClipboard.Size = new System.Drawing.Size(100, 30); + this.btnClearClipboard.TabIndex = 18; + this.btnClearClipboard.Text = "Clear Clipboard"; + this.btnClearClipboard.UseVisualStyleBackColor = true; + this.btnClearClipboard.Click += new System.EventHandler(this.btnClearClipboard_Click); + // + // ClipboardTextTest + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(409, 461); + this.Controls.Add(this.btnClearClipboard); + this.Controls.Add(this.btnGetClipboard); + this.Controls.Add(this.txtOutput); + this.Controls.Add(this.lblOutput); + this.Controls.Add(this.btnSetCommaSeparatedValue); + this.Controls.Add(this.txtCommaSeparatedValue); + this.Controls.Add(this.lblCommaSeparatedValue); + this.Controls.Add(this.btnSetHtml); + this.Controls.Add(this.txtHtml); + this.Controls.Add(this.lblHtml); + this.Controls.Add(this.btnSetRtf); + this.Controls.Add(this.txtRtf); + this.Controls.Add(this.lblRtf); + this.Controls.Add(this.btnSetUnicodeText); + this.Controls.Add(this.txtUnicodeText); + this.Controls.Add(this.lblUnicodeText); + this.Controls.Add(this.btnSetText); + this.Controls.Add(this.txtText); + this.Controls.Add(this.lblText); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "ClipboardTextTest"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Clipboard Text Format Test"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label lblText; + private System.Windows.Forms.TextBox txtText; + private System.Windows.Forms.Button btnSetText; + private System.Windows.Forms.Label lblUnicodeText; + private System.Windows.Forms.TextBox txtUnicodeText; + private System.Windows.Forms.Button btnSetUnicodeText; + private System.Windows.Forms.Label lblRtf; + private System.Windows.Forms.TextBox txtRtf; + private System.Windows.Forms.Button btnSetRtf; + private System.Windows.Forms.Label lblHtml; + private System.Windows.Forms.TextBox txtHtml; + private System.Windows.Forms.Button btnSetHtml; + private System.Windows.Forms.Label lblCommaSeparatedValue; + private System.Windows.Forms.TextBox txtCommaSeparatedValue; + private System.Windows.Forms.Button btnSetCommaSeparatedValue; + private System.Windows.Forms.Label lblOutput; + private System.Windows.Forms.RichTextBox txtOutput; + private System.Windows.Forms.Button btnGetClipboard; + private System.Windows.Forms.Button btnClearClipboard; + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.cs new file mode 100644 index 0000000000..b4cc65d13e --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.cs @@ -0,0 +1,105 @@ +using System; +using System.Windows.Forms; + +namespace ClipboardExample +{ + public partial class ClipboardTextTest : Form + { + public ClipboardTextTest() + { + InitializeComponent(); + } + + private void btnSetText_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(txtText.Text)) + { + Clipboard.SetText(txtText.Text, TextDataFormat.Text); + } + } + + private void btnSetUnicodeText_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(txtUnicodeText.Text)) + { + Clipboard.SetText(txtUnicodeText.Text, TextDataFormat.UnicodeText); + } + } + + private void btnSetRtf_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(txtRtf.Text)) + { + Clipboard.SetText(txtRtf.Text, TextDataFormat.Rtf); + } + } + + private void btnSetHtml_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(txtHtml.Text)) + { + Clipboard.SetText(txtHtml.Text, TextDataFormat.Html); + } + } + + private void btnSetCommaSeparatedValue_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(txtCommaSeparatedValue.Text)) + { + Clipboard.SetText(txtCommaSeparatedValue.Text, TextDataFormat.CommaSeparatedValue); + } + } + + private void btnGetClipboard_Click(object sender, EventArgs e) + { + txtOutput.Clear(); + + // Check if RTF is available first and display it formatted + if (Clipboard.ContainsText(TextDataFormat.Rtf)) + { + string rtfData = Clipboard.GetText(TextDataFormat.Rtf); + txtOutput.AppendText("RTF (formatted): "); + + // Store current selection position + int selectionStart = txtOutput.TextLength; + txtOutput.AppendText("\n"); + + // Insert the RTF content with formatting + int rtfStart = txtOutput.TextLength; + txtOutput.SelectedRtf = rtfData; + txtOutput.AppendText("\n\nRTF (raw): " + rtfData + "\n\n"); + } + + if (Clipboard.ContainsText(TextDataFormat.Text)) + { + txtOutput.AppendText("Text: " + Clipboard.GetText(TextDataFormat.Text) + "\n"); + } + + if (Clipboard.ContainsText(TextDataFormat.UnicodeText)) + { + txtOutput.AppendText("UnicodeText: " + Clipboard.GetText(TextDataFormat.UnicodeText) + "\n"); + } + + if (Clipboard.ContainsText(TextDataFormat.Html)) + { + txtOutput.AppendText("Html: " + Clipboard.GetText(TextDataFormat.Html) + "\n"); + } + + if (Clipboard.ContainsText(TextDataFormat.CommaSeparatedValue)) + { + txtOutput.AppendText("CommaSeparatedValue: " + Clipboard.GetText(TextDataFormat.CommaSeparatedValue) + "\n"); + } + + if (txtOutput.Text.Length == 0) + { + txtOutput.Text = "No text data found in clipboard."; + } + } + + private void btnClearClipboard_Click(object sender, EventArgs e) + { + Clipboard.Clear(); + txtOutput.Text = "Clipboard cleared."; + } + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.resx b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.resx new file mode 100644 index 0000000000..83e41b1c81 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/ClipboardTextTest.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.Designer.cs new file mode 100644 index 0000000000..87281239ea --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.Designer.cs @@ -0,0 +1,65 @@ +namespace ClipboardExample +{ + partial class FileDropClipboardTest + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// The main text box control for displaying clipboard file information. + /// </summary> + private TextBox textBox; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.textBox = new TextBox(); + this.SuspendLayout(); + + // + // textBox + // + this.textBox.Location = new Point(12, 12); + this.textBox.Multiline = true; + this.textBox.Name = "textBox"; + this.textBox.ScrollBars = ScrollBars.Vertical; + this.textBox.Size = new Size(560, 437); + this.textBox.TabIndex = 0; + this.textBox.KeyDown += new KeyEventHandler(this.textBox_KeyDown); + + // + // FileDropClipboardTest + // + this.AutoScaleDimensions = new SizeF(7F, 15F); + this.AutoScaleMode = AutoScaleMode.Font; + this.ClientSize = new Size(584, 461); + this.Controls.Add(this.textBox); + this.Name = "FileDropClipboardTest"; + this.Text = "File Drop Clipboard Test"; + this.ResumeLayout(false); + this.PerformLayout(); + } + + #endregion + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.cs new file mode 100644 index 0000000000..527174e904 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.cs @@ -0,0 +1,82 @@ +using System.Collections.Specialized; + +namespace ClipboardExample; + +public partial class FileDropClipboardTest : Form +{ + public FileDropClipboardTest() + { + InitializeComponent(); + } + + private void textBox_KeyDown(object sender, KeyEventArgs e) + { + // Check for Ctrl+V key combination + if (e.Control && e.KeyCode == Keys.V) + { + // Prevent the default paste behavior + e.Handled = true; + + // Handle the clipboard file drop list + HandleClipboardFileDropList(); + } + } + + private void HandleClipboardFileDropList() + { + try + { + // Check if clipboard contains file drop list + if (Clipboard.ContainsFileDropList()) + { + // Get the file drop list from clipboard + StringCollection files = Clipboard.GetFileDropList(); + + if (files != null && files.Count > 0) + { + // Clear existing text and add file names + textBox.Clear(); + textBox.AppendText("Files from clipboard:" + Environment.NewLine); + textBox.AppendText(new string('-', 30) + Environment.NewLine); + + foreach (string file in files) + { + textBox.AppendText(file + Environment.NewLine); + } + + textBox.AppendText(Environment.NewLine + $"Total files: {files.Count}"); + } + else + { + textBox.Text = "Clipboard contains file drop list but no files found."; + } + } + else + { + // Check what other data formats are available + textBox.Clear(); + textBox.AppendText("No file drop list found in clipboard." + Environment.NewLine); + textBox.AppendText("Available clipboard formats:" + Environment.NewLine); + textBox.AppendText(new string('-', 30) + Environment.NewLine); + + IDataObject dataObject = Clipboard.GetDataObject(); + if (dataObject != null) + { + string[] formats = dataObject.GetFormats(); + foreach (string format in formats) + { + textBox.AppendText($"- {format}" + Environment.NewLine); + } + } + else + { + textBox.AppendText("Clipboard is empty or inaccessible."); + } + } + } + catch (Exception ex) + { + textBox.Text = $"Error accessing clipboard: {ex.Message}"; + } + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.resx b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/FileDropClipboardTest.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.Designer.cs new file mode 100644 index 0000000000..b380e67a72 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.Designer.cs @@ -0,0 +1,44 @@ +namespace ClipboardExample; + +partial class Form1 +{ + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + SuspendLayout(); + // + // Form1 + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 250); + Name = "Form1"; + Text = "Clipboard Example Tester"; + ResumeLayout(false); + } + + #endregion +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs new file mode 100644 index 0000000000..6b6ae0adc7 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.cs @@ -0,0 +1,140 @@ +using System.Collections.Specialized; + +namespace ClipboardExample; + +public partial class Form1 : Form +{ + public Form1() + { + InitializeComponent(); + //<ClipboardClear> + Clipboard.Clear(); + //</ClipboardClear> + } + + //<CustomerClass> + // Customer class used in custom clipboard format examples. + public class Customer + { + public string Name { get; set; } + + public Customer(string name) + { + Name = name; + } + } + //</CustomerClass> + + //<SetTextExample> + // Demonstrates SetAudio, ContainsAudio, and GetAudioStream. + public Stream SwapClipboardAudio(Stream replacementAudioStream) + { + Stream? returnAudioStream = null; + + if (Clipboard.ContainsAudio()) + { + returnAudioStream = Clipboard.GetAudioStream(); + Clipboard.SetAudio(replacementAudioStream); + } + return returnAudioStream; + } + + // Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList + public StringCollection SwapClipboardFileDropList(StringCollection replacementList) + { + StringCollection? returnList = null; + + if (Clipboard.ContainsFileDropList()) + { + returnList = Clipboard.GetFileDropList(); + Clipboard.SetFileDropList(replacementList); + } + return returnList; + } + + // Demonstrates SetImage, ContainsImage, and GetImage. + public Image SwapClipboardImage(Image replacementImage) + { + Image? returnImage = null; + + if (Clipboard.ContainsImage()) + { + returnImage = Clipboard.GetImage(); + Clipboard.SetImage(replacementImage); + } + return returnImage; + } + + // Demonstrates SetText, ContainsText, and GetText. + public string SwapClipboardHtmlText(string replacementHtmlText) + { + string? returnHtmlText = null; + + if (Clipboard.ContainsText(TextDataFormat.Html)) + { + returnHtmlText = Clipboard.GetText(TextDataFormat.Html); + Clipboard.SetText(replacementHtmlText, TextDataFormat.Html); + } + return returnHtmlText; + } + //</SetTextExample> + + //<CustomFormatExample> + // Demonstrates SetDataAsJson, ContainsData, and GetData + // using a custom format name and a business object. + public Customer TestCustomFormat + { + get + { + Clipboard.SetDataAsJson("CustomerFormat", new Customer("Customer Name")); + if (Clipboard.ContainsData("CustomerFormat")) + { + if (Clipboard.TryGetData("CustomerFormat", out Customer customerData)) + return customerData; + } + + return null; + } + } + //</CustomFormatExample> + + //<MultipleFormatsExample> + // Demonstrates how to use a DataObject to add + // data to the Clipboard in multiple formats. + public void TestClipboardMultipleFormats() + { + DataObject data = new(); + + Customer customer = new("Customer #2112"); + ListViewItem listViewItem = new($"Customer as ListViewItem {customer.Name}"); + + // Add a Customer object using the type as the format. + data.SetDataAsJson(customer); + + // Add a ListViewItem object using a custom format name. + data.SetDataAsJson("ListViewItemFormat", listViewItem.Text); + + Clipboard.SetDataObject(data); + + // Retrieve the data from the Clipboard. + DataObject retrievedData = (DataObject)Clipboard.GetDataObject()!; + + if (retrievedData.GetDataPresent("ListViewItemFormat")) + { + if (retrievedData.TryGetData("ListViewItemFormat", out String item)) + { + ListViewItem recreatedListViewItem = new(item); + MessageBox.Show($"Data contains ListViewItem with text of {recreatedListViewItem.Text}"); + } + } + + if (retrievedData.GetDataPresent(typeof(Customer))) + { + if (retrievedData.TryGetData(out Customer newCustomer)) + { + MessageBox.Show($"Data contains Customer with name of {newCustomer.Name}"); + } + } + } + //</MultipleFormatsExample> +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.resx b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.resx new file mode 100644 index 0000000000..8b2ff64a11 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Form1.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Program.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Program.cs new file mode 100644 index 0000000000..3af3751c9e --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Program.cs @@ -0,0 +1,24 @@ +namespace ClipboardExample; + +static class Program +{ + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + + // Run the Multiple Formats Clipboard Test form + Application.Run(new ClipboardMultipleFormatsTest()); + + // Alternative forms available: + // Application.Run(new ClipboardTextTest()); + // Application.Run(new FileDropClipboardTest()); + // Application.Run(new Form1()); + // Application.Run(new ClipboardOperations.ClipboardImageTest()); + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Properties/Resources.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..c21825c756 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace ClipboardOperations.Properties { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ClipboardOperations.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Properties/Resources.resx b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Properties/Resources.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/Properties/Resources.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/image1.png b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/image1.png new file mode 100644 index 0000000000..f0f92ebe9f Binary files /dev/null and b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/image1.png differ diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/image2.png b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/image2.png new file mode 100644 index 0000000000..b36c430e65 Binary files /dev/null and b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/csharp/image2.png differ diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/ClipboardOperations.vbproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/ClipboardOperations.vbproj new file mode 100644 index 0000000000..7972313c2d --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/ClipboardOperations.vbproj @@ -0,0 +1,17 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <RootNamespace>ClipboardExample</RootNamespace> + <StartupObject>Sub Main</StartupObject> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + + <ItemGroup> + <Import Include="System.Data" /> + <Import Include="System.Drawing" /> + <Import Include="System.Windows.Forms" /> + </ItemGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.Designer.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.Designer.vb new file mode 100644 index 0000000000..0a21f031de --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.Designer.vb @@ -0,0 +1,31 @@ +<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> +Partial Class Form1 + Inherits System.Windows.Forms.Form + + 'Form overrides dispose to clean up the component list. + <System.Diagnostics.DebuggerNonUserCode()> + Protected Overrides Sub Dispose(disposing As Boolean) + Try + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + Finally + MyBase.Dispose(disposing) + End Try + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + <System.Diagnostics.DebuggerStepThrough()> + Private Sub InitializeComponent() + components = New System.ComponentModel.Container() + AutoScaleMode = AutoScaleMode.Font + ClientSize = New Size(800, 450) + Text = "Form1" + End Sub + +End Class diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb new file mode 100644 index 0000000000..46d9d921f3 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Form1.vb @@ -0,0 +1,167 @@ +Imports System.Collections.Specialized +Imports System.IO + +Public Class Form1 + + Public Sub New() + InitializeComponent() + '<ClipboardClear> + Clipboard.Clear() + '</ClipboardClear> + End Sub + + '<CustomerClass> + 'Customer class used in custom clipboard format examples. + Public Class Customer + + Public Property Name As String + + Public Sub New(ByVal name As String) + Me.Name = name + End Sub + + End Class + '</CustomerClass> + + '<SetTextExample> + ' Demonstrates SetAudio, ContainsAudio, and GetAudioStream. + Public Function SwapClipboardAudio( + ByVal replacementAudioStream As Stream) As Stream + + Dim returnAudioStream As Stream = Nothing + + If Clipboard.ContainsAudio() Then + returnAudioStream = Clipboard.GetAudioStream() + Clipboard.SetAudio(replacementAudioStream) + End If + + Return returnAudioStream + + End Function + + ' Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList + Public Function SwapClipboardFileDropList(ByVal replacementList As StringCollection) As StringCollection + + Dim returnList As StringCollection = Nothing + + If Clipboard.ContainsFileDropList() Then + returnList = Clipboard.GetFileDropList() + Clipboard.SetFileDropList(replacementList) + End If + + Return returnList + + End Function + + ' Demonstrates SetImage, ContainsImage, and GetImage. + Public Function SwapClipboardImage( + ByVal replacementImage As Image) As Image + + Dim returnImage As Image = Nothing + + If Clipboard.ContainsImage() Then + returnImage = Clipboard.GetImage() + Clipboard.SetImage(replacementImage) + End If + + Return returnImage + End Function + + ' Demonstrates SetText, ContainsText, and GetText. + Public Function SwapClipboardHtmlText( + ByVal replacementHtmlText As String) As String + + Dim returnHtmlText As String = Nothing + + If Clipboard.ContainsText(TextDataFormat.Html) Then + returnHtmlText = Clipboard.GetText(TextDataFormat.Html) + Clipboard.SetText(replacementHtmlText, TextDataFormat.Html) + End If + + Return returnHtmlText + + End Function + '</SetTextExample> + + '<CustomFormatExample> + ' Demonstrates SetData, ContainsData, and GetData + ' using a custom format name and a business object. + Public ReadOnly Property TestCustomFormat() As Customer + Get + Clipboard.SetDataAsJson("CustomerFormat", New Customer("Customer Name")) + + If Clipboard.ContainsData("CustomerFormat") Then + Dim customerData As Customer = Nothing + + If Clipboard.TryGetData("CustomerFormat", customerData) Then + Return customerData + End If + + End If + + Return Nothing + End Get + End Property + '</CustomFormatExample> + + '<MultipleFormatsExample> + ' Demonstrates how to use a DataObject to add + ' data to the Clipboard in multiple formats. + Public Sub TestClipboardMultipleFormats() + + Dim data As New DataObject() + + Dim customer As New Customer("Customer #2112") + Dim listViewItem As New ListViewItem($"Customer as ListViewItem {customer.Name}") + + ' Add a Customer object using the type as the format. + data.SetDataAsJson(customer) + + ' Add a ListViewItem object using a custom format name. + data.SetDataAsJson("ListViewItemFormat", listViewItem.Text) + + Clipboard.SetDataObject(data) + + ' Retrieve the data from the Clipboard. + Dim retrievedData As DataObject = CType(Clipboard.GetDataObject(), DataObject) + + If retrievedData.GetDataPresent("ListViewItemFormat") Then + Dim item As String = Nothing + + If retrievedData.TryGetData("ListViewItemFormat", item) Then + Dim recreatedListViewItem As New ListViewItem(item) + MessageBox.Show($"Data contains ListViewItem with text of {recreatedListViewItem.Text}") + End If + + End If + + If retrievedData.GetDataPresent(GetType(Customer)) Then + Dim newCustomer As Customer = Nothing + + If retrievedData.TryGetData(newCustomer) Then + MessageBox.Show($"Data contains Customer with name of {newCustomer.Name}") + End If + + End If + + End Sub + '</MultipleFormatsExample> + + '<GenericSetDataExample> + ' Demonstrates SetData, ContainsData, and GetData. + Public Function SwapClipboardFormattedData( + ByVal format As String, ByVal data As Object) As Object + + Dim returnObject As Object = Nothing + + If Clipboard.ContainsData(format) Then + returnObject = Clipboard.GetData(format) + Clipboard.SetData(format, data) + End If + + Return returnObject + + End Function + '</GenericSetDataExample> + +End Class diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Program.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Program.vb new file mode 100644 index 0000000000..236767207e --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-add-data-to-the-clipboard/net/vb/Program.vb @@ -0,0 +1,11 @@ +Friend Module Program + + <STAThread()> + Friend Sub Main(args As String()) + Application.SetHighDpiMode(HighDpiMode.SystemAware) + Application.EnableVisualStyles() + Application.SetCompatibleTextRenderingDefault(False) + Application.Run(New Form1) + End Sub + +End Module diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ClipboardUsage.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ClipboardUsage.cs new file mode 100644 index 0000000000..91396ce1b4 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ClipboardUsage.cs @@ -0,0 +1,42 @@ +using System; +using System.Reflection.Metadata; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class ClipboardUsageExample + { + public class MyCustomType + { + public string Data { get; set; } + } + + // <ClipboardUsage> + // Use the resolver with clipboard operations + private static Type SecureTypeResolver(TypeName typeName) + { + // Implementation from SecureTypeResolver example + // ... (allow-list implementation here) + throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted"); + } + + public static void UseSecureTypeResolver() + { + // Retrieve legacy data using the secure type resolver + if (Clipboard.TryGetData("LegacyData", SecureTypeResolver, out MyCustomType data)) + { + ProcessLegacyData(data); + } + else + { + Console.WriteLine("No compatible data found on clipboard"); + } + } + // </ClipboardUsage> + + private static void ProcessLegacyData(MyCustomType data) + { + Console.WriteLine($"Processing legacy data: {data.Data}"); + } + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Form1.Designer.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Form1.Designer.cs new file mode 100644 index 0000000000..ca8ec69d19 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Form1.Designer.cs @@ -0,0 +1,38 @@ +namespace ProjectCS; + +partial class Form1 +{ + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Text = "Form1"; + } + + #endregion +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Form1.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Form1.cs new file mode 100644 index 0000000000..357af0c6a5 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Form1.cs @@ -0,0 +1,9 @@ +namespace ProjectCS; + +public partial class Form1 : Form +{ + public Form1() + { + InitializeComponent(); + } +} diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Program.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Program.cs new file mode 100644 index 0000000000..3e3a06d61a --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/Program.cs @@ -0,0 +1,16 @@ +namespace ProjectCS; + +static class Program +{ + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new Form1()); + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ProjectCS.csproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ProjectCS.csproj new file mode 100644 index 0000000000..8785ccdcdb --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/ProjectCS.csproj @@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <Nullable>enable</Nullable> + <UseWindowsForms>true</UseWindowsForms> + <ImplicitUsings>enable</ImplicitUsings> + <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization> + </PropertyGroup> + + <!-- <PackageReference> --> + <ItemGroup> + <PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0*-*"/> + </ItemGroup> + <!-- </PackageReference> --> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/SecureTypeResolver.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/SecureTypeResolver.cs new file mode 100644 index 0000000000..63f75b4e42 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/SecureTypeResolver.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Reflection.Metadata; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class SecureTypeResolverExample + { + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + public class AppSettings + { + public string Theme { get; set; } + public bool AutoSave { get; set; } + } + + // <SecureTypeResolver> + // Create a security-focused type resolver + private static Type SecureTypeResolver(TypeName typeName) + { + // Explicit allow-list of permitted types—add only what you need + var allowedTypes = new Dictionary<string, Type> + { + ["MyApp.Person"] = typeof(Person), + ["MyApp.AppSettings"] = typeof(AppSettings), + ["System.String"] = typeof(string), + ["System.Int32"] = typeof(int), + // Add only the specific types your application requires + }; + + // Only allow explicitly listed types - exact string match required + if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType)) + { + return allowedType; + } + + // Reject any type not in the allow-list with clear error message + throw new InvalidOperationException( + $"Type '{typeName.FullName}' is not permitted for clipboard deserialization"); + } + // </SecureTypeResolver> + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/TestConfiguration.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/TestConfiguration.cs new file mode 100644 index 0000000000..cbd0f00472 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/TestConfiguration.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Reflection.Metadata; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class TestConfigurationExample + { + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + // <TestConfiguration> + public static void TestBinaryFormatterConfiguration() + { + // Test data to verify configuration + var testPerson = new Person { Name = "Test User", Age = 30 }; + + try + { + // Test storing data (this should work with proper configuration) + Clipboard.SetData("TestPerson", testPerson); + Console.WriteLine("Successfully stored test data on clipboard"); + + // Test retrieving with type resolver + if (Clipboard.TryGetData("TestPerson", SecureTypeResolver, out Person retrievedPerson)) + { + Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}"); + } + else + { + Console.WriteLine("Failed to retrieve test data"); + } + + // Test that unauthorized types are rejected + try + { + Clipboard.TryGetData("TestPerson", UnauthorizedTypeResolver, out Person _); + Console.WriteLine("ERROR: Unauthorized type was not rejected!"); + } + catch (InvalidOperationException ex) + { + Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Configuration test failed: {ex.Message}"); + } + } + + private static Type SecureTypeResolver(TypeName typeName) + { + var allowedTypes = new Dictionary<string, Type> + { + ["ClipboardExamples.Person"] = typeof(Person), + }; + + if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType)) + { + return allowedType; + } + + throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted"); + } + + private static Type UnauthorizedTypeResolver(TypeName typeName) + { + // Intentionally restrictive resolver to test rejection + throw new InvalidOperationException($"No types are permitted by this test resolver"); + } + // </TestConfiguration> + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/runtimeconfig.json b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/runtimeconfig.json new file mode 100644 index 0000000000..6a7c3d90bd --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/csharp/runtimeconfig.json @@ -0,0 +1,7 @@ +{ + "runtimeOptions": { + "configProperties": { + "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization": true + } + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/ClipboardUsage.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/ClipboardUsage.vb new file mode 100644 index 0000000000..32c3bb2b53 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/ClipboardUsage.vb @@ -0,0 +1,36 @@ +Imports System +Imports System.Reflection.Metadata +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class ClipboardUsageExample + + Public Class MyCustomType + Public Property Data As String + End Class + + ' <ClipboardUsage> + ' Use the resolver with clipboard operations + Private Shared Function SecureTypeResolver(typeName As TypeName) As Type + ' Implementation from SecureTypeResolver example + ' ... (allow-list implementation here) + Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted") + End Function + + Public Shared Sub UseSecureTypeResolver() + ' Retrieve legacy data using the secure type resolver + Dim data As MyCustomType = Nothing + If Clipboard.TryGetData("LegacyData", AddressOf SecureTypeResolver, data) Then + ProcessLegacyData(data) + Else + Console.WriteLine("No compatible data found on clipboard") + End If + End Sub + ' </ClipboardUsage> + + Private Shared Sub ProcessLegacyData(data As MyCustomType) + Console.WriteLine($"Processing legacy data: {data.Data}") + End Sub + + End Class +End Namespace \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Form1.Designer.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Form1.Designer.vb new file mode 100644 index 0000000000..0a21f031de --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Form1.Designer.vb @@ -0,0 +1,31 @@ +<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> +Partial Class Form1 + Inherits System.Windows.Forms.Form + + 'Form overrides dispose to clean up the component list. + <System.Diagnostics.DebuggerNonUserCode()> + Protected Overrides Sub Dispose(disposing As Boolean) + Try + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + Finally + MyBase.Dispose(disposing) + End Try + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + <System.Diagnostics.DebuggerStepThrough()> + Private Sub InitializeComponent() + components = New System.ComponentModel.Container() + AutoScaleMode = AutoScaleMode.Font + ClientSize = New Size(800, 450) + Text = "Form1" + End Sub + +End Class diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Form1.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Form1.vb new file mode 100644 index 0000000000..17d659563f --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Form1.vb @@ -0,0 +1,3 @@ +Public Class Form1 + +End Class diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Program.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Program.vb new file mode 100644 index 0000000000..236767207e --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/Program.vb @@ -0,0 +1,11 @@ +Friend Module Program + + <STAThread()> + Friend Sub Main(args As String()) + Application.SetHighDpiMode(HighDpiMode.SystemAware) + Application.EnableVisualStyles() + Application.SetCompatibleTextRenderingDefault(False) + Application.Run(New Form1) + End Sub + +End Module diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/ProjectVB.vbproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/ProjectVB.vbproj new file mode 100644 index 0000000000..06a933be74 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/ProjectVB.vbproj @@ -0,0 +1,22 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <RootNamespace>ProjectVB</RootNamespace> + <StartupObject>Sub Main</StartupObject> + <UseWindowsForms>true</UseWindowsForms> + <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization> + </PropertyGroup> + + <ItemGroup> + <Import Include="System.Data" /> + <Import Include="System.Drawing" /> + <Import Include="System.Windows.Forms" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0*-*"/> + </ItemGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/SecureTypeResolver.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/SecureTypeResolver.vb new file mode 100644 index 0000000000..a5fa631c12 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/SecureTypeResolver.vb @@ -0,0 +1,43 @@ +Imports System +Imports System.Collections.Generic +Imports System.Reflection.Metadata +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class SecureTypeResolverExample + + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + Public Class AppSettings + Public Property Theme As String + Public Property AutoSave As Boolean + End Class + + ' <SecureTypeResolver> + ' Create a security-focused type resolver + Private Shared Function SecureTypeResolver(typeName As TypeName) As Type + ' Explicit allow-list of permitted types—add only what you need + Dim allowedTypes As New Dictionary(Of String, Type) From { + {"MyApp.Person", GetType(Person)}, + {"MyApp.AppSettings", GetType(AppSettings)}, + {"System.String", GetType(String)}, + {"System.Int32", GetType(Integer)} + } ' Add only the specific types your application requires + + ' Only allow explicitly listed types - exact string match required + Dim allowedType As Type = Nothing + If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then + Return allowedType + End If + + ' Reject any type not in the allow-list with clear error message + Throw New InvalidOperationException( + $"Type '{typeName.FullName}' is not permitted for clipboard deserialization") + End Function + ' </SecureTypeResolver> + + End Class +End Namespace \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/TestConfiguration.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/TestConfiguration.vb new file mode 100644 index 0000000000..24735137eb --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/TestConfiguration.vb @@ -0,0 +1,66 @@ +Imports System +Imports System.Collections.Generic +Imports System.Reflection.Metadata +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class TestConfigurationExample + + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + ' <TestConfiguration> + Public Shared Sub TestBinaryFormatterConfiguration() + ' Test data to verify configuration + Dim testPerson As New Person With {.Name = "Test User", .Age = 30} + + Try + ' Test storing data (this should work with proper configuration) + Clipboard.SetData("TestPerson", testPerson) + Console.WriteLine("Successfully stored test data on clipboard") + + ' Test retrieving with type resolver + Dim retrievedPerson As Person = Nothing + If Clipboard.TryGetData("TestPerson", AddressOf SecureTypeResolver, retrievedPerson) Then + Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}") + Else + Console.WriteLine("Failed to retrieve test data") + End If + + ' Test that unauthorized types are rejected + Try + Dim testResult As Person = Nothing + Clipboard.TryGetData("TestPerson", AddressOf UnauthorizedTypeResolver, testResult) + Console.WriteLine("ERROR: Unauthorized type was not rejected!") + Catch ex As InvalidOperationException + Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}") + End Try + + Catch ex As Exception + Console.WriteLine($"Configuration test failed: {ex.Message}") + End Try + End Sub + + Private Shared Function SecureTypeResolver(typeName As TypeName) As Type + Dim allowedTypes As New Dictionary(Of String, Type) From { + {"ClipboardExamples.Person", GetType(Person)} + } + + Dim allowedType As Type = Nothing + If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then + Return allowedType + End If + + Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted") + End Function + + Private Shared Function UnauthorizedTypeResolver(typeName As TypeName) As Type + ' Intentionally restrictive resolver to test rejection + Throw New InvalidOperationException($"No types are permitted by this test resolver") + End Function + ' </TestConfiguration> + + End Class +End Namespace \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/runtimeconfig.json b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/runtimeconfig.json new file mode 100644 index 0000000000..6a7c3d90bd --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-enable-binaryformatter-clipboard-support/vb/runtimeconfig.json @@ -0,0 +1,7 @@ +{ + "runtimeOptions": { + "configProperties": { + "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization": true + } + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/Program.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/Program.cs new file mode 100644 index 0000000000..906bbabb41 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/Program.cs @@ -0,0 +1,16 @@ +using System; +using System.Windows.Forms; + +namespace RetrieveClipboardData +{ + static class Program + { + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/RetrieveClipboardData.csproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/RetrieveClipboardData.csproj new file mode 100644 index 0000000000..d161c73f49 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/RetrieveClipboardData.csproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net48</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs similarity index 91% rename from dotnet-desktop-guide/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs rename to dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs index 3690bf36f5..7245adb62f 100644 --- a/dotnet-desktop-guide/samples/snippets/csharp/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/CS/form1.cs +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/csharp/form1.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.Windows.Forms; public class Form1 : Form { - //<snippet100> + //<HelperMethods> [Serializable] public class Customer { @@ -18,16 +18,14 @@ public string Name set { nameValue = value; } } } - //</snippet100> + //</HelperMethods> public Form1() { - //<snippet1> Clipboard.Clear(); - //</snippet1> } - //<snippet3> + //<RetrieveCustomFormat> // Demonstrates SetData, ContainsData, and GetData // using a custom format name and a business object. public Customer TestCustomFormat @@ -42,9 +40,9 @@ public Customer TestCustomFormat return null; } } - //</snippet3> + //</RetrieveCustomFormat> - //<snippet4> + //<RetrieveMultipleFormats> // Demonstrates how to use a DataObject to add // data to the Clipboard in multiple formats. public void TestClipboardMultipleFormats() @@ -81,9 +79,8 @@ public void TestClipboardMultipleFormats() } } } - //</snippet4> + //</RetrieveMultipleFormats> - //<snippet10> // Demonstrates SetData, ContainsData, and GetData. public Object SwapClipboardFormattedData(String format, Object data) { @@ -95,10 +92,8 @@ public Object SwapClipboardFormattedData(String format, Object data) } return returnObject; } - //</snippet10> - //<snippet2> - //<snippet20> + //<RetrieveCommonFormat> // Demonstrates SetAudio, ContainsAudio, and GetAudioStream. public System.IO.Stream SwapClipboardAudio( System.IO.Stream replacementAudioStream) @@ -111,9 +106,7 @@ public System.IO.Stream SwapClipboardAudio( } return returnAudioStream; } - //</snippet20> - //<snippet30> // Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList public System.Collections.Specialized.StringCollection SwapClipboardFileDropList( @@ -127,9 +120,7 @@ public System.Collections.Specialized.StringCollection } return returnList; } - //</snippet30> - //<snippet40> // Demonstrates SetImage, ContainsImage, and GetImage. public System.Drawing.Image SwapClipboardImage( System.Drawing.Image replacementImage) @@ -142,9 +133,7 @@ public System.Drawing.Image SwapClipboardImage( } return returnImage; } - //</snippet40> - //<snippet50> // Demonstrates SetText, ContainsText, and GetText. public String SwapClipboardHtmlText(String replacementHtmlText) { @@ -156,6 +145,5 @@ public String SwapClipboardHtmlText(String replacementHtmlText) } return returnHtmlText; } - //</snippet50> - //</snippet2> -} + //</RetrieveCommonFormat> +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/Program.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/Program.vb new file mode 100644 index 0000000000..d1d61fe1e2 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/Program.vb @@ -0,0 +1,11 @@ +Imports System +Imports System.Windows.Forms + +Module Program + <STAThread> + Sub Main() + Application.EnableVisualStyles() + Application.SetCompatibleTextRenderingDefault(False) + Application.Run(New Form1()) + End Sub +End Module \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/RetrieveClipboardData.vbproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/RetrieveClipboardData.vbproj new file mode 100644 index 0000000000..d161c73f49 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/RetrieveClipboardData.vbproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net48</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb similarity index 92% rename from dotnet-desktop-guide/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb rename to dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb index d65f67f1f6..7f0583c13d 100644 --- a/dotnet-desktop-guide/samples/snippets/visualbasic/VS_Snippets_Winforms/System.Windows.Forms.Clipboard/vb/form1.vb +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/framework/vb/form1.vb @@ -1,9 +1,9 @@ -Imports System.Windows.Forms +Imports System.Windows.Forms Public Class Form1 Inherits Form - '<snippet100> + '<HelperMethods> <Serializable()> Public Class Customer Private nameValue As String = String.Empty @@ -22,15 +22,13 @@ Public Class Form1 End Property End Class - '</snippet100> + '</HelperMethods> Public Sub New() - '<snippet1> Clipboard.Clear() - '</snippet1> End Sub - '<snippet3> + '<RetrieveCustomFormat> ' Demonstrates SetData, ContainsData, and GetData ' using a custom format name and a business object. Public ReadOnly Property TestCustomFormat() As Customer @@ -44,9 +42,9 @@ Public Class Form1 Return Nothing End Get End Property - '</snippet3> + '</RetrieveCustomFormat> - '<snippet4> + '<RetrieveMultipleFormats> ' Demonstrates how to use a DataObject to add ' data to the Clipboard in multiple formats. Public Sub TestClipboardMultipleFormats() @@ -88,9 +86,8 @@ Public Class Form1 End If End Sub - '</snippet4> + '</RetrieveMultipleFormats> - '<snippet10> ' Demonstrates SetData, ContainsData, and GetData. Public Function SwapClipboardFormattedData( _ ByVal format As String, ByVal data As Object) As Object @@ -105,10 +102,8 @@ Public Class Form1 Return returnObject End Function - '</snippet10> - '<snippet2> - '<snippet20> + '<RetrieveCommonFormat> ' Demonstrates SetAudio, ContainsAudio, and GetAudioStream. Public Function SwapClipboardAudio( _ ByVal replacementAudioStream As System.IO.Stream) _ @@ -124,9 +119,7 @@ Public Class Form1 Return returnAudioStream End Function - '</snippet20> - '<snippet30> ' Demonstrates SetFileDropList, ContainsFileDroList, and GetFileDropList Public Function SwapClipboardFileDropList(ByVal replacementList _ As System.Collections.Specialized.StringCollection) _ @@ -144,9 +137,7 @@ Public Class Form1 Return returnList End Function - '</snippet30> - '<snippet40> ' Demonstrates SetImage, ContainsImage, and GetImage. Public Function SwapClipboardImage( _ ByVal replacementImage As System.Drawing.Image) _ @@ -160,10 +151,9 @@ Public Class Form1 End If Return returnImage + End Function - '</snippet40> - '<snippet50> ' Demonstrates SetText, ContainsText, and GetText. Public Function SwapClipboardHtmlText( _ ByVal replacementHtmlText As String) As String @@ -178,7 +168,6 @@ Public Class Form1 Return returnHtmlText End Function - '</snippet50> - '</snippet2> + '</RetrieveCommonFormat> -End Class +End Class \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs new file mode 100644 index 0000000000..93fde16f00 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/MainForm.cs @@ -0,0 +1,159 @@ +using System.Collections.Specialized; +using System.Drawing; +using System.Windows.Forms; + +namespace RetrieveClipboardData; + +public class MainForm : Form +{ + //<HelperMethods> + [Serializable] + public class Customer + { + private string nameValue = string.Empty; + public Customer(string name) + { + nameValue = name; + } + public string Name + { + get { return nameValue; } + set { nameValue = value; } + } + } + //</HelperMethods> + + public MainForm() + { + Clipboard.Clear(); + } + + //<RetrieveCustomFormat> + // Demonstrates TryGetData using a custom format name and a business object. + // Note: In .NET 10, SetData for objects is no longer supported, + // so this example shows how to retrieve data that might have been + // set by other applications or earlier .NET versions. + public Customer? TestCustomFormat + { + get + { + // For demonstration, we'll use string data instead of objects + // since SetData for objects is no longer supported in .NET 10 + if (Clipboard.TryGetData("CustomerFormat", out object? data)) + { + return data as Customer; + } + return null; + } + } + //</RetrieveCustomFormat> + + //<RetrieveMultipleFormats> + // Demonstrates how to retrieve data from the Clipboard in multiple formats + // using TryGetData instead of the obsoleted GetData method. + public void TestClipboardMultipleFormats() + { + IDataObject? dataObject = Clipboard.GetDataObject(); + + if (dataObject != null) + { + // Check for custom format + if (dataObject.GetDataPresent("CustomFormat")) + { + if (Clipboard.TryGetData("CustomFormat", out object? customData)) + { + if (customData is ListViewItem item) + { + MessageBox.Show(item.Text); + } + else if (customData is string stringData) + { + MessageBox.Show(stringData); + } + } + } + + // Check for Customer type - note that object serialization + // through SetData is no longer supported in .NET 10 + if (dataObject.GetDataPresent(typeof(Customer))) + { + if (Clipboard.TryGetData(typeof(Customer).FullName!, out object? customerData)) + { + if (customerData is Customer customer) + { + MessageBox.Show(customer.Name); + } + } + } + + // For modern .NET 10 applications, prefer using standard formats + if (Clipboard.ContainsText()) + { + string text = Clipboard.GetText(); + MessageBox.Show($"Text data: {text}"); + } + } + } + //</RetrieveMultipleFormats> + + //<RetrieveCommonFormat> + // Demonstrates TryGetData methods for common formats. + // These methods are preferred over the older Get* methods. + public Stream? SwapClipboardAudio(Stream replacementAudioStream) + { + Stream? returnAudioStream = null; + if (Clipboard.ContainsAudio()) + { + returnAudioStream = Clipboard.GetAudioStream(); + Clipboard.SetAudio(replacementAudioStream); + } + return returnAudioStream; + } + + // Demonstrates TryGetData for file drop lists + public StringCollection? SwapClipboardFileDropList(StringCollection replacementList) + { + StringCollection? returnList = null; + if (Clipboard.ContainsFileDropList()) + { + returnList = Clipboard.GetFileDropList(); + Clipboard.SetFileDropList(replacementList); + } + return returnList; + } + + // Demonstrates TryGetData for images + public Image? SwapClipboardImage(Image replacementImage) + { + Image? returnImage = null; + if (Clipboard.ContainsImage()) + { + returnImage = Clipboard.GetImage(); + Clipboard.SetImage(replacementImage); + } + return returnImage; + } + + // Demonstrates TryGetData for text in HTML format + public string? SwapClipboardHtmlText(string replacementHtmlText) + { + string? returnHtmlText = null; + if (Clipboard.ContainsText(TextDataFormat.Html)) + { + returnHtmlText = Clipboard.GetText(TextDataFormat.Html); + Clipboard.SetText(replacementHtmlText, TextDataFormat.Html); + } + return returnHtmlText; + } + + // Example of using TryGetData for custom string-based data + public string? GetCustomStringData(string format) + { + if (Clipboard.TryGetData(format, out object? data)) + { + return data as string; + } + return null; + } + //</RetrieveCommonFormat> +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/Program.cs b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/Program.cs new file mode 100644 index 0000000000..c7a30edb1b --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/Program.cs @@ -0,0 +1,18 @@ +using System.Windows.Forms; + +namespace RetrieveClipboardData; + +internal static class Program +{ + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new MainForm()); + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/RetrieveClipboardData.csproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/RetrieveClipboardData.csproj new file mode 100644 index 0000000000..00df3eb651 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/csharp/RetrieveClipboardData.csproj @@ -0,0 +1,11 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb new file mode 100644 index 0000000000..32ba9e19a3 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/MainForm.vb @@ -0,0 +1,180 @@ +Imports System.Collections.Specialized +Imports System.Drawing +Imports System.Windows.Forms + +Namespace RetrieveClipboardData + + Public Class MainForm + Inherits Form + + '<HelperMethods> + <Serializable()> + Public Class Customer + + Private nameValue As String = String.Empty + + Public Sub New(ByVal name As String) + nameValue = name + End Sub + + Public Property Name() As String + Get + Return nameValue + End Get + Set(ByVal value As String) + nameValue = value + End Set + End Property + + End Class + '</HelperMethods> + + Public Sub New() + Clipboard.Clear() + End Sub + + '<RetrieveCustomFormat> + ' Demonstrates TryGetData using a custom format name and a business object. + ' Note: In .NET 10, SetData for objects is no longer supported, + ' so this example shows how to retrieve data that might have been + ' set by other applications or earlier .NET versions. + Public ReadOnly Property TestCustomFormat() As Customer + Get + Dim data As Object = Nothing + ' For demonstration, we'll use string data instead of objects + ' since SetData for objects is no longer supported in .NET 10 + If Clipboard.TryGetData("CustomerFormat", data) Then + Return TryCast(data, Customer) + End If + Return Nothing + End Get + End Property + '</RetrieveCustomFormat> + + '<RetrieveMultipleFormats> + ' Demonstrates how to retrieve data from the Clipboard in multiple formats + ' using TryGetData instead of the obsoleted GetData method. + Public Sub TestClipboardMultipleFormats() + + Dim dataObject As IDataObject = Clipboard.GetDataObject() + + If dataObject IsNot Nothing Then + + ' Check for custom format + If dataObject.GetDataPresent("CustomFormat") Then + + Dim customData As Object = Nothing + If Clipboard.TryGetData("CustomFormat", customData) Then + + Dim item As ListViewItem = TryCast(customData, ListViewItem) + If item IsNot Nothing Then + MessageBox.Show(item.Text) + ElseIf TypeOf customData Is String Then + MessageBox.Show(CStr(customData)) + End If + + End If + + End If + + ' Check for Customer type - note that object serialization + ' through SetData is no longer supported in .NET 10 + If dataObject.GetDataPresent(GetType(Customer)) Then + + Dim customerData As Object = Nothing + If Clipboard.TryGetData(GetType(Customer).FullName, customerData) Then + + Dim customer As Customer = TryCast(customerData, Customer) + If customer IsNot Nothing Then + MessageBox.Show(customer.Name) + End If + + End If + + End If + + ' For modern .NET 10 applications, prefer using standard formats + If Clipboard.ContainsText() Then + Dim text As String = Clipboard.GetText() + MessageBox.Show($"Text data: {text}") + End If + + End If + + End Sub + '</RetrieveMultipleFormats> + + '<RetrieveCommonFormat> + ' Demonstrates TryGetData methods for common formats. + ' These methods are preferred over the older Get* methods. + Public Function SwapClipboardAudio(ByVal replacementAudioStream As System.IO.Stream) As System.IO.Stream + + Dim returnAudioStream As System.IO.Stream = Nothing + + If Clipboard.ContainsAudio() Then + returnAudioStream = Clipboard.GetAudioStream() + Clipboard.SetAudio(replacementAudioStream) + End If + + Return returnAudioStream + + End Function + + ' Demonstrates TryGetData for file drop lists + Public Function SwapClipboardFileDropList(ByVal replacementList As StringCollection) As StringCollection + + Dim returnList As StringCollection = Nothing + + If Clipboard.ContainsFileDropList() Then + returnList = Clipboard.GetFileDropList() + Clipboard.SetFileDropList(replacementList) + End If + + Return returnList + + End Function + + ' Demonstrates TryGetData for images + Public Function SwapClipboardImage(ByVal replacementImage As Image) As Image + + Dim returnImage As Image = Nothing + + If Clipboard.ContainsImage() Then + returnImage = Clipboard.GetImage() + Clipboard.SetImage(replacementImage) + End If + + Return returnImage + + End Function + + ' Demonstrates TryGetData for text in HTML format + Public Function SwapClipboardHtmlText(ByVal replacementHtmlText As String) As String + + Dim returnHtmlText As String = Nothing + + If Clipboard.ContainsText(TextDataFormat.Html) Then + returnHtmlText = Clipboard.GetText(TextDataFormat.Html) + Clipboard.SetText(replacementHtmlText, TextDataFormat.Html) + End If + + Return returnHtmlText + + End Function + + ' Example of using TryGetData for custom string-based data + Public Function GetCustomStringData(ByVal format As String) As String + + Dim data As Object = Nothing + If Clipboard.TryGetData(format, data) Then + Return TryCast(data, String) + End If + + Return Nothing + + End Function + '</RetrieveCommonFormat> + + End Class + +End Namespace \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/Program.vb b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/Program.vb new file mode 100644 index 0000000000..796fc42ff7 --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/Program.vb @@ -0,0 +1,14 @@ +Imports System.Windows.Forms +Imports System + +Friend Module Program + + <STAThread()> + Friend Sub Main(args As String()) + Application.SetHighDpiMode(HighDpiMode.SystemAware) + Application.EnableVisualStyles() + Application.SetCompatibleTextRenderingDefault(False) + Application.Run(New RetrieveClipboardData.MainForm()) + End Sub + +End Module diff --git a/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/RetrieveClipboardData.vbproj b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/RetrieveClipboardData.vbproj new file mode 100644 index 0000000000..49b98949ec --- /dev/null +++ b/dotnet-desktop-guide/winforms/advanced/snippets/how-to-retrieve-data-from-the-clipboard/net/vb/RetrieveClipboardData.vbproj @@ -0,0 +1,9 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/advanced/toc.yml b/dotnet-desktop-guide/winforms/advanced/toc.yml index 19d11c02fa..369f6bbbd1 100644 --- a/dotnet-desktop-guide/winforms/advanced/toc.yml +++ b/dotnet-desktop-guide/winforms/advanced/toc.yml @@ -330,6 +330,8 @@ items: href: how-to-add-data-to-the-clipboard.md - name: "How to: Retrieve Data from the Clipboard" href: how-to-retrieve-data-from-the-clipboard.md + - name: "How to: Enable BinaryFormatter clipboard support (not recommended)" + href: how-to-enable-binaryformatter-clipboard-support.md - name: Networking in Windows Forms Applications href: networking-in-windows-forms-applications.md - name: Globalizing Windows Forms diff --git a/dotnet-desktop-guide/winforms/migration/clipboard-dataobject-net10.md b/dotnet-desktop-guide/winforms/migration/clipboard-dataobject-net10.md new file mode 100644 index 0000000000..d1d9c83255 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/clipboard-dataobject-net10.md @@ -0,0 +1,391 @@ +--- +title: "Clipboard and DataObject changes in .NET 10" +description: "Learn about the major clipboard and drag-and-drop changes in .NET 10, including new type-safe APIs, JSON serialization, and migration from BinaryFormatter." +author: adegeo +ms.author: adegeo +ms.service: dotnet-desktop +ms.topic: concept-article +ms.date: 09/26/2025 +dev_langs: + - "csharp" + - "vb" +ms.custom: + - copilot-scenario-highlight +ai-usage: ai-assisted + +#customer intent: As a Windows Forms developer, I want to understand the clipboard and DataObject changes in .NET 10 so that I can upgrade my applications and use the new type-safe APIs. + +--- + +# Windows Forms clipboard and DataObject changes in .NET 10 + +This article shows you how to upgrade your Windows Forms clipboard and drag-and-drop operations to the new type-safe APIs in .NET 10. You'll learn how to use the new <xref:System.Windows.Forms.Clipboard.TryGetData*?displayProperty=nameWithType> and <xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)?displayProperty=nameWithType> methods, understand which built-in types work without changes, and discover strategies for handling custom types and legacy data after the removal of `BinaryFormatter`. + +`BinaryFormatter` was removed from the runtime in .NET 9 because of security vulnerabilities. This change broke clipboard and drag-and-drop operations with custom objects. .NET 10 introduces new APIs that use JSON serialization and type-safe methods to restore this functionality, improve security, and provide better error handling and cross-process compatibility. + +One significant change is that <xref:System.Windows.Clipboard.SetData(System.String,System.Object)?displayProperty=nameWithType> no longer works with custom types. It silently fails without storing data on the clipboard. <xref:System.Windows.Forms.Clipboard.GetData(System.String)?displayProperty=nameWithType> is obsolete in .NET 10 and shouldn't be used, even for built-in types. Use the new <xref:System.Windows.Forms.Clipboard.TryGetData*?displayProperty=nameWithType> and <xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)?displayProperty=nameWithType> methods for type-safe operations and JSON serialization of custom objects. + +The following sections provide detailed migration guidance, explain which types work without changes, and show how to handle both new development and legacy data scenarios. + +## Prerequisites + +Before you continue, review these concepts: + +- How applications used `BinaryFormatter` in clipboard and drag-and-drop scenarios before .NET 9. +- The security vulnerabilities that led to the removal of `BinaryFormatter`. +- How to work with `System.Text.Json` serialization patterns and their limitations. + +For more information, see these articles: + +- [Deserialization risks in use of BinaryFormatter and related types](/dotnet/standard/serialization/binaryformatter-security-guide). +- [BinaryFormatter migration guide](/dotnet/standard/serialization/binaryformatter-migration-guide/). + +## Breaking changes from BinaryFormatter removal + +Removing `BinaryFormatter` in .NET 9 fundamentally changes how Windows Forms handles clipboard and drag-and-drop operations with custom types. These changes affect existing code patterns and require careful migration to maintain functionality. + +### Custom types no longer serialize automatically + +In .NET 8 and earlier, you could place any serializable custom object on the clipboard by calling `SetData()`. The `BinaryFormatter` handled serialization automatically. Starting with .NET 9, this pattern no longer works. The `SetData()` method silently fails for custom types and doesn't store data on the clipboard. + +The following code no longer works: + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/ObsoletePatterns.cs" id="ObsoleteCustomType"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/ObsoletePatterns.vb" id="ObsoleteCustomType"::: + +#### What you might see + +- The `SetData()` method completes without throwing an exception but doesn't store the data. +- The clipboard operation appears to succeed from your application's perspective. +- Later attempts to retrieve the data with `GetData()` return `null`. + +#### Migration guidance + +Use the new [`SetDataAsJson<T>()`](xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)) method or manually serialize to a `string` or `byte[]`. For details, see the [Work with custom types](#work-with-custom-types) section. + +### GetData() is obsolete - use TryGetData\<T>() instead + +The legacy `GetData()` method is obsolete in .NET 10. Even if it sometimes returns data, you should migrate to the new type-safe [`TryGetData<T>()`](xref:System.Windows.Forms.Clipboard.TryGetData*) methods for better error handling and type safety. + +**Obsolete code to avoid:** + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/ObsoletePatterns.cs" id="ObsoleteGetData"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/ObsoletePatterns.vb" id="ObsoleteGetData"::: + +**Modern approach using TryGetData\<T>():** + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/ModernApproach.cs" id="ModernTryGetData"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/ModernApproach.vb" id="ModernTryGetData"::: + +#### Benefits of TryGetData\<T>() + +- Type safety: No need for casting—the method returns the exact type you request. +- Clear error handling: Returns a Boolean success indicator instead of using null or exception patterns. +- Future-proof: Designed to work with new serialization methods and legacy data support. + +#### How to identify affected code + +Look for: + +- Any `GetData()` calls, as the entire method is obsolete regardless of data type. +- `DataObject.GetData()` and `IDataObject.GetData()` usage in drag-and-drop operations. + +#### Migration guidance + +Replace all `GetData()` usage with type-safe [`TryGetData<T>()`](xref:System.Windows.Forms.Clipboard.TryGetData*) methods. For comprehensive examples of all overloads, see the [New type-safe APIs](#new-type-safe-apis) section. + +## New type-safe APIs + +.NET 10 introduces three new API families that provide type safety, better error handling, and JSON serialization support for clipboard and drag-and-drop operations: + +- <xref:System.Windows.Forms.Clipboard.TryGetData*> methods for retrieving data +- <xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)> methods for storing data +- <xref:System.Windows.Forms.ITypedDataObject> interface for drag-and-drop operations + +### TryGetData\<T>() methods + +The `TryGetData<T>()` family replaces the obsolete `GetData()` method. It provides type-safe retrieval and clear success or failure indication for your clipboard operations. + +#### Basic type-safe retrieval + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/TypeSafeRetrieval.cs" id="BasicTypeSafeRetrieval"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/TypeSafeRetrieval.vb" id="BasicTypeSafeRetrieval"::: + +#### Custom JSON types + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/TypeSafeRetrieval.cs" id="CustomJsonTypes"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/TypeSafeRetrieval.vb" id="CustomJsonTypes"::: + +#### Use a type resolver for legacy binary data (requires BinaryFormatter; not recommended) + +> [!WARNING] +> Type resolvers only work when BinaryFormatter support is enabled, which isn't recommended due to security risks. For more information, see [Enable BinaryFormatter clipboard support (not recommended)](../advanced/how-to-enable-binaryformatter-clipboard-support.md). + +Type resolvers let you handle legacy binary data by mapping type names to actual types during deserialization. + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/TypeResolver.cs" id="TypeResolverExample"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/TypeResolver.vb" id="TypeResolverExample"::: + +**Important security considerations for type resolvers:** + +- Always use an explicit allowlist of permitted types. +- Never allow dynamic type loading or assembly resolution. +- Validate type names before mapping to actual types. +- Throw exceptions for any unauthorized or unknown types. +- Use this approach only as a temporary bridge during migration. + +### SetDataAsJson\<T>() methods + +These methods provide automatic JSON serialization using `System.Text.Json` with type-safe storage. + +#### Automatic format inference + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/SetDataAsJsonExamples.cs" id="AutomaticFormatInference"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/SetDataAsJsonExamples.vb" id="AutomaticFormatInference"::: + +#### Specify a custom format + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/SetDataAsJsonExamples.cs" id="CustomFormat"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/SetDataAsJsonExamples.vb" id="CustomFormat"::: + +### ITypedDataObject interface + +The <xref:System.Windows.Forms.ITypedDataObject> interface enables type-safe drag-and-drop operations by extending <xref:System.Windows.Forms.IDataObject> with typed methods. + +Starting with .NET 10, <xref:System.Windows.Forms.DataObject> (a common type in drag-and-drop scenarios) implements `ITypedDataObject`. + +#### Use ITypedDataObject in drag-and-drop scenarios + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/ITypedDataObjectExamples.cs" id="DragDropUsage"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/ITypedDataObjectExamples.vb" id="DragDropUsage"::: + +## Types that don't require JSON serialization + +Many built-in .NET types work with clipboard operations without requiring JSON serialization or `BinaryFormatter` support. These types automatically serialize into the .NET Remoting Binary Format (NRBF), which provides efficient storage and maintains type safety. + +These types use NRBF, the same efficient binary format used by the legacy `BinaryFormatter`. NRBF serialization provides these key benefits: + +- **Compact binary representation**: Enables efficient storage and transfer. +- **Built-in type information**: Preserves exact .NET types during round-trip operations. +- **Cross-process compatibility**: Works between different .NET applications. +- **Automatic serialization**: Types serialize without custom code. + +For technical details, see the [.NET Remoting Binary Format specification](/openspecs/windows_protocols/ms-nrbf/75b9fe09-be15-475f-85b8-ae7b7558cfe5). + +Classes that support NRBF-encoded data are implemented in the <xref:System.Formats.Nrbf?displayProperty=fullName> namespace. + +### Type safety guarantees + +Windows Forms provides several safety mechanisms for these built-in types: + +- **Exact type matching**. <xref:System.Windows.Forms.Clipboard.TryGetData*> returns only the requested type. +- **Automatic validation**. Windows Forms validates type compatibility during deserialization. +- **No arbitrary code execution**. Unlike custom types with BinaryFormatter, these types can't execute malicious code. +- **Content validation required**. You must still validate data content and ranges for your application logic. +- **No size constraints**. Large arrays or bitmaps aren't automatically limited. Monitor memory usage. + +### Supported primitive types + +The following primitive types work seamlessly with clipboard and `DataObject` operations. You don't need custom serialization or configuration. The clipboard system automatically handles these built-in .NET types: + +- `bool`, `byte`, `char`, `decimal`, `double`, `short`, `int`, and `long`. +- `sbyte`, `ushort`, `uint`, `ulong`, `float`, and `string`. +- `TimeSpan` and `DateTime`. + +The following examples show how these primitive types work directly with `SetData()` and `TryGetData<T>()` methods: + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/PrimitiveTypesExamples.cs" id="PrimitiveTypesExample"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/PrimitiveTypesExamples.vb" id="PrimitiveTypesExample"::: + +### Collections of primitive types + +Arrays and generic lists of supported primitive types work without extra configuration. However, keep these limitations in mind: + +- All array and list elements must be supported primitive types. +- Avoid `string[]` and `List<string>` because NRBF format has complexity handling null values in string collections. +- Store strings individually or use JSON serialization for string collections. + +The following examples show how arrays and lists can be set on the clipboard: + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/PrimitiveTypesExamples.cs" id="CollectionsExample"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/PrimitiveTypesExamples.vb" id="CollectionsExample"::: + +### System.Drawing types + +Common graphics types from the `System.Drawing` namespace work seamlessly with clipboard and `DataObject` operations. These types are useful for applications that work with visual elements and need to transfer drawing-related data between components or applications. Be aware that serializing a `Bitmap` can consume a large amount of memory, especially for large images. The following types are supported: + +- `Point`, `PointF`, `Rectangle`, `RectangleF`. +- `Size`, `SizeF`, `Color`. +- `Bitmap` (can consume significant memory when serialized). + +The following examples show how these graphics types can be used with clipboard operations: + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/SystemDrawingTypesExamples.cs" id="SystemDrawingTypes"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/SystemDrawingTypesExamples.vb" id="SystemDrawingTypes"::: + +## Work with custom types + +When you use <xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)> and <xref:System.Windows.Forms.Clipboard.TryGetData*> with custom types, `System.Text.Json` handles serialization automatically. Many types work without any special configuration—records, simple classes, and structs with public properties serialize seamlessly. + +### Simple types that work without attributes + +Most straightforward custom types don't require special configuration: + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/CustomTypesExamples.cs" id="SimpleCustomTypes"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/CustomTypesExamples.vb" id="SimpleCustomTypes"::: + +### Use JSON attributes for advanced control + +Use `System.Text.Json` attributes only when you need to customize serialization behavior. For comprehensive guidance on `System.Text.Json` serialization, attributes, and advanced configuration options, see [JSON serialization and deserialization in .NET](/dotnet/standard/serialization/system-text-json/overview). + +The following example shows how you can use JSON attributes to control serialization: + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/CustomTypesExamples.cs" id="JsonAttributesExample"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/CustomTypesExamples.vb" id="JsonAttributesExample"::: + +### Example: Clipboard operations with custom types + +:::code language="csharp" source="./snippets/clipboard-dataobject-net10/net/csharp/CustomTypesExamples.cs" id="CustomTypesClipboardOperations"::: +:::code language="vb" source="./snippets/clipboard-dataobject-net10/net/vb/CustomTypesExamples.vb" id="CustomTypesClipboardOperations"::: + +## Enable BinaryFormatter support (not recommended) + +> [!CAUTION] +> `BinaryFormatter` support is **not recommended**. Use it only as a temporary migration bridge for legacy applications that can't immediately migrate to the new type-safe APIs. + +If you must continue using `BinaryFormatter` for clipboard operations in .NET 10, enable limited support through explicit configuration. This approach carries significant security risks and requires several steps. + +For complete step-by-step instructions, see [Enable BinaryFormatter clipboard support (not recommended)](../advanced/how-to-enable-binaryformatter-clipboard-support.md). For general migration guidance, see the [BinaryFormatter migration guide](/dotnet/standard/serialization/binaryformatter-migration-guide/). + +### Security warnings and risks + +`BinaryFormatter` is inherently insecure and deprecated for these reasons: + +- **Arbitrary code execution vulnerabilities**: Attackers can execute malicious code during deserialization, exposing your application to remote attacks. +- **Denial of service attacks**: Malicious clipboard data can consume excessive memory or CPU resources, causing crashes or instability. +- **Information disclosure risks**: Attackers might extract sensitive data from memory. +- **No security boundaries**: The format is fundamentally unsafe, and configuration settings can't secure it. + +Only enable this support as a temporary bridge while you update your application to use the new type-safe APIs. + +For detailed security guidelines and configuration steps, see [Security warnings and risks](../advanced/how-to-enable-binaryformatter-clipboard-support.md#security-warnings-and-risks) in the how-to guide. + +### Implement security-focused type resolvers + +Even with `BinaryFormatter` enabled, you must implement type resolvers to restrict deserialization to explicitly approved types. Follow these guidelines: + +- **Use explicit allow-lists.** Reject any type not explicitly approved. +- **Validate type names.** Ensure type names exactly match expected values. +- **Limit to essential types.** Include only types required for your clipboard functionality. +- **Throw exceptions for unknown types.** Clearly reject unauthorized types. +- **Review regularly.** Audit and update the allowed list as needed. + +For complete implementation examples and code samples, see [Implement security-focused type resolvers](../advanced/how-to-enable-binaryformatter-clipboard-support.md#implement-security-focused-type-resolvers) in the how-to guide. + +## Use AI to migrate clipboard code + +Migrating clipboard operations from .NET 8 to .NET 10 involves systematic code changes across multiple files and classes. AI tools like GitHub Copilot can help accelerate your migration by identifying legacy patterns, suggesting modern replacements, and creating test scenarios. Instead of manually searching through your codebase and converting each clipboard operation individually, you can use AI to handle repetitive tasks while you focus on validating results and handling edge cases. + +The following sections show specific prompt strategies for different aspects of clipboard migration, from finding problematic code patterns to creating robust JSON-serializable types and comprehensive test suites. + +### Use AI to identify legacy clipboard patterns + +Use Copilot to scan your codebase and locate clipboard operations that need migration. This helps you understand the scope of changes required before starting the actual migration work. + +```copilot-prompt +Find all clipboard operations in my codebase that use GetData(), SetData() with custom objects, DataObject.GetData(), or IDataObject.GetData(). Show me the file paths and line numbers where these patterns occur. +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +### Use AI to convert GetData() to TryGetData\<T>() + +Use Copilot to convert obsolete `GetData()` calls to the new type-safe `TryGetData<T>()` pattern. This conversion includes proper error handling and eliminates unsafe casting. + +```copilot-prompt +Convert this GetData() clipboard code to use the new TryGetData<T>() method with proper error handling: + +[paste your existing GetData() code here] + +Make sure to eliminate casting and add appropriate error handling for when the data isn't available. +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +### Use AI to migrate SetData() to SetDataAsJson\<T>() + +Use Copilot to convert custom object storage from the obsolete `SetData()` method to the new `SetDataAsJson<T>()` approach. This ensures your custom objects are properly serialized to the clipboard. + +```copilot-prompt +Take this SetData() clipboard code that stores custom objects: + +[paste your existing SetData() code here] + +Convert it to use SetDataAsJson<T>() and make the custom types JSON-serializable. Add any necessary System.Text.Json attributes if the types have complex properties. +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +### Use AI to create JSON-serializable data models + +Use Copilot to design custom types that work seamlessly with `SetDataAsJson<T>()` and `TryGetData<T>()`. This includes adding appropriate attributes for properties that need special handling. + +```copilot-prompt +Create a JSON-serializable version of this class for clipboard operations: + +[paste your existing class definition here] + +Make it work with System.Text.Json, add JsonIgnore for sensitive properties, JsonInclude for private fields that should serialize, and JsonPropertyName for any properties that need different names in JSON. +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +### Use AI to generate type-safe wrapper methods + +Use Copilot to create wrapper methods that encapsulate the new clipboard APIs and provide clean interfaces for your application's specific data types. + +```copilot-prompt +Create a type-safe clipboard wrapper class that provides methods for storing and retrieving these custom types: + +[list your custom types here] + +Use SetDataAsJson<T>() and TryGetData<T>() internally, include proper error handling, and add methods like SavePersonToClipboard() and TryGetPersonFromClipboard(). +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +### Use AI to create comprehensive tests + +Use Copilot to generate test suites that verify your clipboard migration works correctly, including round-trip serialization tests and error handling scenarios. + +```copilot-prompt +Generate comprehensive unit tests for this clipboard code: + +[paste your migrated clipboard code here] + +Include tests for successful round-trip serialization, handling of null values, error cases when data isn't available, and verification that the migrated code produces the same results as the original for valid scenarios. +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +### Use AI to validate migration results + +Use Copilot to review your migrated code and identify potential issues or areas where the migration might not be complete. + +```copilot-prompt +Review this migrated clipboard code for potential issues: + +[paste your migrated code here] + +Check for: missing error handling, types that might not serialize properly to JSON, performance concerns with large objects, security issues, and any remaining uses of obsolete methods. +``` + +[!INCLUDE [copilot-disclaimer](../../includes/copilot-disclaimer.md)] + +## Related content + +- [How to add data to the Clipboard](../advanced/how-to-add-data-to-the-clipboard.md) +- [How to retrieve data from the Clipboard](../advanced/how-to-retrieve-data-from-the-clipboard.md) +- [Drag-and-Drop Operations and Clipboard Support](../advanced/drag-and-drop-operations-and-clipboard-support.md) +- <xref:System.Windows.Forms.Clipboard.SetDataAsJson``1(System.String,``0)?displayProperty=fullName> +- <xref:System.Windows.Forms.Clipboard.TryGetData*?displayProperty=fullName> diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/BinaryFormatterSupport.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/BinaryFormatterSupport.cs new file mode 100644 index 0000000000..6d20b9e83e --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/BinaryFormatterSupport.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Reflection.Metadata; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class BinaryFormatterSupport + { + [Serializable] + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + [Serializable] + public class AppSettings + { + public string Theme { get; set; } + public bool AutoSave { get; set; } + } + + [Serializable] + public class MyCustomType + { + public string Data { get; set; } + } + + // <SecureTypeResolver> + // Create a security-focused type resolver + private static Type SecureTypeResolver(TypeName typeName) + { + // Explicit allow-list of permitted types—add only what you need + var allowedTypes = new Dictionary<string, Type> + { + [typeof(Person).FullName!] = typeof(Person), + [typeof(AppSettings).FullName!] = typeof(AppSettings), + [typeof(string).FullName!] = typeof(string), + [typeof(int).FullName!] = typeof(int), + // Add only the specific types your application requires + }; + + // Only allow explicitly listed types - exact string match required + if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType)) + { + return allowedType; + } + + // Reject any type not in the allow-list with clear error message + throw new InvalidOperationException( + $"Type '{typeName.FullName}' is not permitted for clipboard deserialization"); + } + + // Use the resolver with clipboard operations + public static void UseSecureTypeResolver() + { + if (Clipboard.TryGetData("LegacyData", SecureTypeResolver, out MyCustomType data)) + { + Console.WriteLine($"Processing legacy data: {data.Data}"); + } + } + // </SecureTypeResolver> + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/CustomTypesExamples.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/CustomTypesExamples.cs new file mode 100644 index 0000000000..91921aac8c --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/CustomTypesExamples.cs @@ -0,0 +1,74 @@ +using System; +using System.Text.Json.Serialization; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class CustomTypesExamples + { + // <SimpleCustomTypes> + // Records work without any attributes. + public record PersonInfo(string Name, int Age, string Email); + + // Simple classes serialize all public properties automatically. + public class DocumentMetadata + { + public string Title { get; set; } + public DateTime Created { get; set; } + public string Author { get; set; } + } + + // Structs with public properties work seamlessly. + public struct Point3D + { + public double X { get; set; } + public double Y { get; set; } + public double Z { get; set; } + } + // </SimpleCustomTypes> + + // <JsonAttributesExample> + public class ClipboardFriendlyType + { + // Include a field that normally isn't serialized + [JsonInclude] + private int _privateData; + + // Public properties are always serialized + public string Name { get; set; } + + // Exclude sensitive or non-essential data + [JsonIgnore] + public string InternalId { get; set; } + + // Handle property name differences for compatibility + [JsonPropertyName("display_text")] + public string DisplayText { get; set; } + + // Control null value handling + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string OptionalField { get; set; } + } + // </JsonAttributesExample> + + // <CustomTypesClipboardOperations> + public static void CustomTypesClipboardOperationsExample() + { + var data = new ClipboardFriendlyType + { + Name = "Sample", + DisplayText = "Sample Display Text", + InternalId = "internal-123" // This property isn't serialized due to [JsonIgnore] + }; + + Clipboard.SetDataAsJson("MyAppData", data); + + if (Clipboard.TryGetData("MyAppData", out ClipboardFriendlyType retrieved)) + { + Console.WriteLine($"Retrieved: {retrieved.Name}"); + // retrieved.InternalId is null because of [JsonIgnore] + } + } + // </CustomTypesClipboardOperations> + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.Designer.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.Designer.cs new file mode 100644 index 0000000000..c23c093ee9 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.Designer.cs @@ -0,0 +1,38 @@ +namespace csharp; + +partial class Form1 +{ + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Text = "Form1"; + } + + #endregion +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.cs new file mode 100644 index 0000000000..a1158949f6 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.cs @@ -0,0 +1,9 @@ +namespace csharp; + +public partial class Form1 : Form +{ + public Form1() + { + InitializeComponent(); + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.resx b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Form1.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ITypedDataObjectExamples.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ITypedDataObjectExamples.cs new file mode 100644 index 0000000000..fbddb56725 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ITypedDataObjectExamples.cs @@ -0,0 +1,35 @@ +using System; +using System.Reflection.Metadata; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class DragDropExamples + { + public class MyItem + { + public string Name { get; set; } + public string Value { get; set; } + } + + // <DragDropUsage> + private void OnDragDrop(object sender, DragEventArgs e) + { + if (e.Data is ITypedDataObject typedData) + { + // Retrieve files from drag data using a standard format + if (typedData.TryGetData(DataFormats.FileDrop, out string[] files)) + Console.WriteLine($"Dropped files: {string.Join(", ", files)}"); + + // Retrieve text using a standard format + if (typedData.TryGetData(DataFormats.Text, out string text)) + Console.WriteLine($"Dropped text: {text}"); + + // Retrieve custom items using an application-specific format + if (typedData.TryGetData("CustomItem", out MyItem item)) + Console.WriteLine($"Dropped custom item: {item.Name} = {item.Value}"); + } + } + // </DragDropUsage> + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ModernApproach.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ModernApproach.cs new file mode 100644 index 0000000000..5a877c6ac2 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ModernApproach.cs @@ -0,0 +1,35 @@ +using System; +using System.Windows.Forms; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace ClipboardExamples +{ + public class ModernApproach + { + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + // <ModernTryGetData> + public static void ModernTryGetDataExample() + { + var data = new Person { Name = "Alice", Age = 28 }; + Clipboard.SetDataAsJson("MyAppData", data); + + // Use this - type-safe approach with TryGetData<T>() + if (Clipboard.TryGetData("MyApp.Person", out Person person)) + { + // person is guaranteed to be the correct type + Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}"); + } + else + { + // Handle the case where data isn't available or is the wrong type + MessageBox.Show("Unable to retrieve person data from clipboard"); + } + } + // </ModernTryGetData> + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ObsoletePatterns.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ObsoletePatterns.cs new file mode 100644 index 0000000000..5fdff02cb6 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/ObsoletePatterns.cs @@ -0,0 +1,43 @@ +using System; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + // Examples of obsolete patterns that no longer work in .NET 10 + public class ObsoletePatterns + { + // <ObsoleteCustomType> + [Serializable] + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + public static void BrokenCustomTypeExample() + { + // This worked in .NET 8 and earlier but silently fails starting with .NET 9 + Person person = new Person { Name = "John", Age = 30 }; + Clipboard.SetData("MyApp.Person", person); // No data is stored + + // Later attempts to retrieve the data return null + object data = Clipboard.GetData("MyApp.Person"); + } + // </ObsoleteCustomType> + + // <ObsoleteGetData> + public static void ObsoleteGetDataExample() + { + // Don't use - GetData() is obsolete in .NET 10 + object data = Clipboard.GetData("MyApp.Person"); // Obsolete method + + // Always returns null on a custom object type + if (data != null) + { + Person person = (Person)data; + Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}"); + } + } + // </ObsoleteGetData> + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/PrimitiveTypesExamples.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/PrimitiveTypesExamples.cs new file mode 100644 index 0000000000..6840b049a5 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/PrimitiveTypesExamples.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class PrimitiveTypesExamples + { + // <PrimitiveTypesExample> + public static void PrimitiveTypesExample() + { + // Numeric types + Clipboard.SetData("MyInt", 42); + Clipboard.SetData("MyDouble", 3.14159); + Clipboard.SetData("MyDecimal", 123.45m); + + // Text and character types + Clipboard.SetData("MyString", "Hello World"); + Clipboard.SetData("MyChar", 'A'); + + // Boolean and date/time types + Clipboard.SetData("MyBool", true); + Clipboard.SetData("MyDateTime", DateTime.Now); + Clipboard.SetData("MyTimeSpan", TimeSpan.FromMinutes(30)); + + // Later retrieval with type safety + if (Clipboard.TryGetData("MyTimeSpan", out TimeSpan value)) + { + Console.WriteLine($"Clipboard value is: {value}"); + } + } + // </PrimitiveTypesExample> + + // <CollectionsExample> + public static void CollectionsExample() + { + // Arrays of primitive types + int[] numbers = { 1, 2, 3, 4, 5 }; + Clipboard.SetData("NumberArray", numbers); + + double[] coordinates = { 1.0, 2.5, 3.7 }; + Clipboard.SetData("Coordinates", coordinates); + + // Generic lists + List<int> intList = new List<int> { 10, 20, 30 }; + Clipboard.SetData("IntList", intList); + + // Retrieval maintains type safety + if (Clipboard.TryGetData("NumberArray", out int[] retrievedNumbers)) + { + Console.WriteLine($"Numbers: {string.Join(", ", retrievedNumbers)}"); + } + } + // </CollectionsExample> + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Program.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Program.cs new file mode 100644 index 0000000000..4db135f19c --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/Program.cs @@ -0,0 +1,16 @@ +namespace csharp; + +static class Program +{ + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new Form1()); + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/SetDataAsJsonExamples.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/SetDataAsJsonExamples.cs new file mode 100644 index 0000000000..fcceb52777 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/SetDataAsJsonExamples.cs @@ -0,0 +1,50 @@ +using System; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class SetDataAsJsonExamples + { + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + public class AppSettings + { + public string Theme { get; set; } + public bool AutoSave { get; set; } + } + + // <AutomaticFormatInference> + public static void AutomaticFormatInferenceExample() + { + var person = new Person { Name = "Alice", Age = 25 }; + + // The format is automatically inferred from the type name + Clipboard.SetDataAsJson("Person", person); // Uses "Person" as the format + + // Retrieve the data later + if (Clipboard.TryGetData("Person", out Person retrievedPerson)) + { + Console.WriteLine($"Retrieved: {retrievedPerson.Name}"); + } + } + // </AutomaticFormatInference> + + // <CustomFormat> + public static void CustomFormatExample() + { + var settings = new AppSettings { Theme = "Dark", AutoSave = true }; + + // Use a custom format for better organization + Clipboard.SetDataAsJson("MyApp.Settings", settings); + + // Store the same data in multiple formats + Clipboard.SetDataAsJson("Config.V1", settings); + Clipboard.SetDataAsJson("AppConfig", settings); + } + // </CustomFormat> + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/SystemDrawingTypesExamples.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/SystemDrawingTypesExamples.cs new file mode 100644 index 0000000000..7a04bf0cb5 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/SystemDrawingTypesExamples.cs @@ -0,0 +1,31 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class SystemDrawingTypesExamples + { + // <SystemDrawingTypes> + public static void SystemDrawingTypesExample() + { + // Geometric types + Point location = new Point(100, 200); + Rectangle bounds = new Rectangle(0, 0, 500, 300); + Size dimensions = new Size(800, 600); + + Clipboard.SetData("Location", location); + Clipboard.SetData("Bounds", bounds); + Clipboard.SetData("Size", dimensions); + + // Color information + Color backgroundColor = Color.FromArgb(255, 128, 64, 192); + Clipboard.SetData("BackColor", backgroundColor); + + // Bitmap data (use with caution for large images) + Bitmap smallIcon = new Bitmap(16, 16); + Clipboard.SetData("Icon", smallIcon); + } + // </SystemDrawingTypes> + } +} \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/TypeResolver.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/TypeResolver.cs new file mode 100644 index 0000000000..c935be4aa4 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/TypeResolver.cs @@ -0,0 +1,57 @@ +using System; +using System.Reflection.Metadata; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class TypeResolver + { + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + public class AppSettings + { + public string Theme { get; set; } + public bool AutoSave { get; set; } + } + + public class MyType + { + public string Data { get; set; } + } + + // <TypeResolverExample> + public static void TypeResolverExample() + { + // Create a type resolver that maps old type names to current types + Func<TypeName, Type> resolver = typeName => + { + // Only allow specific, known, safe types + return typeName.FullName switch + { + "MyApp.Person" => typeof(Person), + "MyApp.Settings" => typeof(AppSettings), + "System.String" => typeof(string), + "System.Int32" => typeof(int), + _ => throw new InvalidOperationException($"Type not allowed: {typeName.FullName}") + }; + }; + + // Use the resolver with legacy binary data + if (Clipboard.TryGetData("LegacyFormat", resolver, out Person person)) + { + Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}"); + } + + // Use a resolver with conversion control + if (Clipboard.TryGetData("OldCustomData", resolver, out MyType data)) + { + Console.WriteLine($"Processing custom data: {data.Data}"); + } + } + // </TypeResolverExample> + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/TypeSafeRetrieval.cs b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/TypeSafeRetrieval.cs new file mode 100644 index 0000000000..105429cad6 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/TypeSafeRetrieval.cs @@ -0,0 +1,69 @@ +using System; +using System.Windows.Forms; + +namespace ClipboardExamples +{ + public class TypeSafeRetrieval + { + // <BasicTypeSafeRetrieval> + public static void BasicTypeSafeRetrievalExamples() + { + // Retrieve text data using a standard format + if (Clipboard.TryGetData(DataFormats.Text, out string textData)) + Console.WriteLine($"Text: {textData}"); + + // Retrieve an integer using a custom format + if (Clipboard.TryGetData("NumberData", out int numberData)) + Console.WriteLine($"Number: {numberData}"); + + // Retrieve Unicode text using a standard format + if (Clipboard.TryGetData(DataFormats.UnicodeText, out string unicodeText)) + Console.WriteLine($"Unicode: {unicodeText}"); + + // Retrieve raw text data with OLE conversion control + if (Clipboard.TryGetData(DataFormats.Text, out string rawText)) + Console.WriteLine($"Raw: {rawText}"); + + // Retrieve file drops using a standard format + if (Clipboard.TryGetData(DataFormats.FileDrop, out string[] files)) + Console.WriteLine($"Files: {string.Join(", ", files)}"); + } + // </BasicTypeSafeRetrieval> + + // <CustomJsonTypes> + public static void CustomJsonTypesExamples() + { + // Retrieve a custom type stored with SetDataAsJson<T>() + if (Clipboard.TryGetData("Person", out Person person)) + Console.WriteLine($"Person: {person.Name}"); + + // Retrieve application-specific data formats + if (Clipboard.TryGetData("MyApp.Settings", out AppSettings settings)) + Console.WriteLine($"Settings: {settings.Theme}"); + + // Retrieve complex custom objects + if (Clipboard.TryGetData("DocumentData", out DocumentInfo doc)) + Console.WriteLine($"Document: {doc.Title}"); + } + // </CustomJsonTypes> + + public class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + public class AppSettings + { + public string Theme { get; set; } + public bool AutoSave { get; set; } + } + + public class DocumentInfo + { + public string Title { get; set; } + public string Author { get; set; } + public DateTime Created { get; set; } + } + } +} diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/csharp.csproj b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/csharp.csproj new file mode 100644 index 0000000000..5151c0a87e --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/csharp/csharp.csproj @@ -0,0 +1,11 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <Nullable>enable</Nullable> + <UseWindowsForms>true</UseWindowsForms> + <ImplicitUsings>enable</ImplicitUsings> + </PropertyGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/BinaryFormatterSupport.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/BinaryFormatterSupport.vb new file mode 100644 index 0000000000..f0b59c1b65 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/BinaryFormatterSupport.vb @@ -0,0 +1,57 @@ +Imports System +Imports System.Collections.Generic +Imports System.Reflection.Metadata +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class BinaryFormatterSupport + <Serializable> + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + <Serializable> + Public Class AppSettings + Public Property Theme As String + Public Property AutoSave As Boolean + End Class + + <Serializable> + Public Class MyCustomType + Public Property Data As String + End Class + + ' <SecureTypeResolver> + ' Create a security-focused type resolver + Private Shared Function SecureTypeResolver(typeName As TypeName) As Type + ' Explicit allow-list of permitted types—add only what you need + Dim allowedTypes As New Dictionary(Of String, Type) From { + {GetType(Person).FullName, GetType(Person)}, + {GetType(AppSettings).FullName, GetType(AppSettings)}, + {GetType(String).FullName, GetType(String)}, + {GetType(Integer).FullName, GetType(Integer)} + } + ' Add only the specific types your application requires + + ' Only allow explicitly listed types - exact string match required + Dim allowedType As Type = Nothing + If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then + Return allowedType + End If + + ' Reject any type not in the allow-list with clear error message + Throw New InvalidOperationException( + $"Type '{typeName.FullName}' is not permitted for clipboard deserialization") + End Function + + ' Use the resolver with clipboard operations + Public Shared Sub UseSecureTypeResolver() + Dim data As MyCustomType = Nothing + If Clipboard.TryGetData("LegacyData", AddressOf SecureTypeResolver, data) Then + Console.WriteLine($"Processing legacy data: {data.Data}") + End If + End Sub + ' </SecureTypeResolver> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/CustomTypesExamples.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/CustomTypesExamples.vb new file mode 100644 index 0000000000..e5a1270eb0 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/CustomTypesExamples.vb @@ -0,0 +1,64 @@ +Imports System +Imports System.Text.Json.Serialization +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class CustomTypesExamples + ' <SimpleCustomTypes> + ' Simple classes serialize all public properties automatically. + Public Class DocumentMetadata + Public Property Title As String + Public Property Created As DateTime + Public Property Author As String + End Class + + ' Structs with public properties work seamlessly. + Public Structure Point3D + Public Property X As Double + Public Property Y As Double + Public Property Z As Double + End Structure + ' </SimpleCustomTypes> + + ' <JsonAttributesExample> + Public Class ClipboardFriendlyType + ' Include a field that normally isn't serialized + <JsonInclude> + Private _privateData As Integer + + ' Public properties are always serialized + Public Property Name As String + + ' Exclude sensitive or non-essential data + <JsonIgnore> + Public Property InternalId As String + + ' Handle property name differences for compatibility + <JsonPropertyName("display_text")> + Public Property DisplayText As String + + ' Control null value handling + <JsonIgnore(Condition:=JsonIgnoreCondition.WhenWritingNull)> + Public Property OptionalField As String + End Class + ' </JsonAttributesExample> + + ' <CustomTypesClipboardOperations> + Public Shared Sub CustomTypesClipboardOperationsExample() + Dim data As New ClipboardFriendlyType With { + .Name = "Sample", + .DisplayText = "Sample Display Text", + .InternalId = "internal-123" ' This property isn't serialized due to <JsonIgnore> + } + + Clipboard.SetDataAsJson("MyAppData", data) + + Dim retrieved As ClipboardFriendlyType = Nothing + If Clipboard.TryGetData("MyAppData", retrieved) Then + Console.WriteLine($"Retrieved: {retrieved.Name}") + ' retrieved.InternalId is null because of <JsonIgnore> + End If + End Sub + ' </CustomTypesClipboardOperations> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Form1.Designer.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Form1.Designer.vb new file mode 100644 index 0000000000..0a21f031de --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Form1.Designer.vb @@ -0,0 +1,31 @@ +<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> +Partial Class Form1 + Inherits System.Windows.Forms.Form + + 'Form overrides dispose to clean up the component list. + <System.Diagnostics.DebuggerNonUserCode()> + Protected Overrides Sub Dispose(disposing As Boolean) + Try + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + Finally + MyBase.Dispose(disposing) + End Try + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + <System.Diagnostics.DebuggerStepThrough()> + Private Sub InitializeComponent() + components = New System.ComponentModel.Container() + AutoScaleMode = AutoScaleMode.Font + ClientSize = New Size(800, 450) + Text = "Form1" + End Sub + +End Class diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Form1.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Form1.vb new file mode 100644 index 0000000000..17d659563f --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Form1.vb @@ -0,0 +1,3 @@ +Public Class Form1 + +End Class diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ITypedDataObjectExamples.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ITypedDataObjectExamples.vb new file mode 100644 index 0000000000..9bc12a8e42 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ITypedDataObjectExamples.vb @@ -0,0 +1,39 @@ +Imports System +Imports System.Reflection.Metadata +Imports System.Windows.Forms + +Namespace ClipboardExamples + + Public Class DragDropExamples + Public Class MyItem + Public Property Name As String + Public Property Value As String + End Class + + ' <DragDropUsage> + Private Sub OnDragDrop(sender As Object, e As DragEventArgs) + If TypeOf e.Data Is ITypedDataObject Then + Dim typedData As ITypedDataObject = CType(e.Data, ITypedDataObject) + + ' Retrieve files from drag data using a standard format + Dim files As String() = Nothing + If typedData.TryGetData(DataFormats.FileDrop, files) Then + Console.WriteLine($"Dropped files: {String.Join(", ", files)}") + End If + + ' Retrieve text using a standard format + Dim text As String = Nothing + If typedData.TryGetData(DataFormats.Text, text) Then + Console.WriteLine($"Dropped text: {text}") + End If + + ' Retrieve custom items using an application-specific format + Dim item As MyItem = Nothing + If typedData.TryGetData("CustomItem", item) Then + Console.WriteLine($"Dropped custom item: {item.Name} = {item.Value}") + End If + End If + End Sub + ' </DragDropUsage> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ModernApproach.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ModernApproach.vb new file mode 100644 index 0000000000..2fed477671 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ModernApproach.vb @@ -0,0 +1,28 @@ +Imports System +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class ModernApproach + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + ' <ModernTryGetData> + Public Shared Sub ModernTryGetDataExample() + Dim data As New Person With {.Name = "Alice", .Age = 30} + Clipboard.SetDataAsJson("MyAppData", data) + + ' Use this - type-safe approach with TryGetData(Of T)() + Dim person As Person = Nothing + If Clipboard.TryGetData("MyApp.Person", person) Then + ' person is guaranteed to be the correct type + Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}") + Else + ' Handle the case where data isn't available or is the wrong type + MessageBox.Show("Unable to retrieve person data from clipboard") + End If + End Sub + ' </ModernTryGetData> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ObsoletePatterns.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ObsoletePatterns.vb new file mode 100644 index 0000000000..f4a8a1e9ac --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/ObsoletePatterns.vb @@ -0,0 +1,37 @@ +Imports System +Imports System.Windows.Forms + +Namespace ClipboardExamples + ' Examples of obsolete patterns that no longer work in .NET 10 + Public Class ObsoletePatterns + ' <ObsoleteCustomType> + <Serializable> + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + Public Shared Sub BrokenCustomTypeExample() + ' This worked in .NET 8 and earlier but silently fails starting with .NET 9 + Dim person As New Person With {.Name = "John", .Age = 30} + Clipboard.SetData("MyApp.Person", person) ' No data is stored + + ' Later attempts to retrieve the data return null + Dim data As Object = Clipboard.GetData("MyApp.Person") + End Sub + ' </ObsoleteCustomType> + + ' <ObsoleteGetData> + Public Shared Sub ObsoleteGetDataExample() + ' Don't use - GetData() is obsolete in .NET 10 + Dim data As Object = Clipboard.GetData("MyApp.Person") ' Obsolete method + + ' Always returns null on a custom object type + If data IsNot Nothing Then + Dim person As Person = CType(data, Person) + Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}") + End If + End Sub + ' </ObsoleteGetData> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/PrimitiveTypesExamples.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/PrimitiveTypesExamples.vb new file mode 100644 index 0000000000..b135ca6f47 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/PrimitiveTypesExamples.vb @@ -0,0 +1,54 @@ +Imports System +Imports System.Collections.Generic +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class PrimitiveTypesExamples + ' <PrimitiveTypesExample> + Public Shared Sub PrimitiveTypesExample() + ' Numeric types + Clipboard.SetData("MyInt", 42) + Clipboard.SetData("MyDouble", 3.14159) + Clipboard.SetData("MyDecimal", 123.45D) + + ' Text and character types + Clipboard.SetData("MyString", "Hello World") + Clipboard.SetData("MyChar", "A"c) + + ' Boolean and date/time types + Clipboard.SetData("MyBool", True) + Clipboard.SetData("MyDateTime", DateTime.Now) + Clipboard.SetData("MyTimeSpan", TimeSpan.FromMinutes(30)) + + ' Later retrieval with type safety + Dim value As TimeSpan + + If Clipboard.TryGetData("MyTimeSpan", value) Then + Console.WriteLine($"Clipboard value is: {value}") + End If + End Sub + ' </PrimitiveTypesExample> + + ' <CollectionsExample> + Public Shared Sub CollectionsExample() + ' Arrays of primitive types + Dim numbers As Integer() = {1, 2, 3, 4, 5} + Clipboard.SetData("NumberArray", numbers) + + Dim coordinates As Double() = {1.0, 2.5, 3.7} + Clipboard.SetData("Coordinates", coordinates) + + ' Generic lists + Dim intList As New List(Of Integer) From {10, 20, 30} + Clipboard.SetData("IntList", intList) + + ' Retrieval maintains type safety + Dim retrievedNumbers As Integer() = Nothing + + If Clipboard.TryGetData("NumberArray", retrievedNumbers) Then + Console.WriteLine($"Numbers: {String.Join(", ", retrievedNumbers)}") + End If + End Sub + ' </CollectionsExample> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Program.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Program.vb new file mode 100644 index 0000000000..236767207e --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/Program.vb @@ -0,0 +1,11 @@ +Friend Module Program + + <STAThread()> + Friend Sub Main(args As String()) + Application.SetHighDpiMode(HighDpiMode.SystemAware) + Application.EnableVisualStyles() + Application.SetCompatibleTextRenderingDefault(False) + Application.Run(New Form1) + End Sub + +End Module diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/SetDataAsJsonExamples.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/SetDataAsJsonExamples.vb new file mode 100644 index 0000000000..1f9f076d62 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/SetDataAsJsonExamples.vb @@ -0,0 +1,44 @@ +Imports System +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class SetDataAsJsonExamples + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + Public Class AppSettings + Public Property Theme As String + Public Property AutoSave As Boolean + End Class + + ' <AutomaticFormatInference> + Public Shared Sub AutomaticFormatInferenceExample() + Dim person As New Person With {.Name = "Alice", .Age = 25} + + ' The format is automatically inferred from the type name + Clipboard.SetDataAsJson("Person", person) ' Uses "Person" as the format + + ' Retrieve the data later + Dim retrievedPerson As Person = Nothing + If Clipboard.TryGetData("Person", retrievedPerson) Then + Console.WriteLine($"Retrieved: {retrievedPerson.Name}") + End If + End Sub + ' </AutomaticFormatInference> + + ' <CustomFormat> + Public Shared Sub CustomFormatExample() + Dim settings As New AppSettings With {.Theme = "Dark", .AutoSave = True} + + ' Use a custom format for better organization + Clipboard.SetDataAsJson("MyApp.Settings", settings) + + ' Store the same data in multiple formats + Clipboard.SetDataAsJson("Config.V1", settings) + Clipboard.SetDataAsJson("AppConfig", settings) + End Sub + ' </CustomFormat> + End Class +End Namespace \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/SystemDrawingTypesExamples.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/SystemDrawingTypesExamples.vb new file mode 100644 index 0000000000..8d1c3c1de2 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/SystemDrawingTypesExamples.vb @@ -0,0 +1,28 @@ +Imports System +Imports System.Drawing +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class SystemDrawingTypesExamples + ' <SystemDrawingTypes> + Public Shared Sub SystemDrawingTypesExample() + ' Geometric types + Dim location As New Point(100, 200) + Dim bounds As New Rectangle(0, 0, 500, 300) + Dim dimensions As New Size(800, 600) + + Clipboard.SetData("Location", location) + Clipboard.SetData("Bounds", bounds) + Clipboard.SetData("Size", dimensions) + + ' Color information + Dim backgroundColor As Color = Color.FromArgb(255, 128, 64, 192) + Clipboard.SetData("BackColor", backgroundColor) + + ' Bitmap data (use with caution for large images) + Dim smallIcon As New Bitmap(16, 16) + Clipboard.SetData("Icon", smallIcon) + End Sub + ' </SystemDrawingTypes> + End Class +End Namespace \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/TypeResolver.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/TypeResolver.vb new file mode 100644 index 0000000000..d5797fefc6 --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/TypeResolver.vb @@ -0,0 +1,55 @@ +Imports System +Imports System.Reflection.Metadata +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class TypeResolver + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + Public Class AppSettings + Public Property Theme As String + Public Property AutoSave As Boolean + End Class + + Public Class MyType + Public Property Data As String + End Class + + ' <TypeResolverExample> + Public Shared Sub TypeResolverExample() + ' Create a type resolver that maps old type names to current types + Dim resolver As Func(Of TypeName, Type) = + Function(typeName) + ' Only allow specific, known, safe types + Select Case typeName.FullName + Case "MyApp.Person" + Return GetType(Person) + Case "MyApp.Settings" + Return GetType(AppSettings) + Case "System.String" + Return GetType(String) + Case "System.Int32" + Return GetType(Integer) + Case Else + Throw New InvalidOperationException($"Type not allowed: {typeName.FullName}") + End Select + End Function + + ' Use the resolver with legacy binary data + Dim person As Person = Nothing + If Clipboard.TryGetData("LegacyFormat", resolver, person) Then + Console.WriteLine($"Processing person: {person.Name}, Age: {person.Age}") + End If + + ' Use a resolver with conversion control + Dim data As MyType = Nothing + If Clipboard.TryGetData("OldCustomData", resolver, data) Then + Console.WriteLine($"Processing custom data: {data.Data}") + End If + End Sub + ' </TypeResolverExample> + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/TypeSafeRetrieval.vb b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/TypeSafeRetrieval.vb new file mode 100644 index 0000000000..e52e2a6adf --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/TypeSafeRetrieval.vb @@ -0,0 +1,78 @@ +Imports System +Imports System.Windows.Forms + +Namespace ClipboardExamples + Public Class TypeSafeRetrieval + ' <BasicTypeSafeRetrieval> + Public Shared Sub BasicTypeSafeRetrievalExamples() + ' Retrieve text data using a standard format + Dim textData As String = Nothing + If Clipboard.TryGetData(DataFormats.Text, textData) Then + Console.WriteLine($"Text: {textData}") + End If + + ' Retrieve an integer using a custom format + Dim numberData As Integer + If Clipboard.TryGetData("NumberData", numberData) Then + Console.WriteLine($"Number: {numberData}") + End If + + ' Retrieve Unicode text using a standard format + Dim unicodeText As String = Nothing + If Clipboard.TryGetData(DataFormats.UnicodeText, unicodeText) Then + Console.WriteLine($"Unicode: {unicodeText}") + End If + + ' Retrieve raw text data with OLE conversion control + Dim rawText As String = Nothing + If Clipboard.TryGetData(DataFormats.Text, rawText) Then + Console.WriteLine($"Raw: {rawText}") + End If + + ' Retrieve file drops using a standard format + Dim files As String() = Nothing + If Clipboard.TryGetData(DataFormats.FileDrop, files) Then + Console.WriteLine($"Files: {String.Join(", ", files)}") + End If + End Sub + ' </BasicTypeSafeRetrieval> + + ' <CustomJsonTypes> + Public Shared Sub CustomJsonTypesExamples() + ' Retrieve a custom type stored with SetDataAsJson(Of T)() + Dim person As Person = Nothing + If Clipboard.TryGetData("Person", person) Then + Console.WriteLine($"Person: {person.Name}") + End If + + ' Retrieve application-specific data formats + Dim settings As AppSettings = Nothing + If Clipboard.TryGetData("MyApp.Settings", settings) Then + Console.WriteLine($"Settings: {settings.Theme}") + End If + + ' Retrieve complex custom objects + Dim doc As DocumentInfo = Nothing + If Clipboard.TryGetData("DocumentData", doc) Then + Console.WriteLine($"Document: {doc.Title}") + End If + End Sub + ' </CustomJsonTypes> + + Public Class Person + Public Property Name As String + Public Property Age As Integer + End Class + + Public Class AppSettings + Public Property Theme As String + Public Property AutoSave As Boolean + End Class + + Public Class DocumentInfo + Public Property Title As String + Public Property Author As String + Public Property Created As DateTime + End Class + End Class +End Namespace diff --git a/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/vb.vbproj b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/vb.vbproj new file mode 100644 index 0000000000..9870ed665a --- /dev/null +++ b/dotnet-desktop-guide/winforms/migration/snippets/clipboard-dataobject-net10/net/vb/vb.vbproj @@ -0,0 +1,17 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net10.0-windows</TargetFramework> + <RootNamespace>vb</RootNamespace> + <StartupObject>Sub Main</StartupObject> + <UseWindowsForms>true</UseWindowsForms> + </PropertyGroup> + + <ItemGroup> + <Import Include="System.Data" /> + <Import Include="System.Drawing" /> + <Import Include="System.Windows.Forms" /> + </ItemGroup> + +</Project> \ No newline at end of file diff --git a/dotnet-desktop-guide/winforms/toc.yml b/dotnet-desktop-guide/winforms/toc.yml index cdf4238f58..7925d67f11 100644 --- a/dotnet-desktop-guide/winforms/toc.yml +++ b/dotnet-desktop-guide/winforms/toc.yml @@ -23,10 +23,12 @@ items: items: - name: Create an app href: get-started/create-app-visual-studio.md -- name: Migration +- name: Upgrade items: - name: How to upgrade to the latest .NET href: migration/index.md + - name: Clipboard and DataObject changes in .NET 10 + href: migration/clipboard-dataobject-net10.md - name: Accessibility improvements with .NET href: windows-forms-accessibility-improvements.md - name: Advanced topics diff --git a/dotnet-desktop-guide/zone-pivot-groups.yml b/dotnet-desktop-guide/zone-pivot-groups.yml new file mode 100644 index 0000000000..3678e6d1b6 --- /dev/null +++ b/dotnet-desktop-guide/zone-pivot-groups.yml @@ -0,0 +1,11 @@ +### YamlMime:ZonePivotGroups + +groups: + - id: dotnet-version + title: .NET Version + prompt: Choose your .NET version + pivots: + - id: dotnet + title: .NET + - id: dotnetframework + title: .NET Framework \ No newline at end of file