WPF issues with updating textblock related to consolestdout
I am creating a small wpf application in C #. When the button is clicked, the third hand dll function creates a tree type object. This object is linked to a tree structure. This works great, but it takes a little time to load. Since the dll creates an object it prints progress information to the console. I want to redirect this to a TextBlock so that the user receives progress messages.
My ctor window looks like this:
InitializeComponent (); StringRedir s = new StringRedir (ref ProgressTextBlock); Console.SetOut (s); Console.SetError (s); this.DataContext = s;
xaml:
<
TextBlock Text = "{Binding Path = Text}" Width = "244" x: Name = "ProgressTextBlock" TextWrapping = "Wrap" /><
TreeView> ...<
/ TreeView>
The StringRedir class is shown below. The problem is that the TextBlock for some reason is not updated with messages until the TreeView is loaded. Proceeding I can see that the Text property is updating but the TextBlock is not updating. I added a MessageBox.Show () at the point where the text is updated and this seems to cause it to update every time and I can see every message. So I think I need to explicitly refresh the screen somehow ... but that doesn't make sense, I thought that data binding would cause a visual refresh when the property changed. What am I missing here? How do I get it for an update? Any advice is appreciated!
public class StringRedir : StringWriter , INotifyPropertyChanged
{
private string text;
private TextBlock local;
public string Text {
get{ return text;}
set{
text = text + value;
OnPropertyChanged("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public StringRedir(ref TextBlock t)
{
local = t;
Text = "";
}
public override void WriteLine(string x)
{
Text = x +"\n";
//MessageBox.Show("hello");
}
}
source to share
So after some reading in WPF threading model ( http://msdn.microsoft.com/en-us/library/ms741870.aspx ) I finally got it to update by calling Dispatcher Invoke () with dispatch priority set for Render. As Kent suggested, updating the UI in the dispatcher queue was likely a low priority. I ended up doing something like this.
XAML
<TextBox VerticalScrollBarVisibility="Auto"
Text="{Binding Path=Text, NotifyOnTargetUpdated=True}"
x:Name="test" TextWrapping="Wrap" AcceptsReturn="True"
TargetUpdated="test_TargetUpdated"/>
Updated C # handler code
private void test_TargetUpdated (object sender, DataTransferEventArgs e) { TextBox t = sender as TextBox; t.ScrollToEnd (); t.Dispatcher.Invoke (new EmptyDelegate (() => {}), System.Windows.Threading.DispatcherPriority.Render); }
Note. I used to use TextBlock, but I changed it to TextBox as it has scrolling
However, I still feel uneasy about the whole stream. Is there a better way to do this? Thanks to Matt and Kent for their comments. If I had points, they would mark their answers as helpful.
source to share
I believe the problem is with the constructor of your StringRedir class. You pass into the ProgessTextBlock and you do this with it:
local.Text = "";
This actually overwrites the previously set value for ProgressTextBlock.Text, which was the following:
{Binding Text}
See what I mean? By explicitly setting a value for the TextBlock Text property, you unbind.
If I'm reading correctly, it looks like the idea of ββpassing a TextBlock to a StringRedir ctor is a hangover before you try to bind it directly. I would rip it up and stick with the idea of ββbinding as it is more in the "spirit" of WPF.
source to share