none
WPF not updateing bug, called from VSTO/VBA Excel SheetSelectionChange RRS feed

  • Question

  • (Moved from http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/e5306cd0-4c2f-437f-a020-a26571f9a61c.)

    I have a weird bug in WPF/Excel interactions.  (Excel 2010, .Net 3.5)

    I have a VBA addin with a SheetSelectionChange event that COMs into a VSTO addin, passing the formula of the current cell.  The VSTO addin uses WPF in a Task pane that shows the formula in a simple VSTO Label.   Clicking on a different cell in a sheet then updates the WPF in the task pane -- sometimes.

    The first few clicks may not update the WPF label.  Then after it warms up, it works better.  Clicking quickly works better, waiting a few seconds tends to make it fail.  Selecting cells with arrow keys work better.   Same bug under Visual Studio or running stand alone.

    Each COM call always creates a new ViewModel object, which is then just assigned to DataContext (via UpdateUI).  The COM seems to come on the same thread as the WPF controls, but I Invoke just to be sure.  Tracing shows that the event always gets to the the WPF calls, it does not appear to be a COM or VBA problem.  Further, tracing shows the label.Content seems to be being updated properly.  Just cannot see it in the Excel task pane.

    Any ideas how to work around much appreciated.  To really force WPF to update the display.  (My concern, of course, is that in production it may stop working altogether.)  Something like WinForms Invalidate.  I tried adding an InvalidateVisual() but that made it much worse.

    A horrible hack that seems to make the problem mostly go away is to to make all the calls into my VSTO project twice, redundantly.   There are a couple to setup the datastructure.  Just calling the final render one does not do it.  Nor doing it twice in .Net, even with sleeps.  Nor adding delays at the VBA side.  And it does not always work.

    The critical .Net code is called from the COM call from VBA:-

     

            static public void UpdateUI() {
                Trace.WriteLine("UpdateUI " + ViewModel.Current.Formula); // Always good.
                if (true) // Seems not necessary but not harmful.  Loose events either way.
                    TaskPaneUC.Dispatcher.Invoke(new UpdateUIDelegate(DoUpdateUIDelegate), new object[]{ViewModel}); // OK if actually on UI thread.
                    // BeginInvoke same symptoms.
                else
                    DoUpdateUIDelegate(ViewModel);
                Trace.WriteLine("        --> " + TaskPaneUC.FLabel.Content); 
                // Shows updated Content even if screen not updated.  (One behind if BeginInvoke, as expected.)
            }
            public delegate void UpdateUIDelegate(ViewModel vm);
            static public void DoUpdateUIDelegate(ViewModel vm) {
                TaskPaneUC.DataContext = null;
                TaskPaneUC.DataContext = vm;


                TaskPaneUC.FLabel.Content = "xxxx";
                TaskPaneUC.FLabel.Content = ViewModel.Current.Formula; // Unnecessary, and does not help.
            }

        <Grid>
            <Label Content="{Binding Current.Formula}" Height="34" HorizontalAlignment="Left" Margin="12,41,0,0" Name="label2" VerticalAlignment="Top"  Width="261"  />

        </Grid>


    Anthony

    Wednesday, October 12, 2011 2:03 AM

Answers

  • Following

       http://geekswithblogs.net/NewThingsILearned/archive/2008/08/25/refresh--update-wpf-controls.aspx

    I added an Invoke DispatcherPriority.Render which seemed to make the bug go away.  But I leave the thread open as this is all very spooky and I would be interested in proper solutions or explanations.

            static public void DoUpdateUIDelegate(ViewModel vm) {
                //TaskPaneUC.DataContext = null;  -- crashes every other invocation with addtion of user control?
                TaskPaneUC.DataContext = vm;

                TaskPaneUC.FLabel.Content = "xxxx";
                TaskPaneUC.FLabel.Content = ViewModel.Current.Formula; // Does not help.
                Action EmptyDelegate = delegate() { };
                TaskPaneUC.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render,  EmptyDelegate);
            }

     


    Anthony
    • Proposed as answer by Bruce Song Thursday, October 13, 2011 5:46 AM
    • Marked as answer by Anthony Berglas Friday, October 14, 2011 10:04 AM
    Wednesday, October 12, 2011 2:49 AM

