I am working on a Office Ribbon project. (PowerPoint)
A label on it is being periodically updated from a timer. (It is displaying the current number of connections to our server)
When the PowerPoint window is in focus, the label is updated correctly. However, when the window is inactive, the updates are suspended. (they appear only after the PowerPoint window receives focus)
This is an issue for multi-monitor setups, or when snapping PowerPoint to a half of the screen - we need correct data to be displayed.
I understand that the idea behind this is to lower the CPU load, but I would like to override the behavior.
I tried the Invalidate, PerformLayout and similar methods, but can't get the ribbon to refresh. Is there any way to do this?
Nope. There is no way to update the Fluent UI in all windows. The callbacks are invoked when the window becomes active.
You may consider creating Custom Task Panes for such needs. In that case you will be able to control each instance separately and update the UI at runtime. You may find the following articles helpful:
Walkthrough: Automating an Application from a Custom Task Pane
Managing Custom Task Panes in Multiple Application Windows
Related
There are existing posts on SO about related topics, and there are support requests on MSDN that are unsolved/unrepaired. It sounds like the "right way" doesn't work, so I'm wondering there is a workaround.
Let me describe what I perceive to be "the right way" that doesn't work, and maybe you have an alternative path that will work.
I can create an Excel VSTO plugin in Visual Studio, create a custom Ribbon.xml and Ribbon.cs file, and then open multiple workbooks which appear as different windows. Per other SO posts, I can hook into the Application_WindowActivate event and call Invalidate() on my ribbon, forcing a refresh of all the callbacks. The ribbon will reflect the state of the Active Window... and so will every other instance of the ribbon in other windows. As a result, the Ribbon that appears in every window is the same Ribbon. If I have a CheckBox or a Button with a Pressed attribute or anything else stateful, those states will propagate to every open Excel window.
This can be avoided by running Excel with the excel /s parameter which appears to create a new process with its own SDI - but this is not a user-friendly solution.
It looks like what you're supposed to be able to do during an onAction ribbon callback is access the IRibbonControl parameter's Context property, which is supposed to be the correct Excel.Window object. In theory this would present either only the Window you are activating, or would execute for each open Window. And with the correct Context you could execute functions that provide different states back to the Ribbon. And then maybe in one Excel window, you ribbon could have a Pressed Button, and in the other window, an unpressed Button. Except it doesn't work.
When I call Invalidate I get two callbacks (not sure why, the MSDN says it might be a bug) both for the newly activated window, and I execute logic to update the state of active window's ribbon. And then the previous window's ribbon updates the same way.
If this is the way it is, fine. We'll tell our users that they may see some funky Ribbon changes on inactive windows but that it will always be correct on the active window. We can deal with that. But if there is a way around this problem that could make it appear as though the Ribbon had different states on a per-workbook/window basis... that'd be really cool.
You are on the right avenue. Only the active window gets callbacks invoked and its values refreshed. You must switch to another Excel window if you want your ribbon UI invalidated. For each of the callbacks the add-in implements, the responses are cached. For example, if an add-in writer implements the getImage callback procedure for a button, the function is called once, the image loads, and then if the image needs to be updated, the cached image is used instead of recalling the procedure. This process remains in-place until the add-in signals that the cached values are invalid by using the Invalidate method, at which time, the callback procedure is again called and the return response is cached. The add-in can then force an immediate update of the UI by calling the Refresh method.
You may also consider caching UI settings in custom properties. And as soon as a custom property is changed you may trigger UI updates.
Read more about the Fluent UI in the following series of articles:
Customizing the 2007 Office Fluent Ribbon for Developers (Part 1 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 2 of 3)
Customizing the 2007 Office Fluent Ribbon for Developers (Part 3 of 3)
Also, you may find the folliwing links helpful:
Chapter 11: Creating Dynamic Ribbon Customizations (1 of 2)
Chapter 11: Creating Dynamic Ribbon Customizations (2 of 2)
Working on a WPF application that integrates with the Lync Client, but does not replace the Lync Client or use UISupression.
When a call comes in the user can answer the call via our application using Click or Function key. The problem is that after answering the call Lync generates a conversation window which steals focus away from our application. This is a problem because any function keys the user might press (to put the call on hold for example) will go to the Lync Conversation window, not to this application.
I've seen this Microsoft article that allows you to get conversation windows and dock them, which seems like a viable solution, except that our application really doesn't have room to host those windows.
What can we do to prevent or work around this problem where the Lync Client Conversation windows steal focus from our application?
I'd say docking is definitely the way to go. You could create a form as part of your application, but separate from your main application UI, to dock the conversation window into. You could give this a single pixel border, or hide the border totally, so the Lync conversation window won't look as if it's docked - the user shouldn't notice any difference between the docked and non-docked window. The benefit to this approach being, you don't need to dedicate any of your main application's UI real estate to hosting a conversation window.
One thing to be aware of - if you're using Lync 2013, a potential problem with this approach is that docking a conversation window pulls it out of the tabbed conversation view - but this might not be a problem if you're only dealing with Audio calls as these get pulled out of the tabbed conversation view by default.
It would seem you are unable to do this according to MSDN
http://social.msdn.microsoft.com/Forums/lync/en-US/06db0465-6a86-4afb-8f0a-a5299d92a349/how-to-suppress-opening-the-lync-conversation-window?forum=communicatorsdk
Without using the UI Suppression Command
Given that the conversation window is a problem: "Lync generates a conversation window which steals focus " - consider creating your own conversation window, as described here:
Building Lync IM Conversation Windows
This is a bit of a hack, but you could just move the ConversationWindow outside the boundaries of the screen.
Assuming multiple monitors,
Screen[] screenArray;
screenArray = Screen.AllScreens;
cw = LyncClient.GetAutomation().GetConversationWindow(lyncConversation);
cw.Move((screenArray[0].Bounds.Left - cw.Width), (screenArray[0].Bounds.Top - cw.Height));
For the focus problem, don't initialize the application until the movement above is done and you should be fine.
I have a WPF Application using MVVM pattern. Startup window consist of 3 controls Menu,TitleBar and a DataGrid. Currently it takes around 5 seconds to complete all the operations( fetching data from service, dynamically generate DataGrid and its rendering) and after that it displays all of a sudden to UI. Problem is, the end user has to wait for 5 seconds to see the window after starting the program. Most of the operations is related to DataGrid. So I moved the DataGrid related code to Window Loaded Event and now the window open suddenly but showing a black screen and after some time it shows the DataGrid with data.
So my aim is to show the window with Menu and TitleBar and after completing the initial load, do the task to load the DataGrid in a background thread so that I can show a loading panel in the view. How can I call that particular method that related to DataGrid after completing the initial load?
Also, I can't use Splash screen(it's in the requirement).
Please suggest?
You should perform any long running process in a background Thread... this will free up your UI. If you are not familiar with multi-threaded applications, take a look at the BackgroundWorker Class page at MSDN, as it features code examples.
Another thing to note it that you should add IsAsync="True" property to your Binding declaration on the DataGrid.ItemsSource to let it know that it must wait for data. In this way, your DataGrid will be displayed empty and then when the data is ready, it will populate.
I am developing an Office add-in. Due to some limitations of ribbon controls (e.g. menuSeparator has no visibility control or splitButton cannot host a dynamicMenu only a menu), I need to be able to cause the Office app to reload the ribbon by repeating a call to my add-in's IRibbonExtensibility.GetCustomUI.
Is this possible?
As far as I know ribbon add-ins, this would require to stop and then restart your add-in. There must be a way to do so given that one can access the list of add-ins in outlook. you could run another instance of your add-in which would first close the previous one, then return the updated ribbon XML. But this means that it would not be applicable while the user is clicking on the ribbon's components, and you would have to save all your data somewhere and then read it to restore the add-in status. In addition, the user may see the ribbon disapearing and appearing again, which may not be appreciated.
Would the Ribbon.Invalidate() method work for your use case? I frequently use it to refresh the ribbon when I've dynamically added/removed items.
For example, in the Ribbon c# file (Ribbon1.cs by default):
this.ribbon.Invalidate();
When the ribbon needs to be refreshed. This assumes you've set this.ribbon in the Ribbon_Load method.
I have a WinForms app that is retrieving data from a web service on a worker thread and I need to disable all input to my app until the data is loaded.
Currently, I create a semi-transparent form and place it over my application. When the data call completes I close this overlay form. This works fine accept that it causes considerable performance problems for users running the application over terminal services. I tried making the overlay entirely transparent but that still triggers two redraws of the entire window so this did not help at all.
I know that a common recommendation for handling this is to disable all the controls, but that would also redraw much of the screen so I'm looking for another way to block all user input. Any assistance would be greatly appreciated!
UPDATE: I should have mentioned that we have considered the modal dialog. Currently we show the overlay, start the data access thread then construct the form. If there is no better way to block input (App.BlockInput() might be nice) then we could use the modal dialog idea, but we would need to wait until the form construction had completed and there isn't currently a nice, central location to do this.
You could display a small modal (modalForm.ShowDialog(yourForm)) form with progress bar rolling on top of your app. This won't cause big areas to be redrawed.
If your app really is blocked while the operation is running, I'd do what Microsoft frequently does: open a modal dialog box with some kind of throbber animation or ProgressBar, and a Cancel button. Redraw is limited because you're only drawing the size of the new dialog, and input to the rest of your application is blocked because the dialog is modal. Also, users are much more willing to wait when you have some kind of status updates and or animation, because it looks like the computer is "working".
However, if there are operations your user can do while your web service request is running, it's better to leave the controls accessible. At very least, there should always be a way to interrupt/abort the process.
Update: Since you now changed the question: How long is it taking to construct the modal dialog? Why not simply construct the dialog empty, and then populate its controls? If all you have is a small dialog box with a single button and a single ProgressBar, then calling dialog.ShowDialog() should happen faster than your user can interact with your UI. Is that not the case?
One thing you could try for keyboard input is setting the KeyPreview property of the form to True. This will pass all keyboard events to the Form object first instead of to the individual controls. Create an event handler for the KeyPress event of the form and in there you can set the Handled property of the KeyPressEventArgs to True to prevent the key stroke from being passed to any of the controls. If you're currently retrieving data from the web service, set the Handled property True otherwise set it to False and the key stroke will be passed to the controls.
If someone has a good idea on how to handle the mouse input yet you're set.
I'd typically create a LockUI() and UnlockUI() functions in my form that toggle controls and flip a local form field that acts a flag to indicate a long running process. This approach works really well if you use a command pattern.
As previously mentioned, you could toggle keyboard input by using the KeyPreview property of the form (as suggested by TLiebe).
As far as mouse input is concerned, you could disable mouse activity by hooking the WinProc messages and intercepting mouse input messages. This is basically what KeyPreview does.