Brace yourself. I'd recommend the SelectionAdded event for filtering that's more than usually complicated.
Say we want to select Polylines with 100 < length <= 300 that are red and on layers FOO or BAR.
Exhibit #1: The straightforward C# approach.
public class SelectionAddedCmd
{
bool myFilter(DBObject dbo)
{
var pl = dbo as Polyline;
return
pl != null &&
pl.Length > 100.0 &&
pl.Length <= 300.0 &&
pl.ColorIndex == 1 &&
pl.Layer == "FOO" ||
pl.Layer == "BAR";
}
void selectionAdded(object sender, SelectionAddedEventArgs e)
{
var i = 0;
foreach (SelectedObject so in e.AddedObjects)
{
if (!myFilter(so.ObjectId.GetObject(OpenMode.ForRead)))
e.Remove(i);
i++;
}
}
PromptSelectionResult selectWithFilter(Editor ed)
{
try
{
ed.SelectionAdded += new SelectionAddedEventHandler(selectionAdded);
return ed.GetSelection();
}
finally
{
ed.SelectionAdded -= new SelectionAddedEventHandler(selectionAdded);
}
}
[CommandMethod("SELFILC")]
public void SelFilC()
{
var doc = acApp.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
var psr = selectWithFilter(ed);
if (psr.Status == PromptStatus.OK && psr.Value.Count > 0)
ed.WriteMessage("\n{0} Objects selected", psr.Value.Count);
else
ed.WriteMessage("\nNo Objects selected. ");
}
}
}
Drawback: the predicate myFilter is hardcoded into the event handler. Compare this to
Exhibit #2: the F# proposal.
let (+=) e f = Observable.subscribe f e
let selectionIndexed =
Seq.cast<SelectedObject> >> Seq.mapi (fun i so -> i, so.ObjectId)
let myFilter: DBObject->_ = function
| :? Polyline as pl ->
pl.Length > 100. &&
pl.Length <= 300. &&
pl.ColorIndex = 1 &&
pl.Layer = "FOO" ||
pl.Layer = "BAR"
| _ -> false
let selectWithFilter (ed: Editor) f =
use selectionAdded =
ed.SelectionAdded +=
fun e ->
for (i, oid) in selectionIndexed e.AddedObjects do
if oid.GetObject OpenMode.ForRead |> f |> not then
e.Remove i
ed.GetSelection()
[<CommandMethod "SELFILF">]
let selFilF() =
let doc = acApp.DocumentManager.MdiActiveDocument
let db = doc.Database
let ed = doc.Editor
use tr = db.TransactionManager.StartTransaction()
let psr = selectWithFilter ed myFilter
if psr.Status = PromptStatus.OK && psr.Value.Count > 0 then
ed.WriteMessage("\n{0} Objects selected", psr.Value.Count)
else
ed.WriteMessage("\nNo Objects selected. ")
Now trying to replicate this in C# brings me into RX land, but it ain't very pretty.
Exhibit #3:
...
void selectionAdded(SelectionAddedEventArgs e, Func<DBObject, bool> f)
{
var i = 0;
foreach (SelectedObject so in e.AddedObjects)
{
if (!f(so.ObjectId.GetObject(OpenMode.ForRead)))
e.Remove(i);
i++;
}
}
PromptSelectionResult selectWithFilter(Editor ed, Func<DBObject, bool> f)
{
IObservable<IEvent<SelectionAddedEventArgs>> selectionAddedAsObservable =
Observable.FromEvent<SelectionAddedEventHandler,SelectionAddedEventArgs>(
e => new SelectionAddedEventHandler(e),
h => ed.SelectionAdded += h,
h => ed.SelectionAdded -= h);
using( var selectionAddedDisposable = selectionAddedAsObservable.Subscribe(a => selectionAdded(a.EventArgs, f))){
return ed.GetSelection();
}
}
...
var psr = selectWithFilter(ed, myFilter);
...
Any other suggestions to allow passing the predicate function to the event handler?
Greetings, Thorsten