
Hello,
In the Sample Browser under User Prompt Quickstart, it shows a user prompt with an animated progress bar. However, everything is static. How can we make the progress of the progress bar and the text update dynamically?
Thanks.
Benjamin.
Hello,
In the Sample Browser under User Prompt Quickstart, it shows a user prompt with an animated progress bar. However, everything is static. How can we make the progress of the progress bar and the text update dynamically?
Thanks.
Benjamin.
Hi Benjamin,
Great question, and we've updated our sample for the next release to show exactly how to achieve that. The steps are essentially as follows:
Step 1: Cache UI control references that need to be updated
To update the progress bar and status while the prompt is open, the easiest approach is to store references to these controls in local variables that are later used while defining the content of the prompt. In our updated sample, we created the two controls with their default state and updated the "WithContent" method to use these instances when building the content.
Step 2: Update Controls from Background Thread
It is important to note that while the modal dialog is open, the calling thread is blocked. This means any work must be performed on a background thread. When updating the controls from the background thread, you must use "Dispatcher.InvokeAsync" to push the update to the UI thread without blocking the background thread.
In our sample, we used the "BeforeShow" callback to start running a Task. We used this callback because it passes an instance of the UserPromptBuilder that allows us to interact with the associated UserPromptControl that is displayed. We can then use the UserPromptControl.Result property to test if the prompt has been cancelled and optionally automatically assign a result when our work completes.
Updated Sample
Here's the full source of the updated sample that will be included in the next release
//
// SAMPLE: Customize the header and content
//
var statusText = new TextBlock() {
Text = "Estimated time remaining:",
Margin = new Thickness(0, 2, 0, 2)
};
var progressBar = new AnimatedProgressBar() {
Margin = new Thickness(0, 5, 0, 0),
Minimum = 0,
Maximum = 100,
Value = 0,
Height = 20,
State = OperationState.Normal
};
ConfigureUserPrompt(displayResult: true)
// Setting any header background will align the status icon and header content
.WithHeaderContent("Exporting Project (Sample Project)")
.WithHeaderForeground(Colors.White)
.WithHeaderBackground(
new LinearGradientBrush(
startColor: (Color)ColorConverter.ConvertFromString("#094C75"),
endColor: (Color)ColorConverter.ConvertFromString("#066F5C"),
angle: 0d)
)
.WithStatusImage(ImageLoader.GetIcon("Save32.png"))
.WithStandardButtons(UserPromptStandardButtons.Cancel)
.WithContent(new StackPanel() {
Children = {
new TextBlock() {
Inlines = {
new Run("to "),
new Run("Project Templates") { FontWeight = FontWeights.Bold },
new Run(@" (C:\Templates\ProjectTemplates)"),
}
},
statusText,
progressBar
}
})
.WithCheckBoxContent("Check this box to simulate an exception")
.WithWindowStartupLocation(WindowStartupLocation.CenterOwner)
.WithAutoSize(true, minimumWidth: 400)
.BeforeShow(builder => {
// Do work here
Task.Run(() => {
var totalTime = TimeSpan.FromSeconds(10);
var startTaskTime = DateTime.Now;
var isCompleted = false;
var throwException = false;
try {
do {
if (throwException)
throw new ApplicationException("An error was encountered during export.");
Thread.Sleep(100);
var elapsedTime = DateTime.Now - startTaskTime;
var remainingTime = totalTime - elapsedTime;
var percentageComplete = ((elapsedTime / totalTime) * 100);
Dispatcher.InvokeAsync(() => {
throwException = builder.Instance?.IsChecked == true;
if (!throwException) {
progressBar.Value = percentageComplete;
statusText.Text = $"Estimated time remaining: {remainingTime.TotalSeconds.Round(RoundMode.Ceiling)} seconds";
isCompleted = (progressBar.IsCompleted || (builder.Instance?.Result is not null));
}
});
} while (!isCompleted);
if (isCompleted) {
Dispatcher.InvokeAsync(() => {
if ((builder.Instance is not null) && (builder.Instance.Result is null))
builder.Instance.Result = builder.Instance.DefaultResult;
});
}
}
catch (Exception ex) {
Dispatcher.InvokeAsync(() => {
statusText.Text = "Error: " + ex.Message;
progressBar.State = OperationState.Error;
});
}
});
})
.Show();
Nice, thanks! It works except for the size. I tried setting the minimum size to 1000 and even disabled auto-size, but it still always uses the smallest possible size.
Can you please provide more information about what you are referring to in regards to size since the original post only mentioned static content? Are you trying to create a resizable window? From the same sample above, I commented out the following line that auto-sized the content:
.WithAutoSize(true, minimumWidth: 400)
And then I added the following line to configure the window to support resizing:
.WithCanResize(true)
With those two changes the window was resizable and the content would adjust to the size of the window. If you want the window to be resizable you do have to disable auto-sizing of the content. Content auto-sizing is performed once to determine the best width for the content and explicitly sets the content to that width. It is intended to be used with non-resizable windows that will be sized to fit their content. More details about reizable windows here.
Please note that improved support for resizable windows was added in v25.1.0.
Please log in to a validated account to post comments.