I have a WPF application that will run a bunch of executables (via Process) on a button click. In the form, there is a TextBlock that I want to fill with instructions for each executable. The problem is, the TextBlock doesn't update until the Process is complete. I've checked the TextBlock.Text value in the debugger and it has the correct text, but it isn't displayed. Is there a way to force the WPF TextBlock to display its current Text property?
Here is my code:
MainWindow.xaml
<Grid>
<TextBlock x:Name="InstructionsTextBlock" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding Instructions.Instructions, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Height="200" Width="287" Background="#FFB0BBAD"/>
<Button Margin="52,223,525,149" x:Name="UpdateTextBlockAndRunExe" Content="Update TextBlock and run .exe" Click="UpdateTextBlockAndRunExe_Click"></Button>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow: Window {
MainViewModel _main = new MainViewModel();
public MainWindow() {
InitializeComponent();
DataContext = _main;
_main.SetInstructions("Initial Instructions");
}
private void UpdateTextBlockAndRunExe_Click(object sender, RoutedEventArgs e) {
_main.SetInstructions("Before Exe - I want this to show up");
string executablePath = Path.Combine(Utils.getResourcesDirectory(), "uniws", "uniws.exe");
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.FileName = executablePath;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
using(Process exeProcess = Process.Start(startInfo)) {
exeProcess.WaitForExit();
}
_main.SetInstructions("After Exe - I want to end on this");
}
}
MainViewModel.cs
public class MainViewModel {
public InstructionsViewModel Instructions {
get;
private set;
}
public MainViewModel() {
Instructions = new InstructionsViewModel();
}
public void SetInstructions(string instructions) {
Instructions.Instructions = instructions;
}
}
InstructionsViewModel.cs
public class InstructionsViewModel: ObservableObject {
private string _instructions;
public string Instructions {
get {
if (string.IsNullOrEmpty(_instructions))
return "No instructions";
return _instructions;
}
set {
_instructions = value;
OnPropertyChanged("Instructions");
}
}
}
ObservableObject.cs
public class ObservableObject: INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
During Process Exe:
Debug Mode - Showing that the Text property is set:
CodePudding user response:
In short, no. The updates need to be done on the GUI thread, but you're blocking it with calls to WaitForExit.
Best way to handle this is with asynchronous programming. Change your click handler to be async and then await each process to finish using the WaitForExitAsync
function in the answer to this question.
CodePudding user response:
You can use the Dispatcher:
Dispatcher.Invoke(() => _main.SetInstructions("Before Exe - I want this to show up"), DispatcherPriority.Send);
But I would recommend async as Mark already did.