In the compose window I have an split button in the ribbon menu. The split button is formed by a button and a menu.
Under some conditions I need to make not visible the menu "myMenu" (hide it) but I want to make the splitButton "MySplitButton" and the button "MyButton" visible. How can I achieve this?
<splitButton id="MySplitButton"
size="large">
<button id="MyButton"/>
<menu id="myMenu" getVisible = "IsVisible">
<toggleButton id="tg1" />
<toggleButton id="tg2" />
</menu>
</splitButton>
You need to implement the getVisible callback for the menu control like you have in your ribbon XML now. The callback should have the following signature:
C#: bool GetVisible(IRibbonControl control)
VBA: Sub GetVisible(control As IRibbonControl, ByRef visible)
C++: HRESULT GetVisible([in] IRibbonControl *pControl, [out, retval] VARIANT_BOOL *pvarfVisible)
Visual Basic: Function GetVisible(control As IRibbonControl) As Boolean
In the callback you need to set the visible parameter to false if you want to hide the menu and true for bringing it back.
Be aware, for each of the callbacks that the add-in implements, the responses are cached. That means if an add-in writer implements the getVisible callback procedure for a menu, the function is called once, the visible state is set, and then if the visible state needs to be updated, the cached value/state 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.
To update the UI you may find the IRibbonUI.Invalidate and IRibbonUI.InvalidateControl methods helpful. An instance of the IRibbonUI interface is passed to the onLoad callback:
C#: void OnLoad(IRibbonUI ribbon)
VBA: Sub OnLoad(ribbon As IRibbonUI)
C++: HRESULT OnLoad([in] IRibbonUI *pRibbon)
Visual Basic: Function OnLoad(ribbon As IRibbonUI)
So, you can save a reference for future use in the add-in for invalidating the state of controls.
Related
Word has several events to hook into, to control application behavior etc.
Some of these events are: Document.BeforeSave and Application.BeforeSave
As far as I know, they do the same thing - they allow you to perform actions before the new document is saved, or even cancel the save event entirely.
But in later versions of Word, there seems to be an event happening BEFORE the BeforeSave events.
It shows a "Save this file" dialog:
Once the user has chosen where to save, and click [Save], THEN is the BeforeSave event executed.
I need a way to intercept this dialog, to prevent the user from saving in certain situations.
While I can do that with the current BeforeSave event, it results in a very bad user experience, since the user first chooses where to save, only to then be told that the time spent doing that was for nothing since the document is not allowed to be saved at this time.
In earlier versions of Word this was not an issue, Word just showed the regular (simple) Save As dialog whenever the user wanted to save the new document.
Coding in VB, I've tried these two ways to handle the BeforeSave Events at Document-level and Application-level. Both fire too late.
(C# code is also welcome, it's easily translated to VB in this context.)
' --- Handle BeforeSave at Document-level
Dim vstoDoc As Document = Globals.Factory.GetVstoObject(Me.Application.ActiveDocument)
AddHandler vstoDoc.BeforeSave, AddressOf clsWord.ThisDocument_BeforeSave
Public Shared Sub ThisDocument_BeforeSave(sender As Object, e As SaveEventArgs)
' Do stuff...
End Sub
' --- Handle BeforeSave at Application-level
Private Sub HandleDocumentBeforeSaveEvent(document As Word.Document, ByRef SaveAsUI As Boolean, ByRef Cancel As Boolean) Handles _wordApplication.DocumentBeforeSave
' Do stuff...
End Sub
How do I intercept the new dialog?
Alternatively, is there another way to prevent the user from saving the document (like setting a document property or something)?
I found a way.
First part is to expand the ribbon markup to include a <commands> node above the <ribbon> element (if any).
<commands>
<command idMso="FileSave" getEnabled="FileSave_GetEnabled"/>
<command idMso="FileSaveAs" getEnabled="FileSaveAs_GetEnabled"/>
</commands>
Then add callback methods to the ribbon code file:
Public Function FileSave_GetEnabled(ribControl As Office.IRibbonControl) As Boolean
' Simple test to toggle enabled/disabled easily
' - requires ribbon invalidation for changes to take effect though
Return My.Computer.Keyboard.CapsLock
End Function
Public Function FileSaveAs_GetEnabled(ribControl As Office.IRibbonControl) As Boolean
Return FileSave_GetEnabled(ribControl)
End Function
Finally you need to handle the BeforeSave event:
Private Sub HandleDocumentBeforeSaveEvent(Doc As Word.Document, ByRef SaveAsUI As Boolean, ByRef Cancel As Boolean) Handles _wdApp.DocumentBeforeSave
If MessageBox.Show("Do you REALLY want to save the document?", "BeforeSave", MessageBoxButtons.YesNo) = DialogResult.No Then
Cancel = True
Exit Sub
End If
End Sub
This allows you to disable the "Save"-icon in the top of the ribbon, Ctrl+S and the "Save" option in the File menu.
It does not disable the "Save as" option in the file menu, but whenever a user tries to save from there, the BeforeSave event is executed, allowing you to take action there.
All in all, a better solution than previously achieved!
Using c++ builder borland (bcb6).
I wish to invoke manually button click event. I did:
FMap->bbDoneInClick(NULL);
While Done button located on the FMap form, but something went wrong with that.
Do I need to call bbDoneInClick with different parameters and not with NULL ?
Instead of NULL use the Form1 or the bbDone itself ...
it depends on the event code itself how it uses the Sender parameter
Also you can call the event handler safely only if the form is already created
if it does not access Canvas you can use it even in TForm1::TForm1 constructor
if it does you need to take care of not using it prior to OnShow or OnActivate
to avoid VCL problems or App crashes
for common handlers it is sufficient to use main window ... (I use this instead of NULL)
if you have single even handler for multiple components then the even is usually deciding the operation or target from the Sender parameter so in that case you need to pass the pointer to component itself
I am a noob to Windows Forms so this is likely a remedial question. I have a child component with a button and a text field. I want to use multiple instances of these in a parent component or form. At runtime, when the user clicks one of the buttons, I want the parent to get the event to decide what to do with the associated text.
Coming from the long lost world of Borland C++ Builder, during design time, I would simply double-click on the buttons and handlers in the parent would be created which I could just elaborate the code. With Windows Forms, the component controls are not clickable (at design time) from the parent and are "frozen". It is not obvious to me how to pass any child button clicks to a parent. I've tried things like changing the button modifier from private to public but that doesn't help. How is this best accomplished.
Note I am using C++ as I am sharing header file definitions with an associated C++ embedded app.
-Bob
UPDATE:
My apologies, I thought I was still in my C# search :(
This is slightly complicated if you actually want to bubble up an event, or very easy if you use methods.
Methods:
In your child container, add a constructor or property that takes in and stores the parent. Then in the button handler, call this.Parent.ButtonClicked(this); and of course in the parent, add a ButtonClicked(ChildType child) method. That should get you there that way.
Custom Event:
To use events, you need to add a few things. Firstly, add a custom EventArgs class as such:
class ChildClickedEventArgs : EventArgs
{
// include a ctor and property to store and retrieve the child instance.
}
Then add a public delegate for it:
public delegate void ChildClickedEventHandler(object sender, ChildClickedEventArgs e);
Then to your child class, add a ButtonClicked event:
public event ChildClickedEventHandler ChildClicked;
And finally, a helper method to raise it:
private OnButtonClicked()
{
if (this.ChildClicked != null)
{
this.ChildClicked(this, new ChildClickedEventArgs(this));
}
}
Then when you add the child class to the parent, add an event handler for each, and you can handle your custom event for your custom control.
Alternatively:
If you can expose the Button in your child class, simply do the above but register it to this.child.Button.Clicked, saving adding the event handler yourself.
In the course of the development of a Resharper plugin, I'd like to show an error message to a user when they incorrectly use a context action. Is there a way to pop up a window in Visual Studio to communicate the Resharper exception message to the user? I'm developing a plugin with Resharper 8 and VS 2012
You can always use MessageBox - ReSharper also provides a MessageBox static class that provides a number of helper methods to make it easy to display what you want. It also allows for adding "message box handlers" so that you don't actually display a message box during testing.
Alternatively, if you're creating a context action, and you're (indirectly) deriving from BulbActionBase, your ExecutePsiTransaction method (which should do all the work) can return an Action<ITextControl>. This allows you to return an action that will execute after the quick fix/context action has completed, which can be anything from positioning the caret, changing the selection, executing a template or showing a tooltip as an error.
You can return something like this:
return tc => myLocks.QueueReadLock("MyContextAction", () => {
myTooltipManager.Show("Something went wrong!",
lifetime => new TextControlPopupWindowContext(lifetime, tc, myLocks, myActionManager);
});
This is using a number of fields: IShellLocks myLocks, ITooltipManager myTooltipManager and IActionManager myActionManager. These can be injected into a component's constructor by ReSharper's component model, or you can get them with solution.GetComponent<IShellLocks>, etc.
What's happening is that you're returning an action that takes in an ITextControl, and which immediately queues up another action to run, on the UI thread, with the read lock taken. This second action tells the tooltip manager to show an error message as a tooltip, and provides a factory method for creating a popup window context (the lifetime parameter is created and disposed by the call to Show, and allows for cleanup of the context).
You could also look at the ShowAtCaret extension method to ITooltipManager - I can't remember offhand where Show will place the tooltip.
I have a dialog in vb6 which changes the values being displayed in its parent dialog.
The x1 is displayed in txt_c1 text in parent dialog and it has a txt_1validate function too for the text box. Now i want to change the value of txt_c1 txtbox from child dialog and then call its validate function. But the problem is that txt_c1 is not available in child dialog.
Please note that i am working in vb6 in the MS VB 6.0 IDE
Forms are just classes and can therefore be instantiated explictly (and you will probably find your life easier if you do rather than using the automatic instantiation in VB6) and references to forms can be assigned.
You can solve your problem by creating a public property on your child dialog (Form1.frm) of type Form that you set to the instance of the parent dialog thus giving you access to the controls andd methods on the parent from the child.
My VB6 is somewhat rusty (and I don't have an installed instance available) so this isn't going to be actual, correct code - but something along the lines of the following should work
In the code that calls the child:
Form childDialog = new Form1
childDialog.Parent = this
childDialog.ShowModal
Then in the child dialog:
Parent.txt_c1 = newValue
if not Parent.Validate then
...
end if