none
Cannot set Text in a TextBlock?

    Question

  • I am having the most bizarre problem - I cannot change the contents of a TextBlock. This is only 1 place in my program; in other places I have no issue. Just a simple command like

    MyTextBlock.Text = "Hello there";
    
    
    does nothing. I tried first populating its contents (or not), changing alignment, font weight - nothing. Must be something obvious I am missing here - hopefully someone can shed some light. Thanks much.

    Wednesday, April 13, 2011 8:44 PM

Answers

  • Your sleep call is pausing the same thread that the layout is on, so its never going to display the content changes.  Also, the content changes wont be rendered till after the routine is finished.  The way you can accomplish what you want is:

    1) Change button content to "Sending"

    2) Create a BackgroundWorker to do the work of creating an email and sending it via SMTP

    3) Run the worker and At the end of the work you need to Invoke a change on the main thread to change the content to "Sent!".  It's important you invoke the change for this from the other thread so that it will render immediately.  You could then sleep 1 second so the user has time to see it.

     

    • Marked as answer by cgtyoder Monday, April 18, 2011 7:09 PM
    Friday, April 15, 2011 6:30 PM
  • Something similar to this:

     

    this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() { this.QuitButton.Content = "Sent!"; });
    

     

    You need to reference the Dispatcher of the main thread.  Then invoke is a way to call a delegate function from another thread. 

    Just a FYI, Invoke is a synchronous call from another thread, and that thread will not continue untill the method you invoke finishes.  I think that is what you want for this case.  However, BeginInvoke is an asynchronous call that will not wait for the method to finish.


    • Marked as answer by cgtyoder Monday, April 18, 2011 7:09 PM
    Friday, April 15, 2011 9:09 PM

