How to select multiple elements in a Revit drawing - c#

I'm trying to create a "Multi-Select" method in Revit (for 2016/2017) where the users can select specific parameters of instances contained within a drawing (like Nominal Diameter, Pipe Type and so on), and it will select all the instances within the drawing based on their selections. First, screen shot:
Let's predicate this with the fact that this window is created dynamically based on contents of the drawing. Nothing gets put in this window unless there is an element in the drawing that contains/meets one or more of these parameters.
So, ideally, when I Click the DO IT! button, I would like it to select all of the elements in the drawing that have meet any of these parameters. I can filter through this window and find all of my selections - now I just don't know what do with the selections.
I've looked through the Revit.chm and source and found the Selection namespace and class. There are functions like:
PickObject(ObjectType objectType);
that seem like they would be what I want, but I don't know if it's REALLY what I need. Furthermore, if that IS, in fact, what I'm looking for, I don't know the syntax of how to use it.
A little code:
I have a method that collects all of the users' selections:
private List<CheckBox> GetUserFilterPrefs()
{
//CYCLES THROUGH ALL THE PANELS AND BOXES IN THE WINDOW
return lstCheckBox;
}
Now I want to create my EventHandler for btnDoIt_Click...
I started it, but I'm walking in the dark on this part.
private void btnDoIt_Click(object sender, RoutedEventArgs e)
{
int itr = 0;
GetUserFilterPrefs();
List<Reference> lstRefs = new List<Reference>();
foreach (CheckBox cb in lstCheckBox)
{
if (lstElts[itr].Name == cb.Name)
{
//HOW DO I SELECT ALL ITEMS LIKE THE GIVEN ELEMENT
//THAT ARE RELATED TO THE CHECKBOX SELECTION??
}
itr +=1;
}
I'll obviously keep looking around; but if anyone knows a way, or can point me in the right direction, it would be massively helpful!
THANKS!!!

The PickObject function that you've found is one that asks the user to select an object in the model. Based on your description, that is not what you are looking for.
The function you need is:
SetElementIds(ICollection<ElementId> elementIds)
It is also part of the Selection class. This will highlight the desired elements in the model. To clear the selection in the model, pass an empty List as your argument. Passing null will cause an exception to be thrown.
To focus on the elements, you need the function:
UIDocument.ShowElements
There are a number of overloads for this function. Note that none if the elements are in the currently open view, Revit will attempt to find the best view for you, a task that it generally performs very poorly if there are many views in the model.

PickElement prompts the user for interactive element selection, which is not what you are after.
The one and only way to programmatically access elements in the Revit database is to use a filtered element collector:
http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.9

Related

How to implement TreeMaps DrillDown on Button Click?

I am using the treemaps for an application development. The problem I am facing is that I want to use a button to remove the level I have traversed. I have tried but there is no use. I am new to WPF programming and I am not sure about all the methods that can be used. I have put a button and I have been able to retrieve the level number and reduce the level number onClick. but the level is the same. I used drilldown property for the tree maps. But I am not sure of implementing it by use of a button. Sample is mostly preferred as a beginner. Hope for a reply sooner.
Scenario:-
I was using a squarified treemap for visualising a data that consisted of 4 levels. I drillDown the map to view certain details. For example, I have a world map that shows the continents separately. I have clicked on any of the continent, say Asia, I will get another level that shows the countries as a treemap level and further clicking on the country leads me to the cities or states. Like wise I can move backwards without an problem. Now, I am using a button to remove the levels I am currently located in. I am not able to do so and could you please help me with a code snippet for the button
I am partially there, but a detailed research will help me. It took a long time for me to settle in this.
private void Back_Click(object sender, RoutedEventArgs e)
{
int level = TreeMap.Levels.Count;
if (level >= 1)
{
TreeMap.Levels.RemoveAt(level-1);
}
}
This code can only be used to change the level, not the visual of the same level. It will not get you back to the desired screen, but you can notice the level change once you debug and use Watch.

Retrieve activity name

I'm working in a WPF project and I need one requirement.
This is my main window:
http://img210.imageshack.us/img210/9752/rule.jpg
So, I want that when I drag and drop a custom activity (aggregate Functions), display his name in Compute TextBox... Is there some way to do it????
For example:
1) Drag and drop MaxActivity
2) Display in TextBox Compute: MaxActivity
3) Drag and drop SumActivity
4) Display in TextBox Compute: MaxActivity + SumActivity
5) Drag and Drop MaxActivity
6) Display in TextBox Compute: MaxActivity + SumActivity + MaxActivity1
Thanks a lot!
I would strongly suggest you avoid this design, as however you end up implementing it will introduce brittleness to your code without any benefit (I can already see you are aggregating max and sums, why tell me again?)
If you go ahead, get rid of that "Compute" TextBox. TextBoxes = enter text. You are just rephrasing what is already in the design surface. It makes no sense to allow me to change the text you generate. Use a TextBlock or Label.
Do not, I repeat, not save this in your Activity. This is all UI tasks and should not be saving this information in your Activity configuration
If you have this
public sealed class MyActivity : Activity
{
public string Compute {get;set;} // NO!
and this
<!-- NO! -->
<Label>Compute</Label><TextBox Text="{Binding ModelItem.Compute}"/>
you're probably doing this wrong.
So, how do you do it?
You can parse out your ModelItem and generate this string by listening to changes in the ModelItem and constructing this string every time. Note, your ModelItem will not be set when the constructor is called, so you are going to have to listen to changes to the ActivityDesigner.ModelItem property. There is no event for this, so you will have to know how to listen to changes in a DependencyProperty.
Once you are listening to changes in your ModelItem, whenever a change happens, you can walk down your ModelItem (which is hard) or just get the Activity from the ModelItem and examine it.
var child = ModelItem.Properties["Child"].Value.GetCurrentValue();
if(child == null)
computeTextBox.Text = "Please add some computation stuff.";
else
// hard stuff goes here...
Walking down the path from your Activity's child to whatever activities are held inside can be treacherous, as you can't assume your user has thrown in a Sequence rather than a single MaxActivity, or that they don't have seven nested Sequences, etc etc. So you have to have a pretty complex and brittle set of if else logic to try and parse out what is contained below. And if you start changing properties of the Activity tree outside of the ModelItem you're going to get out of sync with the UI.
Now, after reading that, go back to point 1. I listed in this answer and follow my advice. Drop the idea completely.

How can I pass data through a DragDrop event?

I have very little experience making my own events. I'm currently using forms and controls to handle events, so I'm trying to stick to the WinForm way of dealing with events.
WinForm controls already have their own events. Since events are made with a certain delegate type, I have to match the delegate signature in the methods I use to handle what goes on during an event. As far as I can tell, this means I have to take in two parameters in my event handlers: a source Object and an EventArgs.
I'm trying to figure out how to pass various information between controls through DragDrop related events. I've been able to find various code snippets online for different situations, but I'd like to have a better understanding of how the information is actually passed around.
When it comes to DragDrop events in particular, it seems my information will be passed through a DataObject. That, in turn, is passed as a parameter to the DragDrop.DoDragDrop method.
This is where my understanding starts to diminish. In most examples I've seen, it looks like some sort of data format is specified. The data format itself is of type string. Usually the data format is passed along with the data into the DataObject.
What is this data format doing? Is it just showing the type of the data involved? I've seen examples where the data format was tested, and various actions took place depending on what the data format had been. Couldn't you just do a typeof(YourData) to check for the type? I don't understand how a data format is useful.
http://msdn.microsoft.com/en-us/library/ms741842.aspx
In the above link, the first example shows a DataObject being created with no data format being specified. It explains that the data format is automatically chosen and that your object is converted by default.
Can you just pass any object into the DataObject? Then when the DragDrop event takes place on the target control, can that control access the methods and fields of the object passed? Or will the object be converted into some other form by the DataObject? How about various structures?
I've also done some drag and dropping from a Windows explorer icon to my forms. I was able to get the icon's file path with some example code I found. It also used a data format.
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Copy;
}
SUMMARY:
I'm trying to get an understanding of how data is passed through WinForm events. I don't understand some of what I've seen in various DragDrop examples. I understand that data is somehow stored in various formats, and that there is a way to extract that data. I don't understand what a data format is used for. I don't understand exactly what sorts of data can be transferred through a DataObject. My understanding of EventArgs in general is fairly lacking.
Feel free to comment on anything I've mentioned here. If I stated something incorrectly, point it out. Anything that will help me understand this subject better is appreciated.
EDIT:
I decided to explain where I plan to go with this. Maybe someone can use what I'm trying to do as a way to explain some of what I asked.
I have a form that contains a certain type of control. I'm trying to make the placement of the controls highly customizable and dynamic. Whenever another control is added to the form by the user, every control is automatically repositioned to keep things orderly.
I'm currently trying to allow the user to drag a control around the Form to resposition it. Wherever the user decides to drop the control, all of the other controls will move out of the way to make room for the move control.
If I'm going to be able to move the dragged control and all the other controls on the form properly, I need to be able to get certain information from the dragged control. The main data that would need to be passed through the events would take the form of a Location property that uses a System.Drawing.Point structure.
Here's something to give you an idea of how it's done.
First drop two panels on a Form, and set their colors to two different colors.
public Form1()
{
InitializeComponent();
panel1.MouseDown += new MouseEventHandler(panel1_MouseDown);
panel2.AllowDrop = true;
panel2.DragEnter += new DragEventHandler(panel2_DragEnter);
panel2.DragDrop += new DragEventHandler(panel2_DragDrop);
}
void panel1_MouseDown(object sender, MouseEventArgs e)
{
panel1.DoDragDrop(panel1, DragDropEffects.Move);
}
void panel2_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetData(typeof(Panel)) != null) e.Effect = DragDropEffects.Move;
}
void panel2_DragDrop(object sender, DragEventArgs e)
{
Panel p = sender as Panel;//Not needed in this case. Could just write panel2.
Panel dropped = (Panel)e.Data.GetData(typeof(Panel));
dropped.Location = p.PointToClient(new Point(e.X, e.Y));
p.Controls.Add(dropped);
}
Then drag panel1 onto panel2.
I think this problem should not be solved using drag&drop. Drag&drop is meant as a way for multiple applications to exchange data. If the data you are dragging is only meaningful for the current process, don't use drag&drop.
You can just use a field (for example, a field on the instance of the containing Form) to store all the data you need.
Just to say the obvious: Drag&drop is not a way to drag around controls. You need a lot of custom logic for that, anyway. It's not like the built-in features of dnd would help you here.

