Revit API AddInCommandBinding for ID_PROCESS_DROP - c#

I'm trying to bind the drag/drop of a family into the project and disable it.
My code is based on the Revit 2014 SDK Sample DisableCommand
My code has the .CanHaveBinding test and I have a dialog that displays success or failure. When I run the command it shows success, but I'm still able to drag drop families. Any ideas?
RevitCommandId commandId2 = RevitCommandId.LookupCommandId("ID_PROCESS_DROP");
if (!commandId2.CanHaveBinding)
{
TaskDialog.Show("Error", "Drag/Drop cannot be overridden.");
}
else
{
TaskDialog.Show("Success", "Drag/Drop can be overridden.");
}
try
{
AddInCommandBinding dropBinding = uiapp.CreateAddInCommandBinding(commandId2);
dropBinding.Executed += new EventHandler<Autodesk.Revit.UI.Events.ExecutedEventArgs>(dragDropDisable);
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}",ex.ToString());
}
private void dragDropDisable( object sender, Autodesk.Revit.UI.Events.ExecutedEventArgs args)
{
TaskDialog.Show("Disabled", "Never Drag/Drop families into your project!");
}

I think your method (and class) may need to be static - I've had weird things happen with instance methods in the past. Also, I'm not sure what your implementation of the method does exactly but it may need to return a CommandData.Result in order for the command to fully complete

Try this
dropBinding.Executed += dragDropDisable;

Related

Cannot modify the document for either a read-only external command is being executed, or changes to the document are temporarily disabled

I want to set parameter values of the elements when the DocumentChanged event is triggered. I know that the event is read-only.
Is there any another way (except IUpdater) to do it? Except IUpdater because I want every changes in the document.
Here is my code
public Result OnStartup(UIControlledApplication application)
{
try
{
// Event handler for document changing
application.ControlledApplication.DocumentChanged += new EventHandler<DocumentChangedEventArgs>(Application_DocumentChanged);
}
catch (Exception)
{
return Result.Failed;
}
return Result.Succeeded;
}
private void Application_DocumentChanged(object sender, DocumentChangedEventArgs e)
{
Document doc = e.GetDocument();
// Record added elements
if (e.GetAddedElementIds().Count() > 0)
{
using (Transaction transaction = new Transaction(doc))
{
try
{
transaction.Start("Set TCP Parameters");
foreach(ElementId el_id in e.GetAddedElementIds())
{
doc.GetElement(element_id).get_Parameter(new Guid("6558f207-e777-0758-2023-2f34e722cb01")).Set(200)
}
}
catch (Exception ex)
{
TaskDialog.Show("Error: Transaction", ex.Message);
}
transaction.Commit();
}
}
TaskDialog.Show("Document Changed", e.Operation.ToString());
}
I will really appreciate any ideas
You ask: Is there any another way?
Yes. You can subscribe to a one-off shot of the Idling event, cf. Reacting to Changes and Setting Parameters using DMU or DocumentChanged
This has been discussed repeatedly by The Building Coder and in the Revit API discussion forum. Some related articles are listed in the topic group on Idling and External Events for Modeless Access and Driving Revit from Outside.

Clear() not actually clearing the input element

I ran into a scenario today where clear() wasn't actually clearing the input element and I'm hoping someone can shed some light on this as I've never ran into this before.
It looks like the method executes successfully without throwing an exception but it just doesn't clear.
Clear Method - this is in a separate framework that is being used by the calling project
public void Clear(By element)
{
try
{
driver.FindElement(element).Clear();
}
catch (Exception ex)
{
DTAF.Helpers.Screenshot.TakeScreenshot(driver);
throw ex;
}
}
Element HTML - you can see it has value that I was trying to clear
<input aria-invalid="false" id="txtCustomerEditFirstName" type="text" data-testid="firstname" class="MuiFilledInput-input MuiInputBase-input css-1ncak0i" value="test123">
I was able to execute just raw selenium in the Immediate window and it looked like it cleared the input but when I clicked the actual element in the UI the value just came back so it didn't actually clear it.
I did find a workaround where I just get the input value attribute and loop through that pressing the Backspace key until theres nothing left. It works just fine but I'd prefer to use the selenium clear method if possible.
EDIT
Element Locator
public static By FirstNameTextbox = By.Id("txtCustomerEditFirstName");
Calling method
public EditUnregisteredCustomerProfileModal ClearAndEnterFirstName(string firstName)
{
try
{
ElementActions.Clear(EditUnregisteredCustomerProfileModalElements.FirstNameTextbox);
ElementActions.Type(EditUnregisteredCustomerProfileModalElements.FirstNameTextbox, firstName);
}
catch (Exception e)
{
DTAFLogger.GetLogger().Error($"Failed to clear and enter unregistered customer first name '{firstName}'. {e.Message}");
Assert.Fail($"Failed to clear and enter unregistered customer first name '{firstName}'. {e.Message}");
}
return this;
}

Can the Help.ShowHelp API be modified to simply invoke the CHM file?

