Right mouse click event

Right mouse click event

hieu_nguyenZ8FEN
Observer Observer
283 Views
3 Replies
Message 1 of 4

Right mouse click event

hieu_nguyenZ8FEN
Observer
Observer

Hi everyone,

I have a project where I need to add a list of items from an API to the AutoCAD context menu by using .Net 8 and AutoCAD 2025.

I have successfully retrieved the items, but they are only added the first time. My solution is to call the API every time the right mouse button is clicked. However, I haven't been able to find an event that triggers on right-click.

How can I achieve this without creating a command method? or is there any better solution?

 

Thanks all.

0 Likes
284 Views
3 Replies
Replies (3)
Message 2 of 4

norman.yuan
Mentor
Mentor

You need to post your code to show how do you "... add a list of items from an API to the AutoCAD context menu...".

 

If you use Application.AddDefault[Object]ContextMenuExtension() to create the context menu, then your custom context menu items should appear with AutoCAD's context menu when user right-clicks AutoCAD editor with or without entities selected. You would not worry what even triggers the right-click at all.

 

Again, without showing your code, people can hardly make sense about your question.

 

Norman Yuan

Drive CAD With Code

EESignature

Message 3 of 4

hieu_nguyenZ8FEN
Observer
Observer

Here is my current code:

 

public class ContextMenuApplication : IExtensionApplication
{
    private ISaveDrawingAsSectionVariantMenu _menuSaveDrawingAsSectionVariant;

    public async void Initialize()
    {
        var apiConfig = GetAPIConfig();

        var serviceCollection = new ServiceCollection();
        ConfigureServices(serviceCollection, apiConfig);
        var serviceProvider = serviceCollection.BuildServiceProvider();
        _menuSaveDrawingAsSectionVariant = serviceProvider.GetRequiredService<ISaveDrawingAsSectionVariantMenu>();
        _menuSaveDrawingAsSectionVariant.Attach();
    }

    public void Terminate()
    {
        _menuSaveDrawingAsSectionVariant.Detach();
    }

    public static void ConfigureServices(IServiceCollection services, APIConfiguration apiConfig)
    {
        services.AddSingleton(apiConfig);
        services.AddSingleton<ISaveDrawingAsSectionVariantMenu, SaveDrawingAsSectionVariantMenu>();
        services.AddSingleton<ContextMenuApplication>();
    }
}
public class SaveDrawingAsSectionVariantMenu : ContextMenuBase
{
    private List<ContextMenuItemModel> _menuItems = new List<ContextMenuItemModel>();
    private static readonly HttpClient _client = new HttpClient();
    private readonly APIConfiguration _apiConfiguration;
    private readonly string _appServiceUrl = "api/app";

    public SaveDrawingAsSectionVariantMenu()
    {
    }

    public SaveDrawingAsSectionVariantMenu(APIConfiguration apiConfiguration)
    {
        _apiConfiguration = apiConfiguration;
        _client.BaseAddress = new Uri(_apiConfiguration.BaseUrl);
    }

    public async void Attach()
    {
        await GetVariantsAsync();
        //RXClass rxc = Entity.GetClass(typeof(Entity));
        EventHandler onClick = new EventHandler(OnClick);
        // Call the Attach method of the base class
        Attach(_menuItems, onClick, ACadConstants.ContextMenu.SAVE_DRAWING_AS_SECTION_VARIANT);
    }

    public void Detach()
    {
        //RXClass rxc = Entity.GetClass(typeof(Entity));
        // Call the Detach method of the base class
        Detach(null);
    }