c# how to add contextMenuStrip item at runtime?

I am sure that the answer to this has been posted before. Forgive me as I think I am just not thinking of the right search string.
What I have is a context menu strip assigned to my tray icon for my dialer. The idea is for the user to set various numbers and select the user defined numbers from the menu and initiate the dial.
So the menu pops up with Presets, Setup, & Exit. I want the Presets menu to open a new tree listing the user defined number. I also want this to populate from an xml file every time the application is loaded.
My problem is that I have no idea how to dynamically populate a sub menu item and give it a function.
So how would I at start up add user defined numbers to preset -> (userNumber1, usernumber2, userNumber3) and then call the dial() function when clicked?
So I found how to add to the list... I now feel silly for asking that. For anyone else who wants to know that one, The list item is given a name. Im my case the name attribute is " presetsToolStripMenuItem"
So to add an item to it call the name
presetsToolStripMenuItem.DropDownItems.Add(string text)
No to move on. I am stuck now trying to figure out how to assign an event to that newly added function. I did find
presetsToolStripMenuItem.DropDownItems.Add(string text, image, eventargs)
I am struggling with this one. Maybe I need to stop and come back to it later. Perhaps if someone could provide me with an example of using this line to call a function(); I would be most appreciative.
For anyone that is interested I figured out the solution to adding a context menu item at run time with the ability to call a function.
As stated before, to add a sub menu item to a a parent category, use the parent.name. So in my case the preset menu item name was "presetsToolStripMenuItem"
To add function I used the 3 argument method.
ToolStripMenuItem.DropdownItems.Add("string name", image, eventargs);
so my code looks like this:
presetsToolStripMenuItem.DropDownItems.Add("added2", null, disp);
void disp(object sender, EventArgs e)
{
MessageBox.Show("It works!");
}