All replies

  • Following

       http://geekswithblogs.net/NewThingsILearned/archive/2008/08/25/refresh--update-wpf-controls.aspx

    I added an Invoke DispatcherPriority.Render which seemed to make the bug go away.  But I leave the thread open as this is all very spooky and I would be interested in proper solutions or explanations.

            static public void DoUpdateUIDelegate(ViewModel vm) {
                //TaskPaneUC.DataContext = null;  -- crashes every other invocation with addtion of user control?
                TaskPaneUC.DataContext = vm;

                TaskPaneUC.FLabel.Content = "xxxx";
                TaskPaneUC.FLabel.Content = ViewModel.Current.Formula; // Does not help.
                Action EmptyDelegate = delegate() { };
                TaskPaneUC.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render,  EmptyDelegate);
            }

     


    Anthony
    • Proposed as answer by Bruce Song Thursday, October 13, 2011 5:46 AM
    • Marked as answer by Anthony Berglas Friday, October 14, 2011 10:04 AM
    Wednesday, October 12, 2011 2:49 AM
  • Hi Anthony,

    I think it should be the threading problem. From the MSDN article:

    http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

    I found that Render stands for Operations processed at the same priority as rendering. As a result, you can get the newly updated UI.

    Hope this can give you the hint.

    Best Regards,


    Bruce Song [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.

    Thursday, October 13, 2011 5:46 AM
  • Hello Bruce,

    Thanks for that.  And the article where I got it from says the same thing.

    But what I would really like to know is what went wrong in the first place.   There should only be one thread -- I don't create any.

    Anthony


    Anthony
    Thursday, October 13, 2011 6:29 AM
  • Hi Anthony,

    Could you send me a copy of your sample project? Or show me the reproducible step list? I will do  try to reproduce the scenario on my side and do some research about the problem. You can send to my email v-bpeng@microsoft.com

    I am looking forward to your reply.

    Best Regards,


    Bruce Song [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, October 14, 2011 4:02 AM
  • Hi Anthony,

    I have got your sample project and tested on my side. However, I can't reproduce the scenarion on my side. But if I added the Thread.Sleep(1000);

    to the DoUpdateUIDelegate method, I can reproduce the scenario:

            static public void DoUpdateUIDelegate() {
                //TaskPaneUC.DataContext = null;
                Thread.Sleep(1000);
                TaskPaneUC.DataContext = ViewModel;
            }

    So, I think the when running the code on your computers, some of which will take more time to execute and make the UI unable to update. I also created a simple WPF application which demonstrate the scenario:

     

    using System;
    using System.Windows;
    
    namespace WpfApplication1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
            private static Action EmptyDelegate = delegate() { };
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                for (long i = 0; i < 10000000000; i++)
                {
                    this.textBox1.Text = i.ToString();
                    Dispatcher.Invoke(EmptyDelegate, System.Windows.Threading.DispatcherPriority.Render);
                }
            }
        }
    }
    
    

     

    The Invoke method can make the textBox1 update synchronously, if not adding the Invoke method the textBox1 text will not update. You can refer more details from this article:

    http://msdn.microsoft.com/en-us/library/cc647499.aspx

    Hope this can give you the hint.

    Best Regards,


    Bruce Song [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.


    • Edited by Bruce Song Friday, October 14, 2011 9:41 AM
    Friday, October 14, 2011 9:40 AM
  • Hello Bruce,

    Thanks for confirming that you could reproduce the bug.  But I obviously have concerns in code in which a sleep make a difference.  The Render work around seems to work, at least for now.  I would suggest that you log this as a bug.

    Log the code that I sent you.  Your code confirms what is in the original article that I referenced, but is not actually a bug, as I understand it.

    I think we are going around in circles.  So I will mark this as sooved.  I suggest you log the bug, might help someone else in a future release.

    Thanks,

    Anthony


    Anthony
    Friday, October 14, 2011 10:04 AM