    // Set the OnClick method to perform an action when the menu item is selected
    protected override async void OnClick(Object o, EventArgs e)
    {
        Document activeDoc = Application.DocumentManager.MdiActiveDocument;
        if (activeDoc == null)
        {
            Application.ShowAlertDialog("No active document to save.");
            return;
        }
        Editor ed = activeDoc.Editor;

        if (o is MenuItem menuItem && menuItemValues.TryGetValue(menuItem, out string value))
        {
            var destinationFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ACadConstants.TEMP_FOLDER);
            if (!Directory.Exists(destinationFolder))
            {
                Directory.CreateDirectory(destinationFolder);
            }
            string sourceFileName = Path.GetFileNameWithoutExtension(activeDoc.Name);
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string destinationFile = Path.Combine(destinationFolder, $"{sourceFileName}_{timestamp}.dwg");
            try
            {
                using (Database db = activeDoc.Database)
                {
                    // save the drawing to the destination folder
                    db.SaveAs(destinationFile, DwgVersion.Current);
                }

                await UploadFile(ed, destinationFile, value);

                FileHelper.DeleteFile(destinationFile);
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage($"\nError saving file: {ex.Message}.");
            }
        }
    }

    private async Task GetVariantsAsync()
    {
        var response = await _client.GetStringAsync($"{_appServiceUrl}/section-drawing/variants");
        var result = JsonConvert.DeserializeObject<ResultModel<List<SectionDrawingModel>>>(response);

        if (result.IsSucceed && result.Data.Any())
        {
            _menuItems = result.Data.Select(s => new ContextMenuItemModel(s.Id.ToString(), s.Page)).ToList();
        }
    }

    private async Task UploadFile(Editor ed, string filePath, string variantId)
    {
        if (!File.Exists(filePath))
        {
            ed.WriteMessage($"\nFile not found.");
            return;
        }
        using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            using (var content = new MultipartFormDataContent())
            {
                // read file
                var fileContent = new StreamContent(stream);
                fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
                // add file to content
                content.Add(fileContent, "file", Path.GetFileName(filePath));
                
                HttpResponseMessage response = await _client.PostAsync($"{_appServiceUrl}/azure-storage/upload?variantID={variantId}", content);
                if (response.IsSuccessStatusCode)
                {
                    ed.WriteMessage($"\nFile saved successfully.");
                }
                else
                {
                    ed.WriteMessage($"\nError saving file: {response.StatusCode} - {response.ReasonPhrase}.");
                }
            }
        }
    }
}
public class ContextMenuItemModel
{
   public string? Text { get; set; }
	public string? Value { get; set; }
	public string? Title { get; set; }

	public ContextMenuItemModel(string value, string text, string? title = null)
	{
		Text = text;
		Value = value;
		Title = title ?? string.Empty;
	}
}
public abstract class ContextMenuBase
{
    protected static ContextMenuExtension cme;
    protected static Dictionary<MenuItem, string> menuItemValues = new Dictionary<MenuItem, string>();

    // Method to add ContextMenu to object
    public void Attach(string menuItemText, EventHandler onClick, RXClass? entityClass = null)
    {
        cme = new ContextMenuExtension();
        
        MenuItem mi = new MenuItem(menuItemText);
        mi.Click += onClick;
        cme.MenuItems.Add(mi);

        if (entityClass is not null)
        {
            Application.AddObjectContextMenuExtension(entityClass, cme);
        }
        else
        {
            Application.AddDefaultContextMenuExtension(cme);
        }
    }

    public void Attach(List<ContextMenuItemModel> menuItems, EventHandler onClick, string title, RXClass? entityClass = null)
    {
        cme = new ContextMenuExtension
        {
            Title = title
        };

        foreach (var menuItem in menuItems)
        {
            MenuItem mi = new MenuItem(menuItem.Text);
            mi.Click += onClick;
            cme.MenuItems.Add(mi);
            menuItemValues[mi] = menuItem.Value;
        }

        if (entityClass is not null)
        {
            Application.AddObjectContextMenuExtension(entityClass, cme);
        }
        else
        {
            Application.AddDefaultContextMenuExtension(cme);
        }
    }

    // ContextMenu removal method
    public void Detach(RXClass? entityClass = null)
    {
        if (cme is not null)
        {
            if (entityClass != null)
            {
                Application.RemoveObjectContextMenuExtension(entityClass, cme);
            }
            else
            {
                Application.RemoveDefaultContextMenuExtension(cme);
            }
            cme = null;
        }
    }

    // Abstract method to handle the event when the menu is selected
    protected abstract void OnClick(Object o, EventArgs e);
}

 

In the code above, the GetVariantsAsync function is called to retrieve variant items and attach them to the context menu. However, these items are only bound once during the initial attachment. As a result, if the items are updated, the context menu will not reflect those changes.

I expect GetVariantsAsync to be called on every right mouse click to fetch the latest items and ensure the context menu remains up to date.

Thanks.

0 Likes
Message 4 of 4

ActivistInvestor
Mentor
Mentor

I don't think you can use an async method to do what you are trying to do because the call to the method in which you must add items to the menu isn't going to wait for your await'ed code to finish, it is going to return before that. You have to add the items to the menu from the method and you have to do it synchronously. By the time your awaited method call returns, the menu has already popped up. You can't await the return of the method in which you must add the items to the menu.

 

You can try to handle the PopUp event of the ContextMenuExtension class, but I think that would have the same result if you made the event handler async and called await within it.

 

 

 

 

0 Likes