Skip to content

Commit 0c26c0b

Browse files
committed
using form
1 parent 54b36c3 commit 0c26c0b

File tree

4 files changed

+257
-9
lines changed

4 files changed

+257
-9
lines changed
35.3 KB
Loading
56.2 KB
Loading
80 KB
Loading

hub/powertoys/command-palette/using-form-pages.md

Lines changed: 257 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,268 @@ no-loc: [PowerToys, Windows, Insider]
1111

1212
**Previous**: [Display markdown content](using-markdown-content.md)
1313

14-
Now that we know how to present basic markdown content, let's try displaying something more elaborate by leveraging the power of [Adaptive Cards](https://adaptivecards.io/). This is useful for creating forms, or for displaying more complex content.
14+
Now that we know how to present basic markdown content, let's try displaying something more elaborate by leveraging the power of **[Adaptive Cards](https://adaptivecards.io/)**. This is useful for creating forms, or for displaying more complex content.
1515

1616
## Working with forms
1717

18-
Command Palette supports Adaptive Cards, which are a way to create rich, interactive content. This can be useful for creating forms, or for displaying more complex content.
18+
You can create a card in the Command Palette with the `IFormContent` interface (see [FormContent](./microsoft-commandpalette-extensions-toolkit/formcontent.md) for the toolkit implementation). This allows you to provide the Adaptive Card JSON, and the Command Palette will render it for you. When the user submits the form, Command Palette will call the `SubmitForm` method on your form, with the JSON payload and inputs from the form.
1919