How bad is the following snippet?

My question is simple: how bad is the following snippet of code? How would you do it?
CancelEventHandler _windowClosing;
private CancelEventHandler WindowClosing
{
set
{
clearEventHandlerList();
this.Closing += value;
_windowClosing = value;
/*
* if calling the method with null parameters,
* it will set up itself as the primary control on the Window
*/
_windowClosing(null,null);
}
get
{
return _windowClosing;
}
}
private readonly CancelEventHandler[] CONTROLS = null;
private int current = 0;
public InitializerForm()
{
InitializeComponent();
/*
* these are the handlers for the different controls,
* in the order of appereance to the user
*/
STATES = new CancelEventHandler[] { handler1, handler2, etc. };
WindowClosing = CONTROLS[0];
}
private void clearEventHandlerList()
{
foreach (CancelEventHandler c in CONTROLS)
{
this.Closing -= c;
}
}
private void handler1(object obj, CancelEventArgs e)
{
if (obj == null)
{
//hide every other control, but this one, also set up if necessary
}
else
{
//do something
WindowClosing = CONTROLS[++current]; // set the next control to show
e.Cancel = true;
}
}
The point would be that the code wouldn't close a form, but instead show another component on it, and the set the way to handle that (this is mobile platform, so clicking OK button on the top generates a closing event). This is because showing several forms (4 or 5) one after another to the user makes the app blink, and also very annoying, while replacing just components is much smoother. This model works, but seems very nasty, and I would like a cleaner way to handle this.
Update:
I updated the code sample so that variable names are somewhat speaky. Still, I'm convinced this is awful, (a) but not sure how much, and more importantly, (b) how to do it better.
Update 2:
So, it seems that the code is still a bit mysterious.
Now here's what the problem is:
I show the user a form, which instructs him what to do in several languages. He proceeds by clicking OK on the window. Next, I ask for his language, and then a few questions (where his/her GPS is, etc.) like this. After he could answer the questions (this shouldn't take more than a few seconds each), I show him a splash screen (I load stuff in a separate thread meanwhile), which has a picture. Showing these forms one after another makes the whole application start slow, and filled with UI lags.
Here's what I do to work around the lags: I put the content of the windows into panels, and put those panels one on another, and hide every one of them but the one that should be visible to the user. (current variable) Each of the windows does different things, so I need to change handler of the window closing event in addition. In this code the part which enables the panel is in the same function (handler1, handler2, etc.) with the part which handles the window closing event. If the arguments are null, it does the former, if it isn't (that means it was triggered by the user) it does the latter.
I need an extensible solution to this so that I can insert and remove dialogs anytime I want (the order and the pointers to the functions are stored in the CONTROLS field, and this seems to be very convenient, if you actually understand it. Although it is never easy to change the entire content of a form, there ought to be a simpler way to do this, as well a nicer one, that is what I'm looking for.
I hope this time I could explain how the model works.
I think it might be theoretically possible to make that code more delightfully diverting, perilously puckish, jovially jeopardous, cheerily chancy and unwarily whimsical but it would require some serious thought.
somehow your code makes me want to cry, i´m sorry. i read it twice and all i know about it is that it "doesStuff" with "STATES".
if you really want some help on this one you will have to work on it yourself first...
Use, XML! It's human-readable!
More seriously-
It seems like you're trying to create some sort of configuration wizard, so I'd start by researching that. Regarding your particular solution, I generally advocate very strongly against the "layered panel" approach. I do so because I maintain apps written by people who found this approach, or the related "hidden tabs on a tab control" approach, to be a good idea. It's not, and maintainers will curse your name for years to come.
That being said, what alternatives are there? Well, one alternative is what you've already dismissed because of its "flicker". I'd say that, in general, the flicker isn't that big of a deal for a quick and dirty application. It might be a good idea to make sure that your new window is called up before closing the old one, for example. (I'm assuming this is possible, I haven't developed on a mobile device.)
Another possibility might be a less-evil version of your layered panels. Instead of throwing a half-dozen panels into one form, create a separate user control for each wizard page and then add/remove the user controls to a containing form. This can avoid your flicker and will prove to be much easier to maintain because each page is in a different control. This might also ease any subsequent "Back" button functionality and make your data structures more naturally defined because those user controls will be associated with a specific logical bit of data. It's still not ideal, but it's probably good enough for a one-off solution.
A third technique, if you foresee extensive wizard modification as the product matures, might be to generalize the creation of your user controls by defining them in a more logical/declarative manner (e.g. via XML). If you dynamically generate sensible controls based on XML, then modifying the panels might be as easy as diving into your XML and doing something like:
<Questions>
<Question type="Text"> <!-- generate a textbox for the answer field -->
Favorite Color:
</Question>
<Question type="Number" range="0-255"> <!-- Maybe this is a spinner -->
The answer to life, the universe, and everything:
</Question>
</Questions>
That's just off the top of my head, and completely overkill for any one-off application, but it's a possibility.
Now, let me caveat this by saying this might work, but it may not be the answer to your real problem - that of a slow and unresponsive UI when you have a lot of forms. The real answer may be to just go ahead and do all separate forms, but have each form load its child forms in a background thread while the user is staring at the first form.
But assuming you're still set on this, I'd start off by making a separate class just to handle the Panel stacking/hierarchy. Call it PanelManager. You would instantiate the PanelManager and associate it with the main form, then add Panels to it (perhaps keyed to a String) and set the order. In the main form, have the closing handler call PanelManager.CloseCurrentPanel() and if there are no more Panels to show then it's time to close the main form.
Time for pseudo-code! Here's a quick idea for the class, i'll leave it to you to implement it:
public class PanelManager {
// constructor
public PanelManager (Form ownerForm);
// short-cut properties
public Panel this[int idx]
{ get; set; }
public int Index
{ get; set; }
// main functionality
public int AddPanel (Panel p);
public void SetPanelOrder (Panel p, int idx);
public void RemovePanel (Panel p);
public void RemovePanelAt (int idx);
// shows the first Panel
public void Show ();
// shows Panel[idx]
public void Show (int idx);
// adds the panel to the top of the stack and displays it
// returns the index of the panel
public int AddPanelAndShow (Panel p);
// hides the current panel, displays the one underneath it
// returns false if there are no more panels
public bool HideCurrentPanel ();
}
in the constructor for the main form, instantiate it by new PanelManager (this), then in the closing event handler, call panelManager.HideCurrentPanel () and then figure out whether or not you need to close it after that.

Categories

Resources