问题
I have modified code, but now I have another problem. The InvalidOperation exception occurs inside if statement on validating user info. It says that the calling thread cannot access this object because a different thread owns it.Any sugestions?
private void finishConfigButton_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
bool validated = false;
errorLabel.Visibility = System.Windows.Visibility.Collapsed;
validationProfile.IsBusy = true;
finishConfigButton.IsEnabled = false;
backToLoginHyperlink.IsEnabled = false;
worker.DoWork += (o, ea) =>
{
if (newUser.ValidateNewUserInformation(newNameTextBox.Text, newEmailTextBox.Text, newUsernameTextBox.Text, newPasswordPasswordBox.Password, ref errorLabel))
{
validated = true;
string activeDir = Environment.SystemDirectory.Substring(0, 1) + @":\Users\" + Environment.UserName + @"\My Documents\SSK\Users";
string newPath = System.IO.Path.Combine(activeDir, newUser.Username);
Directory.CreateDirectory(newPath);
newUser.SaveUserData(newUser);
newPath = System.IO.Path.Combine(activeDir, newUser.Username + @"\Settings");
Directory.CreateDirectory(newPath);
newUserSettings.SetDefaultValues();
newUserSettings.SaveSettings(newUser, newUserSettings);
}
else
validated = false;
if (worker.CancellationPending)
{
ea.Cancel = true;
return;
}
};
worker.RunWorkerCompleted += (o, ea) =>
{
validationProfile.IsBusy = false;
finishConfigButton.IsEnabled = true;
backToLoginHyperlink.IsEnabled = true;
};
worker.RunWorkerAsync(this);
if (validated)
{
IntelliMonitorWindow intelliMonitor = new IntelliMonitorWindow(newUser, newUserSettings);
intelliMonitor.Show();
this.Close();
}
else
errorLabel.Visibility = System.Windows.Visibility.Visible;
}
回答1:
What you are doing here is running everything on the UI thread. This means that while the heavy code is running, you are blocking the UI from repainting, and hence the validationProfile is not updated untill the end of the method, where IsBusy is set to false.
What you need to do is to process the heavy code into a new thread, which can update the UI at the same time.
Take a look at this blog post written by Brian Lagunas, the creator of Extended Toolkit: http://elegantcode.com/2011/10/07/extended-wpf-toolkitusing-the-busyindicator/
He explains how to use the BusyIndicator with a BackgroundWorker.
回答2:
The busy indicator in your XAML code does not have any content. Put some control(s) into it:
<wpfet:BusyIndicator Name="validationProfile" IsBusy="False" BusyContent="Working...Please wait" DisplayAfter="0" Background="DimGray">
<Grid>
...
</Grid>
</wpfet:BusyIndicator>
If you change to busy, those controls will get disabled and the BusyIndicator will appear above them.
I suppose you want to wrap the whole <Grid Background="LightGray">
with the BusyIndicator.
回答3:
Use a background worker or a new thread to run the heavy process and set the UI thread free. This helps you to update the UI even when the background process is running
Eg:
public void finishConfigButton_Click()
{
worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
//Do the heavy work here
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
//Things to do after the execution of heavy work
validationProfile.IsBusy = false;
};
validationProfile.IsBusy= true;
worker.RunWorkerAsync();
}
}
回答4:
I finally figure it out. You can't use UI objects inside worker.DoWork block.I slightly modified the code and it now works.
private void finishConfigButton_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
errorLabel.Visibility = System.Windows.Visibility.Collapsed;
validationProfile.IsBusy = true;
finishConfigButton.IsEnabled = false;
backToLoginHyperlink.IsEnabled = false;
bool validated = false;
string newName = newNameTextBox.Text;
string newEmail = newEmailTextBox.Text;
string newUsername = newUsernameTextBox.Text;
string newPassword = newPasswordPasswordBox.Password;
string errorMessage = "Unknown error.";
worker.DoWork += (o, ea) =>
{
if (newUser.ValidateNewUserInformation(newName, newEmail, newUsername, newPassword, ref errorMessage))
{
string activeDir = Environment.SystemDirectory.Substring(0, 1) + @":\Users\" + Environment.UserName + @"\My Documents\SSK\Users";
string newPath = System.IO.Path.Combine(activeDir, newUser.Username);
Directory.CreateDirectory(newPath);
newUser.SaveUserData(newUser);
newPath = System.IO.Path.Combine(activeDir, newUser.Username + @"\Settings");
Directory.CreateDirectory(newPath);
newUserSettings.SetDefaultValues();
newUserSettings.SaveSettings(newUser, newUserSettings);
validated = true;
}
else
ea.Cancel = true;
};
worker.RunWorkerCompleted += (o, ea) =>
{
if (validated)
{
IntelliMonitorWindow intelliMonitor = new IntelliMonitorWindow(newUser, newUserSettings);
intelliMonitor.Show();
this.Close();
}
validationProfile.IsBusy = false;
finishConfigButton.IsEnabled = true;
backToLoginHyperlink.IsEnabled = true;
errorLabel.Visibility = System.Windows.Visibility.Visible;
errorLabel.Content = errorMessage;
};
worker.RunWorkerAsync();
}
来源:https://stackoverflow.com/questions/9664391/showing-busy-indicator-control-inside-a-ui