20-
You can create a card in the Command Palette with the **IFormContent** interface (see [FormContent](./microsoft-commandpalette-extensions-toolkit/formcontent.md) for the toolkit implementation). This allows you to provide the Adaptive Card JSON, and the Command Palette will render it for you. When the user submits the form, Command Palette will call the **SubmitForm** method on your form, with the JSON payload and inputs from the form.
20+
> [!TIP]
21+
> Adaptive card payloads can be created using the [Adaptive Card Designer](https://adaptivecards.io/designer/). You can design your card there, and then copy the JSON payload into your extension.
2122
22-
Adaptive card payloads can be created using the [Adaptive Card Designer](https://adaptivecards.io/designer/). You can design your card there, and then copy the JSON payload into your extension.
23+
1. In the `Pages` directory, add a new class
24+
1. Name the class `FormPage`
25+
1. Update the `FormPage` class:
2326

24-
For a full example of using Forms and Content pages, head on over to [`SamplePagesExtension/Pages/SampleContentPage.cs`](https://github.com/microsoft/PowerToys/blob/main/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleContentPage.cs). Some brief things to note:
27+
```csharp
28+
internal sealed partial class FormPage : ContentPage
29+
{
30+
private readonly SampleContentForm sampleForm = new();
31+
32+
public override IContent[] GetContent() => [sampleForm];
33+
34+
public FormPage()
35+
{
36+
Name = "Open";
37+
Title = "Sample Content";
38+
Icon = new IconInfo("\uECA5"); // Tiles
39+
}
40+
}
41+
```
42+
43+
The FormPage is a content page that displays a form (`SampleContentForm`) to the user. It creates an instance of `SampleContentForm`, which is a form (defined later) that describes the UI and logic for a user input form.
44+
45+
1. At the bottom of file (under the `FormPage` class) add:
46+
47+
```csharp
48+
internal sealed partial class SampleContentForm : FormContent
49+
{
50+
public SampleContentForm()
51+
{
52+
TemplateJson = $$"""
53+
{
54+
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
55+
"type": "AdaptiveCard",
56+
"version": "1.6",
57+
"body": [
58+
{
59+
"type": "TextBlock",
60+
"size": "medium",
61+
"weight": "bolder",
62+
"text": " Sample",
63+
"horizontalAlignment": "center",
64+
"wrap": true,
65+
"style": "heading"
66+
},
67+
{
68+
"type": "Input.Text",
69+
"label": "Name",
70+
"style": "text",
71+
"id": "SimpleVal",
72+
"isRequired": true,
73+
"errorMessage": "Name is required",
74+
"placeholder": "Enter your name"
75+
}
76+
],
77+
"actions": [
78+
{
79+
"type": "Action.Submit",
80+
"title": "Submit",
81+
"data": {
82+
"id": "1234567890"
83+
}
84+
}
85+
]
86+
}
87+
""";
88+
89+
}
90+
91+
public override CommandResult SubmitForm(string payload)
92+
{
93+
var formInput = JsonNode.Parse(payload)?.AsObject();
94+
Debug.WriteLine($"Form submitted with formInput: {formInput}");
95+
if (formInput == null)
96+
{
97+
return CommandResult.GoHome();
98+
}
99+
ConfirmationArgs confirmArgs = new()
100+
{
101+
PrimaryCommand = new AnonymousCommand(
102+
() =>
103+
{
104+
string? name = formInput["Name"]?.ToString();
105+
ToastStatusMessage t = new($"Hi {name}" ?? "No name entered");
106+
t.Show();
107+
})
108+
{
109+
Name = "Confirm",
110+
Result = CommandResult.KeepOpen(),
111+
},
112+
return CommandResult.Confirm(confirmArgs);
113+
}
114+
}
115+
```
116+
117+
The `SampleContentForm` contains the form and form submission logic. The `TemplateJson` contains the form structure and actions. This example only contains one text input which has the id of "Name" and has one action of submitting the form. The `SubmitForm` handles parsing the payload; if its invalid will return the command to home and otherwise will display a confirmation dialog and a toast notification.
118+
119+
1. Open <ExtensionName>CommandsProvider.cs
120+
1. Replace the `MarkdownPage` for `FormPage`:
121+
122+
```diff
123+
public <ExtensionName>CommandsProvider()
124+
{
125+
DisplayName = "My sample extension";
126+
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
127+
_commands = [
128+
+ new CommandItem(new FormPage()) { Title = DisplayName },
129+
];
130+
}
131+
```
132+
133+
1. Deploy your extension
134+
1. In command palette, `Reload`
135+
136+
![Screenshot of extension using ContentPage for simple form](../../images/command-palette/form-simple.png)
137+
138+
Adaptive Cards can do more complex forms, including using another json object to dynamically create custom forms. You'll first set up your form with the [Adaptive Card Designer](https://adaptivecards.io/designer/) and then update your command.
139+
140+
1. Open https://adaptivecards.io/designer/
141+
1. In the `CARD PAYLOAD EDITOR` replace the json with:
142+
143+
```json
144+
{
145+
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
146+
"type": "AdaptiveCard",
147+
"version": "1.6",
148+
"body": [
149+
{
150+
"type": "TextBlock",
151+
"size": "medium",
152+
"weight": "bolder",
153+
"text": " ${ParticipantInfoForm.title}",
154+
"horizontalAlignment": "center",
155+
"wrap": true,
156+
"style": "heading"
157+
},
158+
{
159+
"type": "Input.Text",
160+
"label": "Name",
161+
"style": "text",
162+
"id": "Name",
163+
"isRequired": true,
164+
"errorMessage": "Name is required",
165+
"placeholder": "Enter your name"
166+
},
167+
{
168+
"type": "TextBlock",
169+
"size": "medium",
170+
"weight": "bolder",
171+
"text": "${Survey.title} ",
172+
"horizontalAlignment": "center",
173+
"wrap": true,
174+
"style": "heading"
175+
},
176+
{
177+
"type": "Input.Toggle",
178+
"label": "Please accept the terms and conditions:",
179+
"title": "${Survey.questions[0].question}",
180+
"valueOn": "true",
181+
"valueOff": "false",
182+
"id": "AcceptsTerms",
183+
"isRequired": true,
184+
"errorMessage": "Accepting the terms and conditions is required"
185+
},
186+
{
187+
"type": "Input.Toggle",
188+
"label": "How do you feel about red cars?",
189+
"title": "${Survey.questions[1].question}",
190+
"valueOn": "RedCars",
191+
"valueOff": "NotRedCars",
192+
"id": "ColorPreference"
193+
}
194+
],
195+
"actions": [
196+
{
197+
"type": "Action.Submit",
198+
"title": "Submit",
199+
"data": {
200+
"id": "1234567890"
201+
}
202+
}
203+
]
204+
}
205+
```
206+
207+
1. In the `SAMPLE DATA EDITOR` replace the json with:
208+
209+
```json
210+
{
211+
"ParticipantInfoForm": {
212+
"title": "Input.Text elements"
213+
},
214+
"Survey": {
215+
"title": "Input Toggle",
216+
"questions": [
217+
{
218+
"question": "I accept the terms and conditions (True/False)"
219+
},
220+
{
221+
"question": "Red cars are better than other cars"
222+
}
223+
]
224+
}
225+
}
226+
```
227+
228+
The Designer tool should look something like this:
229+
230+
![Screenshot of extension using ContentPage for simple form](../../images/command-palette/form-toggle.png)
231+
232+
To add this content to your extension:
233+
234+
1. Update `TemplateJson` with `CARD PAYLOAD EDITOR` content
235+
1. Under `TemplateJson`, add:
236+
237+
```csharp
238+
DataJson = $$"""
239+
{
240+
"ParticipantInfoForm": {
241+
"title": "Input.Text elements"
242+
},
243+
"Survey": {
244+
"title": "Input Toggle",
245+
"questions": [
246+
{
247+
"question": "I accept the terms and conditions (True/False)"
248+
},
249+
{
250+
"question": "Red cars are better than other cars"
251+
}
252+
]
253+
}
254+
}
255+
""";
256+
```
257+
258+
1. Deploy your extension
259+
1. In command palette, `Reload`
260+
261+
![Screenshot of extension using ContentPage for simple form](../../images/command-palette/form-toggle-comdpal.png)
262+
263+
`TemplateJson` and `DataJson` work together to create dynamic, data-driven forms. `TemplateJson` can act as fhe Form Blueprint and `DataJson` as the Dynamic Content Source.
264+
265+
## Full Sample
266+
267+
For a full example of using Forms and Content pages, head on over to [`SamplePagesExtension/Pages/SampleContentPage.cs`](https://github.com/microsoft/PowerToys/blob/main/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleContentPage.cs).
268+
269+
## Key Items
270+
271+
- Define your form layout using the `TemplateJson` property of your `FormContent`. This is the JSON payload from the CARD PAYLOAD EDITOR in the https://adaptivecards.io/designer/. It describes the structure and UI of your form.
272+
273+
- Optionally bind dynamic data using the `DataJson` property. This is the JSON from the SAMPLE DATA EDITOR in the Adaptive Card Designer. It allows you to inject dynamic values into your card using ${...} placeholders, making your forms easier to localize and maintain.
25274

26-
- Set the **TemplateJson** property of your form to the JSON payload of your Adaptive Card. (this is the "CARD PAYLOAD EDITOR" value in the Adaptive Card Designer)
27-
- Set the **DataJson** property of your **FormContent** to the data you want to use to fill in your card template. (This is the "SAMPLE DATA EDITOR" value in the Adaptive Card Designer). This is optional, but can make authoring cards easier.
28-
- Implement the **SubmitForm** method to handle the form submission. This method will be called when the user submits the form, and will be passed the JSON payload of the form.
275+
- Handle form submissions by implementing the `SubmitForm` method. This method is called when the user submits the form. Youll receive the forms payload as a JSON string, which you can parse and use to trigger actions, show confirmation dialogs, or return navigation results.
29276

30277
```csharp
31278
public override CommandResult SubmitForm(string payload)
@@ -46,7 +293,8 @@ public override CommandResult SubmitForm(string payload)
46293
}
47294
```
48295

49-
Of course, you can mix and match **IContent** in whatever way you'd like. For example, you could use a block markdown first for the body of a post, and have a **FormContent** next to reply to the post.
296+
> [!NOTE]
297+
> You can mix and match different `IContent` types in your extension. For example, you might use a `MarkdownContent` block to display a post, followed by a `FormContent` block to collect a reply.
50298
51299
## Related content
52300

0 commit comments

Comments
 (0)