I'm trying to make a context menu for a hyperlink. It seems there are several contexts where hyperlink events can be intercepted -- at the moment I'm interested in the context menu with idMso="ContextMenuReadOnlyMailHyperlink".
There are two different ways the callback for my new button in this menu can be executed -- if hyperlink is right-clicked in the preview pane of Outlook, or if an email is opened for reading in its own window.
When invoked from preview pane, my callback receives an Explorer COM object. I assumed the Explorer's ActiveInlineResponseWordEditor property would contain my selected hyperlink somehow, but it throws a non-helpful COM exception ("The operation failed").
How can I find the selected hyperlink from this context?
Also, how about for the Inspector COM object (when hyperlink is right-clicked from email in its own window)?
I assumed the Explorer's ActiveInlineResponseWordEditor property would contain my selected hyperlink somehow, but it throws a non-helpful COM exception ("The operation failed").
The ActiveInlineResponseWordEditor property cannot be used when the InlineResponse is not activated. The InlineResponse event of the Explorer class is fired when the user performs an action that causes an inline response to appear in the Reading Pane. In your case the inline response is not activated.
How can I find the selected hyperlink from this context?
The Explorer class provides the Selection property which returns a Selection object that contains the item or items that are selected in the explorer window. You can use the Item method (represented by the indexer in C#) to get a Microsoft Outlook item or conversation header from the selection. Then try to cast it to the MailItem class and get the Inspector object, see the GetInspector method of the MailItem class. The Inspector class provides the WordEditor property which returns the Microsoft Word Document Object Model of the message being displayed. You can use the Word object model to get the selection.
I had a similar problem and my solution looked like:
public void OnCustomHyperlinkMenuClick(IRibbonControl control)
{
Explorer explorer = control.Context as Explorer;
if (explorer != null)
{
Document document = explorer.ActiveInlineResponseWordEditor;
//Note from asker: above line throws a COM Exception ("The operation failed")
if (document != null && document.Windows != null && document.Windows.Count > 0)
{
Microsoft.Office.Interop.Word.Selection selection = document.Windows[1].Selection;
if (selection != null && selection.Hyperlinks != null && selection.Hyperlinks.Count > 0)
{
Hyperlink hyperlink = selection.Hyperlinks[1];
string hyperlinkUrl = hyperlink.Address;
DoSomethingWithUrl(hyperlinkUrl);
}
}
}
}
You will need to add a reference to the word interop assembly "Microsoft.Office.Interop.Word.dll" to your project.
Related
I created WinForm component and I want to replace some properties of my component when developer copy and then paste(not when copy but when paste) component from clipboard at design time.
VisualStudio creates new copy of component and assign properties so it became copy of the source component.
I need to replace some properties on paste operation depending on the selected component.
It is very similar to standard Copy/Paste operation with Control component. When designer change Parent of component if developer select other container (like Panel) before Paste Control.
I think that code to perform it should be somewhere in my ComponentDesigner class.
I explored ComponentDesigner methods but can't find any methods that controls clipboard operations.
You can override the OnParentChanged method of your component, which is executed when the component is pasted onto the form. Then test the DesignMode property to make sure you are in design mode:
public class MyComponent : Label
{
protected override void OnParentChanged(EventArgs e)
{
if (DesignMode) {
// Change properties as desired.
Text = "Design";
}
base.OnParentChanged(e);
}
}
When the component is dropped from the Toolbox, this code is not executed. (I can't explain why, but it happens to be exactly what we need.)
If you derived your component from System.ComponentModel.Component, you can override the property Site; however, this will require some more logic to check whether the component has been pasted.
public override ISite Site
{
get {
return base.Site;
}
set {
base.Site = value;
if (value?.Container is IDesignerHost dh &&
dh.TransactionDescription == "Paste components") {
MessageBox.Show("Pasted");
}
}
}
But probably the transaction description is localized, because it is the text that you see in the drop-down of the Undo button on the toolbar of Visual Studio after having pasted the component.
I have a very simple outlook form region. It is configured as a separate item, and, it is set to appear whenever a we try to compose a new appointment item. (Meeting request).
Once I click the button above, it should populate the sender and go back to the main appointment page. The code to do that is:
private void button1_Click(object sender, EventArgs e)
{
item.RequiredAttendees = "John.Doe#contoso.com";
var exp = item.Application.ActiveInspector();
if (exp == null) Debug.Print("NULL");
else exp.ShowFormPage("Appointment");
}
But this doesn't do anything. What is the correct way of doing this?
Use the SetCurrentFormPage method of the Inspector class to display the specified form page or form region in the inspector.
As a workaround you may try to call the Appointment button programmatically. Use the ExecuteMso method of the CommandBars class to execute the control identified by the idMso parameter. See Office 2013 Help Files: Office Fluent User Interface Control Identifiers for the actual idMso values.
I have a CEdit box where a user can enter relevant information. As soon as he\she starts writing in the box, I need a notification so that I can call doSomething() to perform some other task. Does Windows provide a callback, and if so, how do I use it?
With MFC there's no callback as such, rather you do this by implementing a handler for the appropriate event. You need to handle one of two events: WM_CHAR or EN_CHANGE
Handle the dialog's EN_CHANGE for example duplicating in realtime the entered text elsewhere on the dialog. You need to firstly add an entry in the dialog's message map, and secondly override the appropriate handler:
BEGIN_MESSAGE_MAP(CstackmfcDlg, CDialog)
ON_EN_CHANGE(IDC_EDIT1, &CstackmfcDlg::OnEnChangeEdit1)
END_MESSAGE_MAP()
void CstackmfcDlg::OnEnChangeEdit1()
{
CString text;
m_edit.GetWindowText(text);
m_label.SetWindowText(text); // update a label control to match typed text
}
Or, handle the editbox class's WM_CHAR for example preventing input of certain characters, e.g. ignore anything other than a digit for numerical entry. Derive a class from CEdit, handle the WM_CHAR event of that class (not the dialog) and make your edit control an instance of that class.
BEGIN_MESSAGE_MAP(CCtrlEdit, CEdit)
ON_WM_CHAR()
END_MESSAGE_MAP()
void CCtrlEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// Do nothing if not numeric chars entered, otherwise pass to base CEdit class
if ((nChar >= '0' && nChar <= '9') || VK_BACK == nChar)
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
Note that you can use the VS IDE to put in stubs for the handler overrides by using the Properties bar with the mouse selection in the message map block.
EDIT: Added example code, and corrected explanation of WM_CHAR which I had wrong.
If you double click on the edit box in the resource editor it automatically creates the OnEnChanged event for you.
The following assumes that you have an MFC dialog application.
The class wizard can be started with a right-click:
Double-click the Control ID (has an icon with a small green plus) of the new edit control to add the corresponding member variable to the class.
The class and event wizards will update the class definition and add a CEdit member:
afx_msg void OnEnChangeEdit1(); // Added by event wizard
CEdit m_edit1; // member added by class wizard
The class wizard will update the function:
void CMFCApplication5Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT1, m_edit1); // new variable added with class wizard
}
Double-clicking the control or right-clicking and selecting the add event wizard will update the message map and create the function declaration and definition:
BEGIN_MESSAGE_MAP(CMFCApplication5Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_EN_CHANGE(IDC_EDIT1, &CMFCApplication5Dlg::OnEnChangeEdit1) // new event handler added with wizard
END_MESSAGE_MAP()
Finally the code may be updated to interact with the edit control:
void CMFCApplication5Dlg::OnEnChangeEdit1()
{
// TODO: Add your control notification handler code here
CString text;
m_edit1.GetWindowText(text);
//m_edit1.SetWindowText(text);
}
I'm trying to get the parent name of a context menu item.
So I tried something like this on menuItem_click :
Button clikance = (Button)sender;
string ladyGaga = Convert.ToString(clikance.Content);
But it didn't work (invalid cast exception). thx for any help
i have use a different approach for getting the sender button of my context menu. i have made an event on the "hold_click"
where i have get back the content of the button in a public string
private void GestureListener_DoubleTap(object sender, GestureEventArgs e)
{
Button clikance = (Button)sender;
ButtonEnvoyeur = Convert.ToString(clikance.Content);
}
If you look in the debugger at the point where the exception is raised, you'll see that sender isn't a Button, so trying to do an explicit cast to Button will obviously throw an InvalidCastException.
You can use the VisualTreeHelper to walk up the tree from your actual sender to the Button element:
VisualTreeHelper.GetParent((sender as DependencyObject));
UPDATE: In your instance sender is the MenuItem in the ContextMenu. You can get to the parent ContextMenu from the MenuItem by using the VisualTreeHelper, but unfortunately, ContextMenu does not expose any public members that enable you to access the owner; the Owner property is internal. You could get the source code for the Toolkit and expose the Owner property as publi instead, or use a completely different approach.
Have you thought of using an MVVM framework (such as MVVM Light) to wire up commands to these context menu items? Your current approach is very fragile and will break as soon as you change the visual tree. If you used commands, you could pass any additional information that you need for processing via the command parameter.
Use the Tag property of the MenuItem to retrieve your Button :
// Object creation
Button myButtonWithContextMenu = new Button();
ContextMenu contextMenu = new ContextMenu();
MenuItem aMenuItem = new MenuItem
{
Header = "some action",
Tag = myButtonWithContextMenu, // tag contains the button
};
// Events handler
aMenuItem.Click += new RoutedEventHandler(itemClick);
private void itemClick(object sender, RoutedEventArgs e)
{
// Sender is the MenuItem
MenuItem menuItem = sender as MenuItem;
// Retrieve button from tag
Button myButtonWithContextMenu = menuItem.Tag as Button;
(...)
}
Alex.
Using winforms, I have set the KeyPreview property to true and have event handles for the proper key events within the base form as well.
Within the forms that inherit from it, I set the AcceptButton property based on the requirements of the application.
There are certain cases in which I want the enter key to have functionality different than that of the AcceptButton.
I was hoping to capture the enter key press within my base form and check for the special cases where I do not want the AcceptButton event to fire.
It appears though, that the AcceptButton click is fired before any of the key events within my basef form. I could write functionality into the click events of the possible acceptbuttons, but, in my opinion, that would be a hack.
Any suggestions?
Thanks.
Another way to handle this is to override the form's ProcessDialogKey() method where you can suppress the accept and/or cancel buttons. For example, I have an application with a filter editor that filters a grid based on user input. I want the user to be able to hit the return key when the filter editor control has the focus to apply the filter. The problem is the accept button code runs and closes the form. The code below resolves the issue.
protected override bool ProcessDialogKey(Keys keyData)
{
// Suppress the accept button when the filter editor has the focus.
// This doesn't work in the KeyDown or KeyPress events.
if (((keyData & Keys.Return) == Keys.Return) && (filterEditor.ContainsFocus))
return false;
return base.ProcessDialogKey(keyData);
}
You can take this even further by dropping the following code in a base dialog form. Then you can suppress the accept button for controls in subclasses as necessary.
private readonly List<Control> _disableAcceptButtonList = new List<Control>();
protected override bool ProcessDialogKey(Keys keyData)
{
if (((keyData & Keys.Return) == Keys.Return) && (_disableAcceptButtonList.Count > 0))
{
foreach (Control control in _disableAcceptButtonList)
if (control.ContainsFocus)
return false;
}
return base.ProcessDialogKey(keyData);
}
protected virtual void DisableAcceptButtonForControl(Control control)
{
if (!_disableAcceptButtonList.Contains(control))
_disableAcceptButtonList.Add(control);
}
As our workaround, we captured the enter and leave event for the control that we wanted to have override the acceptbutton functionality. Inside the enter event, we held the current accept button in a private variable and set the acceptbutton to null. On leave, we would reassign the acceptbutton back to the private variable we were holding.
The KeyPreview events could have done something similar to the above. If anyone has a more elegant solution, I would still love to know.
Thanks.