I have a few external commands that work perfectly when called by a pulldown button in Revit. I was attempting to upgrade the code by creating a list of commands in a WPF window using the MVVM pattern - mostly. There are no models in these cases instead a service that speaks to the Revit file. Below is an example of a service executes a call to remove materials:
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace DevTools.Services.Cleaner
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
[Journaling(JournalingMode.NoCommandData)]
public class PurgeAllMaterialsService : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
var uiDoc = commandData.Application.ActiveUIDocument;
var doc = uiDoc.Document;
try
{
using Transaction trans = new(doc, "Delete all materials");
var allMaterials = new FilteredElementCollector(doc)
.OfClass(typeof(Material))
.ToElementIds();
var count = 0;
trans.Start();
foreach (var id in allMaterials)
{
try
{
var m = doc.GetElement(id) as Material;
{
uiDoc.Document.Delete(id);
count++;
}
}
catch (Exception ex)
{
message = ex.Message;
return Result.Failed;
}
}
trans.Commit();
TaskDialog.Show("Delete All Materials", count + " materials have been deleted");
return Result.Succeeded;
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException)
{
return Result.Cancelled;
}
catch (Exception ex)
{
message = ex.Message;
return Result.Failed;
}
}
}
}
The view-model calls the service in a relay command
using CommunityToolkit.Mvvm.Input;
using DevTools.Services.Cleaner;
namespace DevTools.ViewModels
{
public partial class CleanerViewModel
{
private PurgeAllMaterialsService _purgeAllMaterialsService;
[RelayCommand]
private void PurgeAllMaterials()
{
_purgeAllMaterialsService = new PurgeAllMaterialsService();
}
}
}
and the command passes the service to the view.
using Autodesk.Revit.Attributes;
using DevTools.Services.Cleaner;
using DevTools.ViewModels;
using DevTools.Views;
using Nice3point.Revit.Toolkit.External;
namespace DevTools.Commands
{
[UsedImplicitly]
[Transaction(TransactionMode.Manual)]
public class CleanWindowCommand : ExternalCommand
{
public override void Execute()
{
var viewModel = new CleanerViewModel();
var view = new CleanerPage(viewModel);
view.ShowDialog();
}
}
}
in the view the command is called by a button like so.
<Button Content="Purge Materials"
Command="{Binding PurgeAllMaterialsCommand, Mode=OneWay}"
Width="200"
Height="28"
Margin="0,10,0,0"
BorderBrush="Transparent"
Background="{StaticResource HeaderBackgroundBrush}"
Foreground="{StaticResource LightBrush}" />
The problem is the button does not execute the command. What is missing? Is the WPF window blocking the command bein sent?
The Command
property of the Button
must be set or bound to an object that implements the System.Windows.Input.ICommand
interface.
So the PurgeMaterialCommand
property of your view model should return an ICommand
implementation. The Execute(object parameter)
method will then be called when you invoke the command by clicking on the button.