In my Ribbon.xml is given:
<command idMso="FileSave" onAction="SaveIntercept" getEnabled="IsSaveInterceptEnabled" />
in my Ribbon.cs
public void SaveIntercept(IRibbonControl control, ref bool cancelDefault)
{
if (!IsFileSavedBefore()) <= works :)
{
call here build in FileSave
(Build in function switch tab when file was never saved before)
return;
}
... File was saved before ... continue with my code
}
I already try
RibbonUi.ActivateTabMso("TabSave");
and
Application.CommandBars.ExecuteMso("FileSave");
How call origial functionality?
Maybe this could work?
public void SaveIntercept(IRibbonControl control, ref bool cancelDefault)
{
if (!IsFileSavedBefore())
{
cancelDefault = false;
return;
}
...
Related
I have created a simple custom editor tool, which allows me to keep mouse position in a straight line. I require this to draw texture on a terrain in a straight line. Unfortunately, when I enable "Paint texture" tool in the terrain editor in inspector, my custom tool gets disabled and vice-versa. How can I keep both my custom tool and terrain paint tool enabled at once?
Custom tool selected but paint texture is deactivated-
Custom tool got deselected on paint texture selection-
Following is the OnToolGUI method
public override void OnToolGUI(EditorWindow window)
{
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
Event e = Event.current;
if (!(window is SceneView))
return;
if (!ToolManager.IsActiveTool(this))
return;
if (e.shift)
{
if (e.type == EventType.MouseDown)
{
if (e.button == 0)
{
downY = e.mousePosition.y;
}
}
if (e.type == EventType.MouseDrag)
{
if (e.button == 0)
{
e.mousePosition = new Vector2(e.mousePosition.x, downY);
Debug.Log("Mouse Position: " + e.mousePosition);
}
}
}
As mentioned in the comments I guess it simply is the nature of the tools that they are exclusive and you can only have one active at a time.
As alternative I would rather simply
enable/disable this via a general header menu entry
(optionally) store that decision persistent in EditorPrefs (pretty much like PlayerPrefs but for the editor itself)
and accordingly attach a listener to SceneView.duringSceneGui
This could look somewhat like e.g.
public static class StraightLineTool
{
// Used for the displayed menu labels
// and also simply (ab)used as the unique key for the EditorPrefs
private const string k_MenuName = "My Tools/Straight Line Tool";
private static float downY;
// Property for simplifying access and setting more centralized
private static bool IsEnabled
{
get => EditorPrefs.GetBool(k_MenuName, false);
set => EditorPrefs.SetBool(k_MenuName, value);
}
// method to be called when clicking the menu button
[MenuItem(k_MenuName)]
private static void ToggleEnabled()
{
IsEnabled = !IsEnabled;
ApplySettings();
}
// adding a checkmark when is enabled and simply always allow to click it
[MenuItem(k_MenuName, true)]
private static bool ToggleEnabledValidate()
{
Menu.SetChecked(k_MenuName, IsEnabled);
return true;
}
// Called on every project loading or code recompilation
[InitializeOnLoadMethod]
private static void Initialize()
{
EditorApplication.delayCall -= ApplySettings;
EditorApplication.delayCall += ApplySettings;
}
private static void ApplySettings()
{
// remove so only happening once
EditorApplication.delayCall -= ApplySettings;
SceneView.duringSceneGui -= OnSceneGUI;
if (IsEnabled)
{
// if enabled start listening
SceneView.duringSceneGui += OnSceneGUI;
}
}
// Callback listening to any SceneView.duringSceneGui
private static void OnSceneGUI(SceneView sceneView)
{
// Not sure tbh what this does or if you need it still in this approach
// HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
var currentEvent = Event.current;
if (currentEvent.shift)
{
if (currentEvent.type == EventType.MouseDown)
{
if (currentEvent.button == 0)
{
downY = currentEvent.mousePosition.y;
}
}
if (currentEvent.type == EventType.MouseDrag)
{
if (currentEvent.button == 0)
{
currentEvent.mousePosition = new Vector2(currentEvent.mousePosition.x, downY);
Debug.Log("Mouse Position: " + currentEvent.mousePosition);
}
}
}
}
}
=> using this the tool will be enabled/disabled persistent even when restarting Unity
Then using this as start point you can probably still try to somehow integrate this somewhere more nicely into the SceneView menus - but maybe that is also overkill ;)
I'm developing a BHO in CSHARP and I have an issue in onDocumentComplete method.
It runs on every IFRAME that the main document loads. How can I avoid it? I only want to handle events in the main window.
public void OnDocumentComplete(object pDisp, ref object URL)
{
document = (HTMLDocument)webBrowser.Document;
document.body.style.backgroundColor = "red";
}
Compare this.site with pDisp. If they're equal, then the code is running in the main frame:
public void OnDocumentComplete(object pDisp, ref object URL)
{
if (pDisp != this.site) {
// Ignore subframes
return;
}
document = (HTMLDocument)webBrowser.Document;
document.body.style.backgroundColor = "red";
}
I have an applications that install two other application which has a "Help" option. Each of these application has a common help file but the contents should be displayed based on the index selected for the application in the "Table of Contents". If i open one application, the help of that particular application should be displayed.
My code looks like this for Appl1.
private void Help_Click(Core.CommandBarButton Ctrl, ref bool CancelDefault)
{
if (System.IO.File.Exists(new PlugInConstants().HELP_FILE_Path))
{
System.Windows.Forms.Help.ShowHelp(new System.Windows.Forms.Control(),
new PlugInConstants().HELP_FILE_Path,
System.Windows.Forms.HelpNavigator.TableOfContents, "Appl1");
}
else
{
System.Windows.Forms.MessageBox.Show(m_objLanguage.ERR_HELP_NOT_FOUND.Replace
("%1", m_objGlobalConfig.HelpFilename));
}
CancelDefault = false;
}
and looks like this for Appl2
private void HelpToolStripMenuItem_Click(object sender, EventArgs e)
{
helpToolStripMenuItem.Enabled = false;
string helpFilePath;
helpFilePath = new TrayConstants().HELP_FILE_Path;
if (System.IO.File.Exists(helpFilePath))
{
System.Windows.Forms.Help.ShowHelp(new System.Windows.Forms.Control(),
helpFilePath, System.Windows.Forms.HelpNavigator.TableOfContents, "Appl2") ;
}
else
{
if (m_helpPage == null)
m_helpPage = new HelpPage();
m_helpPage.ShowDialog();
}
helpToolStripMenuItem.Enabled = true;
}
from this i can only see the Content page of common helpfile, but not the particular application help that is selected.
Now i did run Appl1 but still i can see the main MyAppbut not Appl1that is automatically selected and the contents that is displayed to right.
I am using VS 2010,C#, win forms
thanks in advance
I believe that your issue is that you are accessing the wrong value in the HelpNavigator enum. Looks like it should be Topic, not TableOfContents.
System.Windows.Forms.Help.ShowHelp(new System.Windows.Forms.Control(),
helpFilePath, System.Windows.Forms.HelpNavigator.Topic, "Appl2") ;
http://msdn.microsoft.com/en-us/library/system.windows.forms.helpnavigator.aspx
Another hi all,
I am doing Excel automation via Interop in C#, and I want to be informed when a workbook is closed. However, there is no Close event on the workbook nor a Quit event on the application.
Has anybody done that before? How can I write a piece of code which reacts to the workbook being closed (which is only executed if the workbook is really closed)? Ideally that should happen after closing the workbook, so I can rely on the file to reflect all changes.
Details about what I found so far:
There is a BeforeClose() event, but if there are unsaved changes this event is raised before the user being asked whether to save them, so at the moment I can process the event, I don't have the final file and I cannot release the COM objects, both things that I need to have/do. I do not even know whether the workbook will actually be closed, since the user might choose to abort closing.
Then there is a BeforeSave() event. So, if the user chooses "Yes" to save unsaved changes, then BeforeSave() is executed after BeforeClose(). However, if the user chooses to "Abort", then hits "file->save", the exact same order of events is executed. Further, if the user chooses "No", the BeforeSave() isn't executed at all. The same holds as long as the user doesn't click any of these options.
I've created a hack using a polling-like approach, and it works:
Given the workbook to observe, I create a thread which periodically tries to find that workbook in the workbooks collection.
(The DisposableCom class is my current solution to properly cleanup COM objects.)
Excel.Application app = wbWorkbook.Application;
string sWorkbookName = wbWorkbook.Name;
Thread overseeWorkbooksThread = new Thread(new ThreadStart(
delegate()
{
bool bOpened = false;
Excel.Workbooks wbsWorkbooks = app.Workbooks;
using (new DisposableCom<Excel.Workbooks>(wbsWorkbooks))
{
while (true)
{
Thread.Sleep(1000);
if (wbsWorkbooks.ContainsWorkbookProperly(sWorkbookName))
bOpened = true;
else
if (bOpened)
// Workbook was open, so it has been closed.
break;
else
{
// Workbook simply not finished opening, do nothing
}
}
// Workbook closed
RunTheCodeToBeRunAfterWorkbookIsClosed();
}
}));
overseeWorkbooksThread.Start();
The "ContainsWorkbookProperly" extension methods looks like this:
public static bool ContainsWorkbookProperly(this Excel.Workbooks excelWbs,
string sWorkbookName)
{
Excel.Workbook wbTemp = null;
try
wbTemp = excelWbs.Item(sWorkbookName);
catch (Exception)
{
// ignore
}
if (wbTemp != null)
{
new DisposableCom<Excel.Workbook>(wbTemp).Dispose();
return true;
}
return false;
}
Still I would be interested if there is a simpler or better solution.
This is not my code, but this worked a treat for me:
https://gist.github.com/jmangelo/301884
Copy paste:
using System;
using Excel = Microsoft.Office.Interop.Excel;
namespace Helpers.Vsto
{
public sealed class WorkbookClosedMonitor
{
internal class CloseRequestInfo
{
public CloseRequestInfo(string name, int count)
{
this.WorkbookName = name;
this.WorkbookCount = count;
}
public string WorkbookName { get; set; }
public int WorkbookCount { get; set; }
}
public WorkbookClosedMonitor(Excel.Application application)
{
if (application == null)
{
throw new ArgumentNullException("application");
}
this.Application = application;
this.Application.WorkbookActivate += Application_WorkbookActivate;
this.Application.WorkbookBeforeClose += Application_WorkbookBeforeClose;
this.Application.WorkbookDeactivate += Application_WorkbookDeactivate;
}
public event EventHandler<WorkbookClosedEventArgs> WorkbookClosed;
public Excel.Application Application { get; private set; }
private CloseRequestInfo PendingRequest { get; set; }
private void Application_WorkbookDeactivate(Excel.Workbook wb)
{
if (this.Application.Workbooks.Count == 1)
{
// With only one workbook available deactivating means it will be closed
this.PendingRequest = null;
this.OnWorkbookClosed(new WorkbookClosedEventArgs(wb.Name));
}
}
private void Application_WorkbookBeforeClose(Excel.Workbook wb, ref bool cancel)
{
if (!cancel)
{
this.PendingRequest = new CloseRequestInfo(
wb.Name,
this.Application.Workbooks.Count);
}
}
private void Application_WorkbookActivate(Excel.Workbook wb)
{
// A workbook was closed if a request is pending and the workbook count decreased
bool wasWorkbookClosed = true
&& this.PendingRequest != null
&& this.Application.Workbooks.Count < this.PendingRequest.WorkbookCount;
if (wasWorkbookClosed)
{
var args = new WorkbookClosedEventArgs(this.PendingRequest.WorkbookName);
this.PendingRequest = null;
this.OnWorkbookClosed(args);
}
else
{
this.PendingRequest = null;
}
}
private void OnWorkbookClosed(WorkbookClosedEventArgs e)
{
var handler = this.WorkbookClosed;
if (handler != null)
{
handler(this, e);
}
}
}
public sealed class WorkbookClosedEventArgs : EventArgs
{
internal WorkbookClosedEventArgs(string name)
{
this.Name = name;
}
public string Name { get; private set; }
}
}
When I used it I changed it from return the name of the workbook to a reference to the workbook.
Schedule a SyncContext action on workbook.Deactivate. This event is fired both when the workbook is closed and when another workbook takes focus.
Normally, in the Deactivate handler you can't check if the workbook is closed or just lost focus, but you can enqueue an action on the SyncContext to execute right after the event. In that action you can check if your workbook is still alive and execute code in case it's not.
Here's an example:
// put a syncContext instance somewhere you can reach it
static SynchronizationContext syncContext = SynchronizationContext.Current ?? new System.Windows.Forms.WindowsFormsSynchronizationContext();
// subscribe to workbook deactivate
workbook.Deactivate += workbook_Deactivate;
[DebuggerHidden]
private void workbook_Deactivate()
{
// here, the workbook is still alive, but we can schedule
// an action via the SyncContext which will execute
// right after the deactivate event is completed. At that
// point, the workbook instance (RCW) will no longer be usable
// meaning that the workbook has been closed
syncContext.Post(x =>
{
try
{
// will throw if workbook is gone
workbook.Path.ToString();
}
catch
{
// handle workbook closed
}
}, null);
}
Can you use both events? On BeforeClose() set a flag, then BeforeSave() see if the flag is set. You would need a way to reset it, though, in case BeforeClose() is triggered and BeforeSave() isn't. Not sure if there is something else that could help with that.
Edit: Looks like you covered this already with "the exact same order of events is executed". But if you can find a way to reset it (another "Cancel" event?) it may work.
I have a VSPackage with a dockable tool window containing form data. If there are unsaved changes in this form, I would like to cancel a close to either the tool window and the visual studio IDE if the user clicks cancel on saving changes before closing. I can perform the save test on close, but I don't see any event handler methods or other options to actually cancel the close.
Here is some blurb from the package:
private DTE2 _applicationObject = null;
///--------------------------------------------------------------------------------
/// <summary>This property gets the visual studio IDE application object.</summary>
///--------------------------------------------------------------------------------
public DTE2 ApplicationObject
{
get
{
if (_applicationObject == null)
{
// Get an instance of the currently running Visual Studio IDE
DTE dte = (DTE)GetService(typeof(DTE));
_applicationObject = dte as DTE2;
}
return _applicationObject;
}
}
///--------------------------------------------------------------------------------
/// <summary>
/// Initialization of the package; this method is called right after the package is sited, so this is the place
/// where you can put all the initilaization code that rely on services provided by VisualStudio.
/// </summary>
///--------------------------------------------------------------------------------
protected override void Initialize()
{
Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
base.Initialize();
// add the event handlers
if (ApplicationObject != null)
{
// wire up window events
PackageWindowEvents = (WindowEvents)ApplicationObject.Events.get_WindowEvents(null);
PackageWindowEvents.WindowClosing += new _dispWindowEvents_WindowClosingEventHandler(PackageWindowEvents_WindowClosing);
// wire up solution events
PackageSolutionEvents = ApplicationObject.Events.SolutionEvents;
PackageSolutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(CheckOpenCurrentSolution);
// wire up DTE events
PackageDTEEvents = ApplicationObject.Events.DTEEvents;
PackageDTEEvents.OnBeginShutdown += new _dispDTEEvents_OnBeginShutdownEventHandler(HandleVisualStudioShutdown);
}
}
void PackageWindowEvents_WindowClosing(Window window)
{
// handle save/cancel scenarios
}
And some blurb from the ToolWindowPane that implements IVsWindowFrameNotify3:
protected override void OnClose()
{
base.OnClose();
}
public int OnClose(ref uint pgrfSaveOptions)
{
return (int)__FRAMECLOSE.FRAMECLOSE_PromptSave;
}
The OnClose and WindowClosing methods fire when expected, but I'm not finding a way to cancel the close. What am I missing? Are there different events required to cancel a close?
For completeness, there IS a useful way to cancel a close with a tool window (provided by user Chicobo on the msdn forum and repeated here).
In your ToolWindowPane class, implement the IVsWindowFrameNotify2 interface, which provides a method OnClose.
To cancel the window closing, consider using :
public int OnClose(ref uint pgrfSaveOptions)
{
// Check if your content is dirty here, then
// Prompt a dialog
MessageBoxResult res = MessageBox.Show("This Document has been modified. Do you want to save the changes ?",
"Unsaved changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
// If the users wants to save
if (res == MessageBoxResult.Yes)
{
// Handle with your "save method here"
}
if (res == MessageBoxResult.Cancel)
{
// If "cancel" is clicked, abort the close
return VSConstants.E_ABORT;
}
// Else, exit
return VSConstants.S_OK;
}
For the purpose of completing the previous answer, the following code snippet targets an unanswered part of the question which is on how to prevent the close operation of the Visual Studio IDE inside a VsPackage:
protected override int QueryClose(out bool pfCanClose)
{
pfCanClose = false;
return VSConstants.S_OK;
}
Use the above code inside your package class.