This is my primary way for displaying help topics from within my WinForm button click handlers:
Handler:
private void buttonHelp_Click(object sender, EventArgs e)
{
CutTools.DisplayHelpTopic(this, "create-new-viewport.htm");
}
Base method:
public static void DisplayHelpTopic(Control parent, string topic)
{
try
{
// Use an empty form as the parent so that the help file will not block the CAD software
Form mHelpParent = new Form();
// Use location of this DLL file
System.Reflection.Module mod = parent.GetType().Module;
string path = Path.GetDirectoryName(mod.FullyQualifiedName);
Help.ShowHelp(mHelpParent,
Path.Combine(path, "cut-tools-help.chm"), HelpNavigator.Topic, topic);
}
catch (System.Exception ex)
{
_AcAp.Application.ShowAlertDialog(
string.Format("\nError: {0}\nStackTrace: {1}", ex.Message, ex.StackTrace));
}
}
The forms are displaid inside AutoCAD, BricsCAD or ZWCAD. The about is fine and great. But if I want to simply display the CHM file itself (so no actual form is available) I have to do this:
[CommandMethod("TS_DisplayHelp")]
public void TS_DisplayHelp()
{
// Use location of this DLL file
System.Reflection.Module mod = GetType().Module;
System.Diagnostics.Process.Start(
Path.Combine(Path.GetDirectoryName(mod.FullyQualifiedName), "cut-tools-help.chm"));
}
It works but has one drawback. It spawns a new instance of the help and does not use the same instance.
For example:
You start one of the other commands and show the help via button click. You cancel.
You start a different command and show the help via button click. Help.ShowHelp uses same instance.
You can command and start help via TS_DISPLAYHELP and it starts new instance.
Given the context of TS_DISPLAYHELP I can't work out how to directly use Help.ShowHelp as I can in my button click handlers.
At the moment I have managed to get around this issue by duplicating the DisplayHelpTopic code directly in the command TS_DISPLAYHELP method:
[CommandMethod("TS_DisplayHelp")]
public void TS_DisplayHelp()
{
try
{
// Use an empty form as the parent so that the help file will not block the CAD software
Form mHelpParent = new Form();
// Use location of this DLL file
System.Reflection.Module mod = GetType().Module;
string path = Path.GetDirectoryName(mod.FullyQualifiedName);
Help.ShowHelp(mHelpParent,
Path.Combine(path, "cut-tools-help.chm"), HelpNavigator.Topic, "command-index.htm");
}
catch (System.Exception ex)
{
_AcAp.Application.ShowAlertDialog(
string.Format("\nError: {0}\nStackTrace: {1}", ex.Message, ex.StackTrace));
}
}
I know that my default topic is "command-index.htm".
I am happy with the above resolution.

FileSystem.CopyDirectory with UIOption.AllDialogs does not display a dialog from within a specific part of my program

I'm having a hard time figuring out what I'm doing wrong. I use FileSystem.CopyDirectory so I can display a native copy dialog using UIOption.AllDialogs. In one part of my program, this works perfectly fine. In another part, it copies, but doesn't display the dialog at all.
This is the part that does not display a dialog:
private void saveBackupButton_Click(object sender, EventArgs e)
{
try
{
FileSystem.CopyDirectory(SelectedProfile.FolderPath + saveFolderPath, SelectedProfile.FolderPath + backupSavePath + DateTime.Now.ToString("dd.MM.yy HH.mm.ss"), UIOption.AllDialogs);
}
catch(Exception ex)
{
//stuff
}
}
The part that works fine is on another form, looks like this:
private void Copy()
{
try
{
FileSystem.CopyDirectory(strCopyFromPath, strCopyToPath, UIOption.AllDialogs, UICancelOption.ThrowException);
}
catch (Exception e)
{
//stuff
}
}
I've tried making a new project in VS and just only having the top code executing from a click of a button, and it works fine everytime. But when I try it from the program, it doesn't display the dialog.
As Peter Ritchie mentions, it looks like this method does not produce a dialog when the copy operation happens fast enough.

In C# what is the best way to close a newly opened form if there is a failure in CTOR/Load event?

What is the best method [pattern] to close a newly opened Windows form in C#/.NET if there is a trappable error in the CTOR/_Load event ?
i.e.,
bool loadError;
MyForm_Load(...) {
try {
} catch (SomeException ex) {
// Alert the user
MessageBox.Show("There was a critical error. The form will close. Please try again.");
// There was some sort of error, and I just want the form to close now.
loadError = true;
return;
}
}
Now I want to act on loadError.
I've tired using the Activate event, but that yielded no results. Any suggestions on what the best way to go about doing this is ?
Update: Perhaps I should have been a little more explicit. Doing a "this.Close();" during the forms Load event will cause an exception to be thrown as a form can't be closed and the exception will be "Cannot call Close() while doing CreateHandle()".
As I mentioned in the comments, I tried with a sample example and called this.Closed() inside of the catch block. It worked just fine. The application showed the message box and didn't show me the form. I am using .NET3.5 SP1, though.
Suppose this error happens in earlier version of .NET Framework, can you try to see whether any of the followings works for you or not? They, again, seem to work fine on my machine, yet, cannot guarantee whether they will work on yours, though.
Put this.Close() in the finally block to see it works
finally {
if (this.loadError)
this.Close();
}
Defer the Form.Close() after Form.OnLoad event handler completes.
See the sample below: this does not contain any anonymous delegate or lambda expression for older versions of .NET FW. Please forgive me for partial class :)
using System;
using System.Windows.Forms;
namespace ClosingFormWithException
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public delegate void InvokeDelegate();
private void Form1_Load(object sender, EventArgs e)
{
try
{
MyTroublesomeClass myClass = new MyTroublesomeClass();
}
catch (ApplicationException ex)
{
MessageBox.Show("There was a critical error. The form will close. Please try again.");
this.BeginInvoke(new InvokeDelegate(CloseTheForm));
}
}
private void CloseTheForm()
{
this.Close();
}
}
class MyTroublesomeClass
{
public MyTroublesomeClass()
{
throw new ApplicationException();
}
}
}
Use the combination of 1 and 2 if 2 does not work:
finally {
if (this.loadError)
this.BeginInvoke(new InvokeDelegate(CloseTheForm));
}
Have you tried calling this.Close() in the FormShown event? That event is fired once when your form is first shown. You could check your error condition there and then call close.
I think you should call form.Dispose() and then set form = null. This should cover for the main usage scenario.

Categories

Resources