|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: AI-powered Smart .NET MAUI Dataform for Data Entry | Syncfusion® |
| 4 | +description: Learn here all about how to build an AI powered smart data entry in .NET MAUI SfDataForm by integrating Azure OpenAI. |
| 5 | +platform: maui |
| 6 | +control: SfDataForm |
| 7 | +documentation: ug |
| 8 | +--- |
| 9 | + |
| 10 | +# AI-powered Smart .NET MAUI Dataform for Data Entry |
| 11 | + |
| 12 | +This guide explains how to implement AI-powered smart data forms in a .NET MAUI application using Syncfusion® DataForm ([SfDataForm](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.DataForm.SfDataForm.html)) and AIAssistView ([SfAIAssistView](https://help.syncfusion.com/cr/maui/Syncfusion.Maui.AIAssistView.SfAIAssistView.html)) controls. These forms automatically generate fields, validate input, and guide users interactively using AI logic from Azure OpenAI Services. |
| 13 | + |
| 14 | +## Integrating Azure OpenAI with the .NET MAUI app |
| 15 | + |
| 16 | +### Step 1: Set Up the .NET MAUI Project |
| 17 | + |
| 18 | +Create a [.NET MAUI app](https://learn.microsoft.com/en-us/dotnet/maui/get-started/first-app?view=net-maui-9.0&viewFallbackFrom=net-maui-7.0&tabs=vswin&pivots=devices-android) in [Visual Studio](https://visualstudio.microsoft.com/) and install the following NuGet packages: `Syncfusion.Maui.DataForm`, `Syncfusion.Maui.AIAssistView`, [`Azure.AI.OpenAI`](https://www.nuget.org/packages/Azure.AI.OpenAI/1.0.0-beta.12), [`Microsoft.Extensions.AI.OpenAI`](https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/9.8.0-preview.1.25412.6) and [`Azure.Identity`](https://www.nuget.org/packages/Azure.Identity) from [NuGet Gallery](https://www.nuget.org/) |
| 19 | + |
| 20 | +### Step 2: Set up Azure OpenAI |
| 21 | + |
| 22 | +To enable AI functionality in your .NET MAUI Scheduler, first ensure that you have access to [Azure OpenAI](https://azure.microsoft.com/en-in/products/ai-services/openai-service). In the Azure portal, create an Azure OpenAI resource and deploy a model such as GPT-35. Assign a deployment name (for example, GPT35Turbo) that you’ll reference in your application code. Finally, copy the API key and endpoint URL from the resource settings, as these are required for authentication and communication with the OpenAI service. |
| 23 | + |
| 24 | +### Step 3: Connect to the Azure OpenAI |
| 25 | + |
| 26 | +To connect your .NET MAUI app to Azure OpenAI, create a service class that handles communication with the AI model. |
| 27 | + |
| 28 | +``` |
| 29 | +/// <summary> |
| 30 | +/// Represents Class to interact with Azure AI. |
| 31 | +/// </summary> |
| 32 | +internal class DataFormAIService : AzureBaseService |
| 33 | +{ |
| 34 | + |
| 35 | +} |
| 36 | +``` |
| 37 | +In this service, define a method called `GetAnswerFromGPT`. This method takes a user prompt from the SfAIAssistView control as input, sends it to the deployed model (e.g., GPT35Turbo), and returns the AI-generated response. |
| 38 | + |
| 39 | +``` |
| 40 | + /// <summary> |
| 41 | + /// Represents Class to interact with Azure AI. |
| 42 | + /// </summary> |
| 43 | + public class AzureAIServices : AzureBaseService |
| 44 | + { |
| 45 | + /// <summary> |
| 46 | + /// Initializes a new instance of the <see cref="AzureAIServices"/> class. |
| 47 | + /// </summary> |
| 48 | + public AzureAIServices() |
| 49 | + { |
| 50 | + |
| 51 | + } |
| 52 | +
|
| 53 | + /// <summary> |
| 54 | + /// Retrieves an answer from the deployment name model using the provided user prompt. |
| 55 | + /// </summary> |
| 56 | + /// <param name="userPrompt">The user prompt.</param> |
| 57 | + /// <returns>The AI response.</returns> |
| 58 | + internal async Task<string> GetAnswerFromGPT(string userPrompt) |
| 59 | + { |
| 60 | + ChatHistory = string.Empty; |
| 61 | + if (IsCredentialValid && Client != null && ChatHistory != null) |
| 62 | + { |
| 63 | + // Add the user's prompt as a user message to the conversation. |
| 64 | + ChatHistory = ChatHistory + "You are a predictive analytics assistant."; |
| 65 | + // Add the user's prompt as a user message to the conversation. |
| 66 | + ChatHistory = ChatHistory + userPrompt; |
| 67 | + try |
| 68 | + { |
| 69 | + //// Send the chat completion request to the OpenAI API and await the response. |
| 70 | + var response = await Client.CompleteAsync(ChatHistory); |
| 71 | + return response.ToString(); |
| 72 | + } |
| 73 | + catch |
| 74 | + { |
| 75 | + // If an exception occurs (e.g., network issues, API errors), return an empty string. |
| 76 | + return ""; |
| 77 | + } |
| 78 | + } |
| 79 | +
|
| 80 | + return ""; |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +Within the base service class (AzureBaseService), initialize the OpenAIClient with your Azure endpoint, deployment name, and API key. |
| 87 | + |
| 88 | +``` |
| 89 | + public abstract class AzureBaseService |
| 90 | + { |
| 91 | + #region Fields |
| 92 | + /// <summary> |
| 93 | + /// The EndPoint |
| 94 | + /// </summary> |
| 95 | + internal const string Endpoint = "YOUR_END_POINT_NAME"; |
| 96 | +
|
| 97 | + /// <summary> |
| 98 | + /// The Deployment name |
| 99 | + /// </summary> |
| 100 | + internal const string DeploymentName = "DEPLOYMENT_NAME"; |
| 101 | +
|
| 102 | + /// <summary> |
| 103 | + /// The Image Deployment name |
| 104 | + /// </summary> |
| 105 | + internal const string ImageDeploymentName = "IMAGE_DEPOLYMENT_NAME"; |
| 106 | +
|
| 107 | + /// <summary> |
| 108 | + /// The API key |
| 109 | + /// </summary> |
| 110 | + internal const string Key = "API_KEY"; |
| 111 | +
|
| 112 | + /// <summary> |
| 113 | + /// The already credential validated field |
| 114 | + /// </summary> |
| 115 | + private static bool isAlreadyValidated = false; |
| 116 | +
|
| 117 | + /// <summary> |
| 118 | + /// Indicating whether an credentials are valid or not |
| 119 | + /// </summary> |
| 120 | + private static bool _isCredentialValid; |
| 121 | +
|
| 122 | + #endregion |
| 123 | +
|
| 124 | + public AzureBaseService() |
| 125 | + { |
| 126 | + ValidateCredential(); |
| 127 | + } |
| 128 | +
|
| 129 | + internal IChatClient? Client { get; set; } |
| 130 | +
|
| 131 | + /// <summary> |
| 132 | + /// To get the Azure open ai method |
| 133 | + /// </summary> |
| 134 | + private void GetAzureOpenAI() |
| 135 | + { |
| 136 | + try |
| 137 | + { |
| 138 | + var client = new AzureOpenAIClient(new Uri(Endpoint), new AzureKeyCredential(Key)).AsChatClient(modelId: DeploymentName); |
| 139 | + this.Client = client; |
| 140 | + } |
| 141 | + catch (Exception) |
| 142 | + { |
| 143 | + } |
| 144 | + } |
| 145 | + } |
| 146 | + ``` |
| 147 | + |
| 148 | +## Integrating AI-powered smart DataForm Generation in .NET MAUI DataForm |
| 149 | + |
| 150 | +### Step 1: Designing the User Interface |
| 151 | + |
| 152 | +#### Editor and Button - Capturing User Prompts |
| 153 | + |
| 154 | +Use an Editor to collect natural language prompts and a Button to send the prompt to Azure OpenAI. The Editor allows users to describe the form they want, while the Button triggers the logic to process the prompt and generate the form. |
| 155 | + |
| 156 | +``` |
| 157 | + <VerticalStackLayout Margin="20" VerticalOptions="Center" HorizontalOptions="Center"> |
| 158 | + <Label x:Name="describeLabel" |
| 159 | + Text="Create AI-Powered Smart Forms in .NET MAUI for Efficient Productivity." |
| 160 | + LineBreakMode="WordWrap" FontSize="Small" FontAttributes="Bold" /> |
| 161 | + <Grid ColumnDefinitions="0.7*,0.3*" Margin="10" ColumnSpacing="5"> |
| 162 | + |
| 163 | + <Editor AutoSize="TextChanges" x:Name="entry" |
| 164 | + PlaceholderColor="Gray" |
| 165 | + VerticalOptions="Center" |
| 166 | + HorizontalOptions="Fill" |
| 167 | + Placeholder="Create your own data form" /> |
| 168 | + <Button x:Name="createButton" |
| 169 | + Grid.Column="1" CornerRadius="10" |
| 170 | + HeightRequest="35" Text="" |
| 171 | + FontSize="Small" |
| 172 | + FontFamily="MauiMaterialAssets" |
| 173 | + VerticalOptions="Center" HorizontalOptions="Start" /> |
| 174 | + </Grid> |
| 175 | + </VerticalStackLayout> |
| 176 | +``` |
| 177 | + |
| 178 | +#### Busy Indicator - Showing Processing Status |
| 179 | + |
| 180 | +The SfBusyIndicator provides visual feedback while the AI processes the prompt. It is shown during form generation and hidden once the form is ready. |
| 181 | + |
| 182 | +``` |
| 183 | +xmlns:core="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core" |
| 184 | +
|
| 185 | + <core:SfBusyIndicator IsVisible="False" |
| 186 | + x:Name="busyIndicator" |
| 187 | + IsRunning="False" |
| 188 | + AnimationType="Cupertino" /> |
| 189 | +``` |
| 190 | + |
| 191 | +#### DataForm - Displaying the Generated Form |
| 192 | + |
| 193 | +The SfDataForm renders the generated form dynamically based on the AI response. |
| 194 | + |
| 195 | +``` |
| 196 | +xmlns:dataform="clr-namespace:Syncfusion.Maui.DataForm;assembly=Syncfusion.Maui.DataForm" |
| 197 | +
|
| 198 | + <dataform:SfDataForm x:Name="dataForm" |
| 199 | + Grid.RowSpan="1" |
| 200 | + Grid.Row="1" AutoGenerateItems="False" |
| 201 | + ValidationMode="PropertyChanged" |
| 202 | + LayoutType="TextInputLayout" |
| 203 | + HorizontalOptions="Center"> |
| 204 | + <dataform:SfDataForm.TextInputLayoutSettings> |
| 205 | + <dataform:TextInputLayoutSettings ShowHelperText="True"/> |
| 206 | + </dataform:SfDataForm.TextInputLayoutSettings> |
| 207 | + </dataform:SfDataForm> |
| 208 | +``` |
| 209 | + |
| 210 | +#### AI AssistView - Providing Suggestions |
| 211 | + |
| 212 | +The SfAIAssistView offers contextual help, such as real-time suggestions or chatbot-style assistance. |
| 213 | + |
| 214 | +``` |
| 215 | + <aiassistview:SfAIAssistView x:Name="aiAssistView" |
| 216 | + Grid.Row="1" HorizontalOptions="Fill" |
| 217 | + ShowHeader="False" |
| 218 | + AssistItems="{Binding Messages}"> |
| 219 | + <aiassistview:SfAIAssistView.Behaviors> |
| 220 | + <local:DataFormAssistViewBehavior x:Name="dataFormAssistViewModel" AIActionButton="{x:Reference aiActionButton}" RefreshButton="{x:Reference refreshButton}" CloseButton="{x:Reference close}" |
| 221 | + DataFormNameLabel="{x:Reference dataFormNameLabel}" BusyIndicator="{x:Reference busyIndicator}" DataForm="{x:Reference dataForm}" DataFormGeneratorModel="{x:Reference dataFormGeneratorModel}" Entry="{x:Reference entry}" CreateButton="{x:Reference createButton}"/> |
| 222 | + </aiassistview:SfAIAssistView.Behaviors> |
| 223 | + </aiassistview:SfAIAssistView> |
| 224 | +``` |
| 225 | + |
| 226 | +### Step 2: Create and Edit Data Form Items using Azure OpenAI |
| 227 | + |
| 228 | +#### Creating Data Form Items |
| 229 | + |
| 230 | +We first create a button click event that triggers the AI-powered form item generation process. |
| 231 | + |
| 232 | +``` |
| 233 | + private async void OnCreateButtonClicked(object? sender, EventArgs e) |
| 234 | + { |
| 235 | + UpdateBusyIndicator(true); |
| 236 | +
|
| 237 | + if (AzureBaseService.IsCredentialValid && Entry?.Text is string text && !string.IsNullOrEmpty(text)) |
| 238 | + { |
| 239 | + GetDataFormFromAI(text); |
| 240 | + } |
| 241 | + else if (!string.IsNullOrEmpty(DataFormGeneratorModel?.FormTitle)) |
| 242 | + { |
| 243 | + await CreateOfflineDataForm(DataFormGeneratorModel.FormTitle); |
| 244 | + DataFormGeneratorModel.ShowInputView = false; |
| 245 | + DataFormGeneratorModel.ShowDataForm = true; |
| 246 | + } |
| 247 | +
|
| 248 | + } |
| 249 | +``` |
| 250 | + |
| 251 | +#### Generate Items from User Prompts |
| 252 | + |
| 253 | +The following method sends the user’s prompt to Azure OpenAI and processes the response to generate actions such as New Form, Change Title, Add, Remove, or Replace. |
| 254 | + |
| 255 | +``` |
| 256 | +internal async void GetDataFormFromAI(string userPrompt) |
| 257 | +{ |
| 258 | + string prompt = $"Given the user's input: {userPrompt}, determine the most appropriate single action to take. " + |
| 259 | + $"The options are 'Add', 'Add Values','PlaceholderText' ,'Remove', 'Replace', 'Insert', 'New Form', 'Change Title', or 'No Change'" + |
| 260 | + " Without additional formatting and special characters like backticks, newlines, or extra spaces."; |
| 261 | +
|
| 262 | + var response = await this.semanticKernelService.GetAnswerFromGPT(prompt); |
| 263 | +
|
| 264 | + if (string.IsNullOrEmpty(response)) |
| 265 | + { |
| 266 | + AssistItem subjectMessage = new AssistItem() { Text = "Please try again...", ShowAssistItemFooter = false }; |
| 267 | + this.DataFormGeneratorModel?.Messages.Add(subjectMessage); |
| 268 | + UpdateCreateVisibility(); |
| 269 | + UpdateBusyIndicator(false); |
| 270 | + } |
| 271 | + else |
| 272 | + { |
| 273 | + if (response == string.Empty) |
| 274 | + { |
| 275 | + UpdateBusyIndicator(false); |
| 276 | + if (Application.Current != null) |
| 277 | + { |
| 278 | + var mainWindow = Application.Current.Windows.FirstOrDefault(); |
| 279 | + if (mainWindow != null && mainWindow.Page != null) |
| 280 | + { |
| 281 | + await mainWindow.Page.DisplayAlert("", "Please enter valid inputs.", "OK"); |
| 282 | + } |
| 283 | + } |
| 284 | + } |
| 285 | + else if (response == "New Form") |
| 286 | + { |
| 287 | + if (this.DataFormGeneratorModel != null) |
| 288 | + this.DataFormGeneratorModel.ShowOfflineLabel = false; |
| 289 | + this.GenerateAIDataForm(userPrompt); |
| 290 | + } |
| 291 | + else if (response == "Change Title") |
| 292 | + { |
| 293 | + string dataFormNamePrompt = $"Change the title for data form based on user prompt: {userPrompt}. Provide only the title, with no additional explanation"; |
| 294 | + string getDataFormName = await this.semanticKernelService.GetAnswerFromGPT(dataFormNamePrompt); |
| 295 | + this.DataFormNameLabel!.Text = getDataFormName; |
| 296 | + AssistItem subjectMessage = new AssistItem() { Text = "The Data Form title changed successfully...", ShowAssistItemFooter = false }; |
| 297 | + this.DataFormGeneratorModel?.Messages.Add(subjectMessage); |
| 298 | + } |
| 299 | + else |
| 300 | + { |
| 301 | + this.EditDataForm(userPrompt, response); |
| 302 | + } |
| 303 | + } |
| 304 | +} |
| 305 | +``` |
| 306 | + |
| 307 | +``` |
| 308 | +private async void GenerateAIDataForm(string userPrompt) |
| 309 | +{ |
| 310 | + string dataFormNamePrompt = $"Generate a title for a data form based on the following string: {userPrompt}. The title should clearly reflect the purpose of the data form in general term. Provide only the title, with no additional explanation"; |
| 311 | + string getDataFormName = await this.semanticKernelService.GetAnswerFromGPT(dataFormNamePrompt); |
| 312 | + this.DataFormNameLabel!.Text = getDataFormName; |
| 313 | +
|
| 314 | + string prompt = $"Generate a data form based on the user prompt: {userPrompt}."; |
| 315 | + string condition = "Property names must be in PascalCase. " + |
| 316 | + "Must be property names and its value " + |
| 317 | + "Without additional formatting characters like backticks, newlines, or extra spaces. " + |
| 318 | + "and map each property to the most appropriate DataForm available item type includes: DataFormTextItem , DataFormMultiLineTextItem, DataFormPasswordItem, DataFormNumericItem, DataFormMaskedTextItem, DataFormDateItem, DataFormTimeItem, DataFormCheckBoxItem, DataFormSwitchItem, DataFormPickerItem, DataFormComboBoxItem, DataFormAutoCompleteItem, DataFormRadioGroupItem, DataFormSegmentItem" + |
| 319 | + "The result must be in JSON format" + |
| 320 | + "Without additional formatting characters like backticks, newlines, or extra spaces."; |
| 321 | +
|
| 322 | + var typeResponse = await this.semanticKernelService.GetAnswerFromGPT(prompt + condition); |
| 323 | +
|
| 324 | + var dataFormTypes = JsonConvert.DeserializeObject<Dictionary<string, object>>(typeResponse); |
| 325 | +
|
| 326 | + if (this.DataForm != null && dataFormTypes != null) |
| 327 | + { |
| 328 | + var items = new ObservableCollection<DataFormViewItem>(); |
| 329 | + foreach (var data in dataFormTypes) |
| 330 | + { |
| 331 | + DataFormItem? dataFormItem = GenerateDataFormItems(data.Value.ToString(), data.Key); |
| 332 | + if (dataFormItem != null) |
| 333 | + items.Add(dataFormItem); |
| 334 | + } |
| 335 | +
|
| 336 | + this.DataForm.Items = items; |
| 337 | + } |
| 338 | +
|
| 339 | + AssistItem subjectMessage = new AssistItem() { Text = "As per your comment data form created successfully...", ShowAssistItemFooter = false }; |
| 340 | + this.DataFormGeneratorModel?.Messages.Add(subjectMessage); |
| 341 | +
|
| 342 | + UpdateCreateVisibility(); |
| 343 | + UpdateBusyIndicator(false); |
| 344 | +} |
| 345 | +``` |
| 346 | + |
| 347 | +#### Generating a New Data Form |
| 348 | + |
| 349 | +When the user request is identified as "New Form", a complete form can be created dynamically. |
| 350 | + |
| 351 | +#### Editing Data Form Items |
| 352 | + |
| 353 | +Azure OpenAI also allows editing an existing form. The following operations are supported: |
| 354 | + |
| 355 | +(a) Add a New Item |
| 356 | + |
| 357 | +* Generates a new property and maps it to the best fitting DataForm item type. |
| 358 | + |
| 359 | +(b) Remove an Item |
| 360 | + |
| 361 | +* Finds and removes an existing property from the DataForm. |
| 362 | + |
| 363 | +(c) Replace an Item |
| 364 | + |
| 365 | +* Replaces one form field with another. |
| 366 | + |
| 367 | +(d) Add Values to a Field |
| 368 | + |
| 369 | +* Populates additional values into picker or combo-box items. |
| 370 | + |
| 371 | +#### Handling Requests via AIAssistView |
| 372 | + |
| 373 | +Finally, the `Request` event in AIAssistView listens to user inputs and invokes the data form generation or edit methods. |
| 374 | + |
| 375 | +With these implementations, the DataForm becomes AI-powered, enabling users to create and modify form structures dynamically via Azure OpenAI. |
| 376 | + |
| 377 | +``` |
| 378 | + private async void OnAssistViewRequest(object? sender, RequestEventArgs e) |
| 379 | + { |
| 380 | + string requestText = e.RequestItem.Text; |
| 381 | + if (AzureBaseService.IsCredentialValid && this.DataFormGeneratorModel != null) |
| 382 | + { |
| 383 | + this.DataFormGeneratorModel.ShowOfflineLabel = false; |
| 384 | + this.GetDataFormFromAI(requestText); |
| 385 | + return; |
| 386 | + } |
| 387 | +
|
| 388 | + await CreateOfflineDataForm(requestText); |
| 389 | + } |
| 390 | +
|
| 391 | +``` |
| 392 | + |
| 393 | + |
| 394 | + |
| 395 | +You can download the complete sample from this [link](https://github.com/syncfusion/maui-demos/tree/master/MAUI/SmartComponents/SampleBrowser.Maui.SmartComponents/Samples/SmartComponents/SmartDataForm). |
0 commit comments