Prevent Revit from receiving a delete command in modeless form - api

I am desperately trying to prevent revit from receiving a delete command while in a modeless form (one that was displayed with Show() and not Showdialog()), but I can’t seem to be able to stop it.
My problem is my form contains textboxes: its core function requires the user to be able to navigate (pan and zoom) in revit without having to close and reopen the form.
But as soon as the user presses delete on any textbox text revit thinks the user has entered a delete command in the main window, and this could lead to accidental deletions.
Note the api queries a revitdb based on selected entities (families) and is then they are able to rename the family type or family name, using a textbox and ‘apply’ button. But as soon as they hit the delete button on the keyboard, guess what – the entity is deleted in revit.
I’ve spend 10 hours on this and it could turn out to be catastrophic to my plans.

Answer my own question: The workaround is to just 'pin' selected elements when user clicks on textbox.
private void textBox3_MouseClick(object sender, MouseEventArgs e)
{
//find selected items
Selection sel = uidoc.Selection;
ICollection<ElementId> ids = sel.GetElementIds();
foreach (ElementId id in ids)
{
Element el = doc.GetElement(id);
el.Pinned = true;
}
}
Then unpin when user leaves field (unsatisfying andw.

Related

MS Access one form to select table values and use to populate multiple fields on other form?

I have a table called GL_Account and IM_Productline
The table IM_Productline has various fields that need to be populated with a value from the field GL_Account.AccountKey (i.e. IM_ProductLine.InventoryAcctkey and IM_ProductLine.CostOfGoodsSoldAcctKey)
To populate the IM_ProductLine table I made a form "Product Line Maintenance" with all the fields. To populate the field IM_ProductLine.InventoryAcctkey I put a (magnifying glass) button behind the field with the following code:
Private Sub CMD_Select_GL_Account_Click()
Me.Refresh
If IsNull(Select_ProductType) Then
'do nothing
Else
Forms![Product Line Maintenance].InventoryAcctkey = Me.SelectGLAccountKey.Column(0)
Forms![Product Line Maintenance].Refresh
End If
DoCmd.Close
End Sub
So the button opens a form Called "Select GL Account" with a combo box that enable to SELECT GL_Account.AccountKey, GL_Account.Account, GL_Account.AccountDesc
FROM GL_Account; and when the OK button is clicked it writes the value from GL_Account.AccountKey to IM_ProductLine.InventoryAcctkey, closes the form "Select GL Account" and then refreshes the form "Product Line Maintenance" so the account number and description become visible for the user.
This all work fine but here's my question:
Now rather than creating a new form for every account field I need to populate (i.e. "Select Inventory GL Account" select "Cost Of Goods Sold GL Account" etc) I'd prefer to use the form "Select GL Account" to select and populate the 11 different account fields. So behind each xxxAcctkeyfield on form "Product Line Maintenance" is a (magnifying glass) button that when clicked pulls up the form "Select GL Account" and when "OK" is clicked it writes the selected AccountKey to the correct field on form "Product Line Maintenance"?
I'd greatly appreciate anyone's efforts to understand what I am trying to explain and point me in the right direction.
Ok, there is the issue that all 11 fields should not require to be "copied" since you have a relational database (you would ONLY store the row PK ID of that selection in the current report. (a so called FK (foreign key) value). That way, say you want to change the choice? Well then you could pop up that form - search + select the one record with all that information, and then upon return ONLY store the one value.
So, I would give some thoughts to the above - you want to leverage the relational database features. And as a result, you don't need to "copy" all that data. This is not much different then say creating a invoice. I can create the new invoice, but all of the address information, and the customer that this ONE invoice belongs to? Well, that is one column with a FK value that points to the customer. Once I select that one customer, then display of the customer name + address can be say a sub form or some such - but no need exists to "copy" that information. It would also means with near zero code, you could move a invoice between customers!!! - (just change the one fk column with to the new/different customer ID (PK) value.
Now, back to the question at a hand?
You can certainly pop up a form, let the user select, enter, pick and do whatever. And THEN you can have the calling code grab + pick out the values from that form.
The way you do this? It involves a not too wide known trick.
The code that calls the form can simply open that form as a dialog form. This will HALT the calling code, the user does whatever, and when done the calling code will THEN continue. Not only does the calling code continue, but it can get/grab/pull/take any values from that pop up form WIHOUT having to use global vars.
The approach is thus thus:
dim strF as string
strF = "frmPopAskInfo"
docmd.OpenForm strF,,,,,,acDialog
' above code waits for user input
if application.AllForms(strF).IsLoaded = true then
' user did not cancel, get values from form
me!AccountNo = forms(strf)!AccountNumber
etc. etc. etc.
docmd.Close acForm,strF
end if
Now the only other issue? Well, the "ok" button on the popup for DOES NOT close the form, what it does is set visible = False. This will kick the form out of dialog mode.
me.Visible = False
So, if the user hits the cancel buttton (close form) or hits the X upprer right (close form), then the form will NOT be loaded when your calling code continues. But, if they hit OK button, then you don't close the form, but ONLY set visbile = false.
This allows the calling code to continue, you are free to get/grab/take values from that form, and then once done, you close the form.
So a form close in that popup = user canceled the form.
So, a popup form, and even a modal form? They do NOT halt the VBA calling code, but a acDialog form does!
You can thus place 2 or 5 little buttons that pops up this form, the user can pick/choose/select/enter values. When they hit ok, your calling code continues, and you are free to pull values from that form. So while all 3-4 buttons might pop up that form, each individual button launch of the form can have code that follows and updates the given control the pop button was placed beside.

How can I set my ComboBox to allow the user to type in the first few characters and then automatically select the item by pressing ENTER?

I have a feeling this is a very simple thing that I'm overlooking.
I have two ComboBoxes that allow users to search for/select the record that they want to view. One is filled with Customer Names and the other is filled with Customer Numbers, so the user can look for a particular record by either selecting the Name or Number.
Each ComboBox is filled by a Data Table returned from a SQL Server database.
Each ComboBox has DropDownStyle set to DropDown, AutoCompleteMode set to SuggestAppend and AutoCompleteSource set to ListItems.
The user can either select by clicking the DropDown arrow and then clicking on the item they was or they can begin by typing and the ComboBox narrows the number of items in the list based on the characters the user is typing.
Using the mouse to click on the item in the list that they want works fine...it fires off a routine to retrieve the selected item from the database.
However, when the user types in the desired selection and presses ENTER, nothing happens. They must click the DropDown arrow and click on the item in order for the program to pull the appropriate record.
How do I get the ComboBox to pull the appropriate record when the user hits enter?
I'm using Visual Basic.
From the sounds of it, you need three events.
You need to use a timer to know when the user has stopped typing. To do that, you need one event would be when that field they're typing in has it's value change (<control's name>.TextChanged). That would start/restart a timer ticking (so that the user has a couple seconds to pause before the next event fires).
The next event would be the Tick event for that timer. That event would stop the timer, and then give focus to the right field so that when the user hits ENTER, they're not hitting ENTER in the field they've been typing in. You'll need to write a function to look up the right item in the ComboBox and call that.
Then you'd have a third event, either KeyPress, KeyDown, or KeyUp on the ComboBox itself. I'd lean towards the KeyUp to avoid issues if the user holds ENTER for whatever reason. That'd be what selects the item.
As a final FYI, I'm assuming you're using Visual Studio to write your code. If not, you should, and if you are/once you are, you can select the field you want to work with in the drop-down at the top left of the editor and then look at the associated events in the top right drop-down.
Thank you to JMichael for getting me on the right track with this one. I'm posting my solution here just in case it helps someone who has a similar question in the future:
The code that I added to the ComboBox's SelectionChangeCommitted event needed also to be added to the ComboBox's KeyUp event:
Private Sub cboPolicySearch_KeyUp(sended as Object, e As KeyEventArgs) Handles cboPolicySearch.KeyUp
If e.KeyCode = Keys.Enter Then
GetSelectedPolicySearchRecord()
e.Handled = True
End If
End Sub 'cboPolicySearch_KeyUp
The GetSelectedPolicySearchRecord() sub contained all of the information I needed to call my SQL Stored Procedure to select the data for the record that the user selected in the ComboBox.
Previously, this was only being called from the ComboBox's "SelectionChangeCommitted" event which is executed when the user clicks the drop down and then clicks a policy number from the drop down list.
I needed to add the same call to the GetSelectedPolicySearchRecord in the ComboBox's "KeyUp" event for when the user presses enter.

wxPropertyGrid::EditorValidate() always returns true

A user is editing a value in a property grid, then he clicks a button outside the property grid which executes code to read the property values. Sometimes the OLD value of the property is read, rather than the new value which was being edited. I have to tell the users that they must complete the editing, by hitting return or clicking on another property before clicking any buttons outside the grid. They forget, and report a bug.
I would like to make this foolproof. Perhaps by forcing the current edit to complete when the mouse leaves the property grid.
I know how to handle the mouse leaving event. I do not know how to force the property grid to accept any partial edits.
I have tried, as a hint to the user,
pg = new wxPropertyGrid( ...
....
if( ! pg->EditorValidate() )
{
SetStatusText("Please complete editing");
return;
}
but EditorValidate() always returns true
Found it!
wxPropertyGrid::CommitChangesFromEditor()
http://docs.wxwidgets.org/trunk/classwx_property_grid.html#a6e06d92a622237457fea00372df1eaae

vb.net, I have only one textbox in the form and the validate event won't fire

I'm trying to make a text box where you can enter a page number and the form will change. I'm using a textbox to allow the user to enter a page number and I want the form to change when the are no longer actively editing the textbox, the problem is that the textbox is the only editable control in the form so it never loses focus and the validating event never fires. Is there any way to do this?
As matzone said use the keypress but not for the page number keys.
For example, say your user wants to goto page "51", then the user can type 51 and press the return key.
Have the events fire on the return key,
private void TextBox1_KeyDown(object sender, KeyEventArgs e)
{
//13 is the keycode for Return
If (e.KeyCode == 13)
{
//SuperAweomseEventGoesHere
}
}
Use a button as was mentioned to indicate the user wants to load a new page.
Use the validating event to make sure the user entered a valid value.
Alternatively:
If your page numbers are all double digits you could also use the TextChanged event and keep checking if the text value equals a page number. Having at least 2 digits is important since 1 will load a page and the user won't be able to load 10
Another alternative:
Use a NumberUpDown control or a combobox so the user is restricted to the proper values

Reading 'Checked' Property Of Dynamic CheckBox on Page Postback

Apologies in advance for the long-winded post, but I'm having some trouble with a .NET page I'm building.
END QUESTION: How can I check the 'Checked' property of a dynamically-created checkbox during the Page_Load subroutine?
DETAILS: Basically, I have a VB.NET page that creates some table rows dynamically, based on a number selected by the user. So, for example, if the user selects "3" from a dropdown list, the page posts back and creates three table rows. Each row contains a couple of textboxes, a dropdown list, and a checkbox (which are all .NET form controls rather than plain HTML controls, I should point out).
Typically, the user would enter a few details into the form controls, and click the 'Submit' button, after which the page iterates through each row, and inserts the data into a SQL Server table.
But if the user ticks the checkbox for that row, this signifies that the page is to ignore that row, and NOT insert that row of data into the database when the user clicks 'Submit'.
This works well, but there is a problem. If the user clicks 'Submit' and some of the values entered into the form controls are invalid (so, for example, the user didn't enter their name) then the page won't submit the data, and instead, shows an error to the user informing them of the values they need to change. But if the user creates three rows, for example, but decides to "ignore" the third row (by ticking the checkbox) then when the page posts back, finds some invalid entries, and re-shows the form to the user to allow them to correct any errors, I'd rather the page didn't render the third row altogether. After all, they chose to create three rows originally, but then decided that they only needed two. So it makes sense that the third row is not recreated.
To start with, I simply used code similar to the following within my Page_Load subroutine:
If objCheckbox.Checked = False
' Render the row, and recreate the dynamic controls '
Else
' Dont render the row or any of the controls '
End If
But what seemed to happen was that objCheckbox.Checked was always returning False, even when the checkbox was ticked. Once the page had loaded, the table rows had rendered again, and the tick was present in the checkbox, so it's not like the value was lost on postback. But at the point I check whether the checkbox is ticked, it always returns False, rendering a table row that the user doesn't need.
Does anyone know how to get round this problem? I've read lots of articles about the .NET ViewState, and the page lifecycle, but I've yet to find a solution that works. I simply need to be able to check if a checkbox is ticked before re-creating some dynamic controls.
I tried this alternative code, which utilises the ViewState, but to no avail:
If objIgnoreCheckbox.ViewState("Checked") = False Then
' Render the row, and recreate the dynamic controls '
Else
' Dont render the row or any of the controls '
End If
When doing this, I get the following error:
'System.Web.UI.Control.Protected Overridable ReadOnly Property ViewState() As System.Web.UI.StateBag' is not accessible in this context because it is 'Protected'.
So I tried to create a custom class, that inherited from Checkbox, and tried to override the ViewState property to make it public, so that it can be read from:
Public Class CheckboxControl
' Inherits all Checkbox properties and methods '
Inherits Checkbox
' Create the public ViewState property '
Public Overrides ReadOnly Property ViewState As StateBag
Get
Dim objChecked As Object = ViewState("Checked")
If Not (IsNothing(objChecked)) Then
Return objChecked
End If
End Get
End Property
End Class
But then I basically found out that you can't override a protected member with a public one. I've had very little experience with creating new classes etc, so I'm stumped.
So if anyone can tell me how to go about checking the darned checkbox, I'd be eternally grateful! I've spent a full working day trying to solve the problem, but with no luck!
Thanks in advance!
For static controls, the view state of controls is restored in Page_Init, which happens before Page_Load, so they contain the correct values in Page_Load. Dynamic controls are created in Page_Load, so their viewstate is incorrect in Page_Load and will be restored after Page_Load, but before calling event handlers. MSDN says:
After the Page_Load event has run, but before control event-handling methods are called, the remaining view state information is loaded into the dynamically created controls.
This is why Checked returns false, and why changing the visibility of CheckBox.ViewState will not solve your problem.
Solution (untested, but I think it should work): Create all controls (even those that you don't want to display) in Page_Load. Attach an event handler on CheckedChanged to your CheckBoxes (in Page_Load). After Page_Load has finished, ASP.NET will restore the view state of the newly created controls (that's why it is important to create the same controls in Page_Load, so that ASP.NET can correctly map the view state to the control), and ASP.NET will call your event handler for those CheckBoxes that have been changed. There, you can remove the rows you don't want to display.
This is how you add the event handler
AddHandler objCheckbox.CheckedChanged, AddressOf MyCheckedChangedFunction
This function would look something like this:
Function MyCheckedChangedFunction(sender As Object, e As EventArgs)
Dim objCheckbox = DirectCast(sender, CheckBox)
... access objCheckbox.Changed and do something useful here ...
End Function