Check .NET version (should be 8.0.x)
# Fix: Auto-Click Menu Issue di Blazor .NET 8
Home / Knowledge Base / Troubleshooting / Menu Auto-Click Fix
Problem
Setelah upgrade dari .NET 6 ke .NET 8, menu "mengklik sendiri" atau navigasi tidak stabil, terutama setelah ada notifikasi SignalR yang memicu StateHasChanged.
Root Cause
Technical Context
- Framework: .NET 8 Blazor Server
- UI Component: DevExpress DxTreeView
- Issue: Enhanced navigation in .NET 8 changed rendering behavior
Specific Issues
- SignalR
StateHasChanged- memicu re-renderDxTreeView - TreeView Animation (
Slide) - menyebabkan selection event terpicu ulang - Tidak ada navigation guard - double navigation terjadi
- Tidak ada debounce - rapid clicks tidak ter-handle
- .NET 8 Enhanced Navigation - lebih strict pada component lifecycle
Solutions Applied
1. Navigation Guard dengan Debounce (300ms)
Before:
void LoadPage(TreeViewNodeEventArgs e)
{
NavigationManager.NavigateTo(menuUrl);
}
After:
private bool isNavigating = false;
private string lastNavigatedUrl = string.Empty;
private System.Threading.Timer? navigationTimer;
void LoadPage(TreeViewNodeEventArgs e)
{
// Prevent duplicate navigation
if (isNavigating || targetUrl == lastNavigatedUrl)
return;
// Prevent navigation if already on the page
var currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
if (currentUrl.Equals(targetUrl, StringComparison.OrdinalIgnoreCase))
return;
// Debounce navigation (300ms)
navigationTimer?.Dispose();
navigationTimer = new System.Threading.Timer(_ =>
{
InvokeAsync(() =>
{
NavigationManager.NavigateTo(targetUrl);
isNavigating = false;
});
}, null, 300, Timeout.Infinite);
}
2. Disable TreeView Animation
Before:
<DxTreeView AnimationType="LayoutAnimationType.Slide" ... >
After:
<DxTreeView AnimationType="LayoutAnimationType.None" ... >
3. Add Stable Rendering Key
Before:
<DxTreeView Data="@GlobalMenus" ... >
After:
<DxTreeView
Data="@GlobalMenus"
@key="@($"treeview-{GlobalMenus?.GetHashCode()}")" ... >
4. Implement IDisposable
@implements IDisposable
public void Dispose()
{
navigationTimer?.Dispose();
if (hubConnection is not null)
{
_ = hubConnection.DisposeAsync();
}
}
Files Modified
Project Structure
erp/
├── Neuron_ERP/ # Main Blazor Server application
│ └── Shared/
│ └── NavMenu.razor # ← Modified file
├── NeuronLibrary/ # Core library
├── NeuronLibraryUI/ # UI components library
├── docker-compose.yaml # Docker configuration
└── Dockerfile # Docker build file
Changes Applied
Neuron_ERP/Shared/NavMenu.razor- Added navigation guard
- Added debounce timer (300ms)
- Changed animation to None
- Added @key for stable rendering
- Implemented IDisposable
Testing Steps
Prerequisites
Verify your environment first:
# Check .NET version (should be 8.0.x)
dotnet --version
# Check project SDK
Get-Content Neuron_ERP/Neuron_ERP.csproj | Select-String "TargetFramework"
# Expected output: <TargetFramework>net8.0</TargetFramework>
1. Clean & Rebuild:
# Clean solution
dotnet clean
# Remove bin and obj folders
Get-ChildItem -Path . -Include bin,obj -Recurse -Directory | Remove-Item -Recurse -Force
# Restore and rebuild
dotnet restore
dotnet build
2. Test Scenarios:
- Click menu - should navigate once
- Click same menu twice - should ignore second click
- SignalR notification arrives - menu should not auto-navigate
- Click different menus rapidly - should debounce properly
3. Run Application:
# Run locally
dotnet run --project Neuron_ERP
# Or with Docker
docker-compose up --build
Browser Console Test
Open browser console (F12) and monitor for issues:
What to Check
// Blazor Server specific checks
// 1. Should not see duplicate navigation logs
// Look for: "Navigating to..." messages appearing twice
// 2. Should not see "Navigation in progress" errors
// Blazor will log if trying to navigate during another navigation
// 3. Should not see animation glitches
// TreeView should expand/collapse smoothly
// 4. Check SignalR connection
// Look for: "WebSocket connected" or "Long polling connected"
// 5. Monitor component lifecycle
// Enable verbose logging in browser DevTools > Console settings
Debugging in Visual Studio
// Add logging in NavMenu.razor.cs or NavMenu.razor @code block
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
Console.WriteLine($"NavMenu rendered. First: {firstRender}");
}
// Log navigation attempts
void LoadPage(TreeViewNodeEventArgs e)
{
Console.WriteLine($"Navigation requested to: {targetUrl}");
// ... rest of code
}
Enable Detailed Blazor Logging
Add to appsettings.Development.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Components": "Debug",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
Expected Behavior After Fix
| Scenario | Before | After |
|---|---|---|
| Click menu | Sometimes double-navigates | Single navigation |
| SignalR notification | Auto-clicks random menu | No auto-click |
| Rapid clicks | All clicks processed | Debounced (300ms) |
| Same page click | Navigates again | Ignored |
| Animation | Janky | Smooth (disabled) |
Rollback (If Needed)
# Revert specific file
git checkout Neuron_ERP/Shared/NavMenu.razor
# Or revert all changes
git restore .
# Or reset to last commit
git reset --hard HEAD
Related Issues
.NET 8 Blazor Server Specific
- Enhanced Navigation in .NET 8 Blazor
- Blazor component re-rendering issues
- SignalR StateHasChanged conflicts
DevExpress Components
- DevExpress DxTreeView SelectionChanged event
- TreeView animation conflicts with re-rendering
General Blazor Best Practices
- Component lifecycle management
- Navigation guards and debouncing
- IDisposable implementation for cleanup
Environment
- Framework: .NET 8
- UI Framework: Blazor Server
- Component Library: DevExpress Blazor
- Database: PostgreSQL 17 (not related to this issue)
- Real-time: SignalR
Performance Impact
- Positive: Reduced unnecessary re-renders
- Positive: Prevented double API calls from duplicate navigation
- Positive: Improved UX with debounce
- Negative: 300ms delay on navigation (barely noticeable)
Lessons Learned
- Always use debounce for navigation events
- Disable animations if causing re-render issues
- Use @key for stable component instances
- Implement IDisposable for cleanup
- Test with SignalR when using real-time updates
Created: Januari 2026
Version: 1.0