All replies

  • Have you considered that the property is being set again by another method after you execute your code?  Is the control's DataContext changing?  Is it bound to another property?

    Evan

    Wednesday, April 13, 2011 8:59 PM
  • I just added the control to a XAML file, and added the 1 line of code to change the Text, so there's nowhere else in the code that modifies the control. I put a breakpoint on that line of code, step over it and nothing changes. Its DataContext never changes. It is not part of any BindingGroup, bound to any property.
    Wednesday, April 13, 2011 9:10 PM
  • I just added the control to a XAML file, and added the 1 line of code to change the Text, so there's nowhere else in the code that modifies the control. I put a breakpoint on that line of code, step over it and nothing changes. Its DataContext never changes. It is not part of any BindingGroup, bound to any property.

    Can you post your XAML too?
    http://blog.voidnish.com
    Wednesday, April 13, 2011 9:22 PM
    Moderator
  • <Window x:Class="CWS.Chameleon.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Chameleon" Height="384" Width="614" ResizeMode="CanMinimize" IsTabStop="False" WindowStartupLocation="CenterScreen">
      <StackPanel>
        <Frame x:Name="MainFrame" VerticalAlignment="Top" HorizontalAlignment="Left" IsTabStop="False" Width="610" Height="288" NavigationUIVisibility="Hidden" Focusable="False" />
        <Canvas Height="56" Width="593" HorizontalAlignment="Left">
          <TextBlock Canvas.Left="219" Canvas.Top="26" Name="EmailStatusTextBlock" Text="XX" TextAlignment="Center" FontWeight="Bold" />
          <Image Canvas.Left="6" Canvas.Top="9" Height="47" Name="image1" Stretch="Uniform" Width="179" Source="/Chameleon;component/logo.tif" />
          <Button Canvas.Left="320" Canvas.Top="22" Content="Quit" Height="23" Name="QuitButton" Width="75" Click="Click_QuitButton" />
          <Button Canvas.Left="435" Canvas.Top="22" Content="&lt; Back" Height="23" Name="BackButton" Width="75" Click="Click_BackButton" IsEnabled="{Binding Path=CanBrowseBack}" />
          <Button Canvas.Left="516" Canvas.Top="22" Content="Next &gt;" Height="23" Name="NextButton" Width="75" Click="Click_NextButton" IsDefault="{Binding UpdateSourceTrigger=LostFocus}" IsEnabled="{Binding Path=CanBrowseNext}" />
        </Canvas>
      </StackPanel>
    </Window>
    
    

    I did another test and noticed that at the point in the code where I am trying to change EmailStatusTextBlock.Text, I also cannot change QuitButton.Content, where I could at a previous point in the code.
    Wednesday, April 13, 2011 9:27 PM
  • Okay, so where exactly are you setting the text? In which method?
    http://blog.voidnish.com
    Wednesday, April 13, 2011 10:32 PM
    Moderator
  • This is a small NavigationServices app - at this point, the user has done one navigation (which happens in MainFrame), and the user has clicked NextButton. I am attempting to change the Text in Click_NextButton().

     

    Thursday, April 14, 2011 12:28 PM
  • I changed the TextBlock to a TextBox and still the same problem. It's like I can't modify anything in that Canvas.
    Thursday, April 14, 2011 2:46 PM
  • Hi,

     

    I tried one sample with the xaml you gave and tried to change text of textblock on next button event, I am able to change the Text....i didn't faced any issues with your XAML.


    Vipul Mistry Embedded Engineer eInfochips
    Thursday, April 14, 2011 4:17 PM
  • Hi Cgtyoder,

    Based on your description I think it's most likely the TextBlock EmailStatusTextBlock or QuitButton you can access is not ones you see on the screen. With the current information you have provided, it's unclear what could probably cause this. Have you manupilated these controls in the code behind that could possibly replace these controls?

    Could you please provide more information/code about this problem? If we use your xaml code above and change the textblock's text in on button_click, it would certainly work as expected. We need to know how to make it NOT work. It will help us resolve this problem.

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, April 15, 2011 2:41 AM
    Moderator
  • > Based on your description I think it's most likely the TextBlock EmailStatusTextBlock
    >or QuitButton you can access is not ones you see on the screen.

    Well, I am setting the Content from the same method at two different points in time - the first time it works, the second not, and there is no changes in reference, so I am pretty sure they are the same objects. There is only 1 object named EmailStatusTextBlock and 1 object named QuitButton in the entire solution.

    Another data point: when I try to change the Content earlier in the code where it is "working;" if I call

    System.Threading.Thread.Sleep(1000);

    immediately after setting the Content, the button name does not change on screen, until after the Sleep is over. This is basically what I am doing in the problem area. This is the pseudocode:

    private void Click_NextButton(object sender, RoutedEventArgs e)
    {
      if ((string)NextButton.Content == "Send")
      {
        this.QuitButton.Content = "Sending";
        <create email message; send it via local SMTP server>
        QuitButton.Content = "Sent!";
        System.Threading.Thread.Sleep(1000);
        this.Close();
      }
    
      <other code>
    }
    

    Is there a way to force the GUI to refresh? Is calling Sleep() somehow keeping the screen from updating?

    Friday, April 15, 2011 2:00 PM
  • Your changing the content for a button on a window, and immediately closing the window all in the same method and expecting to see the content change? 
    Friday, April 15, 2011 5:48 PM
  • The sending of the email takes about 2-3 seconds. And I am pausing the app for 1 second. It is hardly "immediate."
    Friday, April 15, 2011 5:51 PM
  • Your sleep call is pausing the same thread that the layout is on, so its never going to display the content changes.  Also, the content changes wont be rendered till after the routine is finished.  The way you can accomplish what you want is:

    1) Change button content to "Sending"

    2) Create a BackgroundWorker to do the work of creating an email and sending it via SMTP

    3) Run the worker and At the end of the work you need to Invoke a change on the main thread to change the content to "Sent!".  It's important you invoke the change for this from the other thread so that it will render immediately.  You could then sleep 1 second so the user has time to see it.

     

    • Marked as answer by cgtyoder Monday, April 18, 2011 7:09 PM
    Friday, April 15, 2011 6:30 PM
  • KP_SES, thanks much for the answer - that got me 70% of the way there. However, I am still having trouble figuring out how to change the content to "Sent!". I obviously can't set it in the RunWorkerCompleted handler, and also Sleep() and exit the program. What's the best way to get a hook to the main thread to set this last bit of content?
    Friday, April 15, 2011 8:24 PM
  • Something similar to this:

     

    this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() { this.QuitButton.Content = "Sent!"; });
    

     

    You need to reference the Dispatcher of the main thread.  Then invoke is a way to call a delegate function from another thread. 

    Just a FYI, Invoke is a synchronous call from another thread, and that thread will not continue untill the method you invoke finishes.  I think that is what you want for this case.  However, BeginInvoke is an asynchronous call that will not wait for the method to finish.


    • Marked as answer by cgtyoder Monday, April 18, 2011 7:09 PM
    Friday, April 15, 2011 9:09 PM
  • Ok, thanks for that - I didn't think of inlining code in the Invoke method. I will try it out in the office on Monday. btw, wouldn't a a DispatcherPriority of Render be more appropriate? I don't know if the difference may be negligible or none in practice.
    Saturday, April 16, 2011 4:55 PM
  • Hi Cgtyoder,

    Try the following code.

          QuitButton.Content = "sending";
          Dispatcher.Invoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input);
          Thread.Sleep(2000);
          QuitButton.Content = "sent";
          Dispatcher.Invoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input);
          Thread.Sleep(1000);
    

    Your code is running in the UI thread, which means Thread.Sleep will freeze the UI so you cannot see the changes. A call to Dispatcher.Invoke with Input priority ensures tasks with a higher priority such as rendering could take place before the UI is frozen.

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, April 18, 2011 3:52 AM
    Moderator
  • Since your in another thread, and the main thread is basically free, the priority shouldn't matter much.  Render is certainly appropriate, but I don't think it will make any difference.  Now if you are going to do it all on the same thread then the priority is important, like Min shows above.
    Monday, April 18, 2011 11:16 AM

  • this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() { this.QuitButton.Content = "Sent!"; });
    

    This is working only intermittently - I'm not sure why. (Depends on what other code I have in the RunWorkerCompleted handler; changing the DispatcherPriority may or may not help.) If I use code from Min Zhu with DispatcherPriority.Input as shown, then it works perfectly. I suppose I'll use that, although it would be interesting to understand why your solution isn't working for me.

     

    [Update]
    I lied - it only worked once. I can't get it working consistently. Very strange.


    • Edited by cgtyoder Monday, April 18, 2011 2:04 PM new info
    Monday, April 18, 2011 1:57 PM
  • Hard to tell unless you posted the code for the Routine that sends the email (the second thread), and routine that calls the Backgroundworker.RunWorkerAsyc. 

     

     


    Monday, April 18, 2011 2:21 PM
  • Here's the relevant code:

    private void BackgroundEmail_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      EmailStatusTextBlock.Text = "Sent!";
      EmailStatusTextBlock.Foreground = Brushes.Green;
      NextButton.Content = "Sent!";
      Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Input);
      System.Threading.Thread.Sleep(2000);
      this.Close();
    }
    
    private void Click_NextButton(object sender, RoutedEventArgs e)
    {
      if ((string)NextButton.Content == "Send")
      {
        EmailStatusTextBlock.Foreground = Brushes.Red;
        EmailStatusTextBlock.Text = "Sending...";
        NextButton.Content = "Sending";
        NextButton.IsEnabled = false;
        BackButton.IsEnabled = false;
        QuitButton.IsEnabled = false;
    
        BkgdXferStruct bgxs;
        bgxs.emailToList = ((MyPage)MainFrame.NavigationService.Content).EmailToList;
        bgxs.bodyText = ((MyPage)MainFrame.NavigationService.Content).EmailText;
    
        backgroundEmail.RunWorkerAsync(bgxs);
        return;
      }
    
      <other code>
    }
    
    

    The text changes in Click_NextButton() always happen; only "sometimes" are the text changes in the handler seen on-screen.

    Monday, April 18, 2011 3:10 PM
  • I don't see the code for sending the email.  In your code that is sending the email are you calling the code below at the end?

     

    this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate() { this.QuitButton.Content = "Sent!"; });
    Monday, April 18, 2011 3:42 PM
  • I did not have any text-setting code in the emailing method - it is all in the BackgroundEmail_RunWorkerCompleted() method.

    I tried moving it from BE_RWC to the end of the email sending method, but that did not make a difference.

    Monday, April 18, 2011 4:16 PM
  • Here is a very basic sample:

     

    <Window x:Class="MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="MainWindow" Height="350" Width="525">
      <Grid>
        <Button Name="button" IsEnabled="True"></Button>
      </Grid>
    </Window>
    

     

    using Microsoft.VisualBasic;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    using System.Windows.Threading;
    using System.ComponentModel;
    
    class MainWindow
    {
    
    	public MainWindow()
    	{
    		// This call is required by the designer.
    		InitializeComponent();
    
    		// Add any initialization after the InitializeComponent() call.
    
    		BackgroundWorker BGW = new BackgroundWorker();
    		BGW.DoWork += Test;
    		BGW.RunWorkerAsync();
    	}
    
    	public void Test()
    	{
    		for (int i = 0; i <= 10; i++) {
    			this.Dispatcher.Invoke(DispatcherPriority.Normal, s => button.Content == (object[])s[0], new object[] { i.ToString() });
    			System.Threading.Thread.Sleep(500);
    		}
    	}
    }
    
    

    Monday, April 18, 2011 5:18 PM
  • Ok, that works - I didn't realize I have to call Invoke() from the DoWork handler (and Sleep() there too). Why is it that it won't work from the RunWorkerCompleted() handler?
    Monday, April 18, 2011 6:12 PM
  • The problem you faced is trying to update the UI, which is on the main thread, in a method that is also on the main thread.  By using the Background worker, you keep your UI active and do the work on another thread.  The RunWorkerCompleted routine is AFTER the work is completed (background thread ended), so your back on the main thread.  So basically, putting the UI update in the RunWorkerCompleted was no different than your original problem.  When you update the UI in the Background thread, the UI is free and will show updates. 

     

     

     

    Monday, April 18, 2011 6:21 PM