BeepDataNavigator
Advanced data navigation control with UnitOfWork integration and customizable button layout
?? Table of Contents
Overview
BeepDataNavigator is a comprehensive data navigation control that provides complete record navigation functionality with UnitOfWork integration. It offers first, previous, next, last navigation buttons, along with optional query, filter, email, and print capabilities.
Built on the BeepControl foundation, BeepDataNavigator features automatic button layout, record position display, theming support, and seamless integration with data sources through the UnitOfWork pattern. Perfect for data entry forms, record browsing interfaces, and administrative dashboards.
Key Features
?? Complete Navigation
First, Previous, Next, Last navigation with automatic button state management
?? UnitOfWork Integration
Seamless integration with UnitOfWork pattern for data management
?? Extended Actions
Optional Query, Filter, Email, and Print functionality
?? Position Display
Real-time record position indicator showing current record and total count
?? Auto Layout
Intelligent button arrangement with center alignment and proper spacing
?? Customizable UI
Configurable button visibility, sizes, and spacing options
Visual Preview
BeepDataNavigator with all navigation and action buttons
Basic Usage
Simple Data Navigator
// Create a basic data navigator
var dataNavigator = new BeepDataNavigator
{
Size = new Size(400, 50),
Location = new Point(20, 20),
Dock = DockStyle.Bottom // Common placement
};
// Connect to UnitOfWork
dataNavigator.UnitOfWork = myUnitOfWork;
// Add to form
this.Controls.Add(dataNavigator);
Navigator with Custom Actions
// Create navigator with all features enabled
var navigator = new BeepDataNavigator
{
Size = new Size(500, 60),
Location = new Point(20, 400),
ShowQuery = true,
ShowFilter = true,
ShowSendEmail = true,
ShowPrint = true,
ButtonWidth = 35,
ButtonHeight = 35
};
// Configure events
navigator.QueryCalled += OnQueryClicked;
navigator.FilterCalled += OnFilterClicked;
navigator.EmailCalled += OnEmailClicked;
navigator.PrintCalled += OnPrintClicked;
// Set data source
navigator.UnitOfWork = customerUnitOfWork;
this.Controls.Add(navigator);
Event Handling
private void OnQueryClicked(object sender, BeepEventDataArgs e)
{
var queryDialog = new QueryBuilderForm();
if (queryDialog.ShowDialog() == DialogResult.OK)
{
// Apply query to data source
ApplyQuery(queryDialog.QueryExpression);
}
}
private void OnFilterClicked(object sender, BeepEventDataArgs e)
{
var filterPanel = new FilterPanel();
filterPanel.Show();
}
private void OnEmailClicked(object sender, BeepEventDataArgs e)
{
var currentRecord = navigator.UnitOfWork.Units.Current;
if (currentRecord != null)
{
EmailCurrentRecord(currentRecord);
}
}
private void OnPrintClicked(object sender, BeepEventDataArgs e)
{
var currentRecord = navigator.UnitOfWork.Units.Current;
if (currentRecord != null)
{
PrintCurrentRecord(currentRecord);
}
}
Properties
Core Properties
| Property | Type | Default | Description |
|---|---|---|---|
UnitOfWork |
IUnitofWork | null | Data source providing navigation functionality |
ButtonWidth |
int | 30 | Width of navigation buttons in pixels |
ButtonHeight |
int | 30 | Height of navigation buttons in pixels |
Visibility Properties
| Property | Type | Default | Description |
|---|---|---|---|
ShowQuery |
bool | false | Show/hide the Query button |
ShowFilter |
bool | false | Show/hide the Filter button |
ShowSendEmail |
bool | false | Show/hide the Email button |
ShowPrint |
bool | false | Show/hide the Print button |
Button References
| Property | Type | Description |
|---|---|---|
btnFirst |
BeepButton | First record navigation button |
btnPrevious |
BeepButton | Previous record navigation button |
btnNext |
BeepButton | Next record navigation button |
btnLast |
BeepButton | Last record navigation button |
btnQuery |
BeepButton | Query action button |
btnFilter |
BeepButton | Filter action button |
btnEmail |
BeepButton | Email action button |
btnPrint |
BeepButton | Print action button |
txtPosition |
BeepLabel | Record position display label |
Events
Navigation Events
| Event | Type | Description |
|---|---|---|
CurrentChanged |
EventHandler | Raised when the current record changes |
ListChanged |
ListChangedEventHandler | Raised when the data list changes |
PositionChanged |
EventHandler | Raised when the record position changes |
DataSourceChanged |
EventHandler | Raised when the data source changes |
Action Events
| Event | Type | Description |
|---|---|---|
QueryCalled |
EventHandler<BeepEventDataArgs> | Raised when Query button is clicked |
FilterCalled |
EventHandler<BeepEventDataArgs> | Raised when Filter button is clicked |
EmailCalled |
EventHandler<BeepEventDataArgs> | Raised when Email button is clicked |
PrintCalled |
EventHandler<BeepEventDataArgs> | Raised when Print button is clicked |
Examples
Complete Data Entry Form
public partial class CustomerEntryForm : Form
{
private BeepDataNavigator navigator;
private IUnitofWork customerUoW;
private BeepTextBox txtName;
private BeepTextBox txtEmail;
private BeepTextBox txtPhone;
public CustomerEntryForm()
{
InitializeComponent();
SetupDataNavigator();
SetupDataBinding();
}
private void SetupDataNavigator()
{
navigator = new BeepDataNavigator
{
Dock = DockStyle.Bottom,
Height = 60,
ShowQuery = true,
ShowFilter = true,
ShowPrint = true,
ButtonWidth = 40,
ButtonHeight = 40
};
// Wire up events
navigator.QueryCalled += Navigator_QueryCalled;
navigator.FilterCalled += Navigator_FilterCalled;
navigator.PrintCalled += Navigator_PrintCalled;
navigator.CurrentChanged += Navigator_CurrentChanged;
this.Controls.Add(navigator);
}
private void SetupDataBinding()
{
// Initialize UnitOfWork
customerUoW = new UnitofWork();
customerUoW.LoadData(); // Load customer data
// Connect navigator
navigator.UnitOfWork = customerUoW;
// Bind form controls to current customer
BindControls();
}
private void BindControls()
{
txtName.DataBindings.Clear();
txtEmail.DataBindings.Clear();
txtPhone.DataBindings.Clear();
if (customerUoW.Units != null)
{
txtName.DataBindings.Add("Text", customerUoW.Units, "Name");
txtEmail.DataBindings.Add("Text", customerUoW.Units, "Email");
txtPhone.DataBindings.Add("Text", customerUoW.Units, "Phone");
}
}
private void Navigator_CurrentChanged(object sender, EventArgs e)
{
// Update form title with current customer info
var current = customerUoW.Units.Current as Customer;
if (current != null)
{
this.Text = $"Customer Entry - {current.Name} (ID: {current.Id})";
}
else
{
this.Text = "Customer Entry - No Record";
}
}
private void Navigator_QueryCalled(object sender, BeepEventDataArgs e)
{
var queryForm = new CustomerQueryForm();
if (queryForm.ShowDialog() == DialogResult.OK)
{
// Apply query and refresh data
customerUoW.ApplyFilter(queryForm.FilterExpression);
BindControls(); // Rebind after filter
}
}
private void Navigator_FilterCalled(object sender, BeepEventDataArgs e)
{
var filterForm = new CustomerFilterForm();
filterForm.ShowForm(this);
}
private void Navigator_PrintCalled(object sender, BeepEventDataArgs e)
{
var current = customerUoW.Units.Current as Customer;
if (current != null)
{
var report = new CustomerReport(current);
report.Print();
}
}
}
Master-Detail with Navigator
public partial class OrderMasterDetailForm : Form
{
private BeepDataNavigator orderNavigator;
private BeepDataNavigator orderItemNavigator;
private IUnitofWork orderUoW;
private IUnitofWork orderItemUoW;
private BeepGrid orderItemGrid;
public OrderMasterDetailForm()
{
InitializeComponent();
SetupNavigators();
SetupMasterDetailRelationship();
}
private void SetupNavigators()
{
// Master navigator (Orders)
orderNavigator = new BeepDataNavigator
{
Location = new Point(20, 250),
Size = new Size(400, 50),
ShowQuery = true,
ShowFilter = true
};
orderNavigator.CurrentChanged += OrderNavigator_CurrentChanged;
// Detail navigator (Order Items)
orderItemNavigator = new BeepDataNavigator
{
Location = new Point(20, 500),
Size = new Size(400, 50),
ShowQuery = false,
ShowFilter = false
};
this.Controls.AddRange(new Control[] {
orderNavigator, orderItemNavigator
});
}
private void SetupMasterDetailRelationship()
{
// Initialize UnitOfWork objects
orderUoW = new UnitofWork();
orderItemUoW = new UnitofWork();
// Load data
orderUoW.LoadData();
// Connect navigators
orderNavigator.UnitOfWork = orderUoW;
orderItemNavigator.UnitOfWork = orderItemUoW;
// Setup initial detail data
LoadOrderItems();
}
private void OrderNavigator_CurrentChanged(object sender, EventArgs e)
{
LoadOrderItems();
}
private void LoadOrderItems()
{
var currentOrder = orderUoW.Units.Current as Order;
if (currentOrder != null)
{
// Load order items for current order
orderItemUoW.LoadDataWithFilter($"OrderId = {currentOrder.Id}");
// Refresh the grid
orderItemGrid.DataSource = orderItemUoW.Units;
}
else
{
// Clear order items if no order selected
orderItemUoW.Clear();
orderItemGrid.DataSource = null;
}
}
}
Custom Navigation Logic
public class CustomDataNavigator : BeepDataNavigator
{
private bool _hasUnsavedChanges;
private Form _parentForm;
public bool HasUnsavedChanges
{
get => _hasUnsavedChanges;
set => _hasUnsavedChanges = value;
}
public CustomDataNavigator(Form parentForm)
{
_parentForm = parentForm;
SetupCustomBehavior();
}
private void SetupCustomBehavior()
{
// Override button clicks to check for unsaved changes
btnFirst.Click += (s, e) => NavigateWithUnsavedCheck(() => UnitOfWork?.MoveFirst());
btnPrevious.Click += (s, e) => NavigateWithUnsavedCheck(() => UnitOfWork?.MovePrevious());
btnNext.Click += (s, e) => NavigateWithUnsavedCheck(() => UnitOfWork?.MoveNext());
btnLast.Click += (s, e) => NavigateWithUnsavedCheck(() => UnitOfWork?.MoveLast());
}
private void NavigateWithUnsavedCheck(Action navigationAction)
{
if (HasUnsavedChanges)
{
var result = MessageBox.Show(
_parentForm,
"You have unsaved changes. Do you want to save before navigating?",
"Unsaved Changes",
MessageBoxButtons.YesNoCancel,
MessageBoxIcon.Question);
switch (result)
{
case DialogResult.Yes:
if (SaveCurrentRecord())
{
HasUnsavedChanges = false;
navigationAction?.Invoke();
}
break;
case DialogResult.No:
HasUnsavedChanges = false;
navigationAction?.Invoke();
break;
case DialogResult.Cancel:
// Do nothing - stay on current record
break;
}
}
else
{
navigationAction?.Invoke();
}
UpdateRecordCountDisplay();
}
private bool SaveCurrentRecord()
{
try
{
// Implement your save logic here
// Return true if save successful, false otherwise
UnitOfWork?.SaveChanges();
return true;
}
catch (Exception ex)
{
MessageBox.Show(
_parentForm,
$"Error saving record: {ex.Message}",
"Save Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return false;
}
}
}
Theming
BeepDataNavigator provides comprehensive theming with automatic application of theme styles to all child buttons and labels:
Theme Application
var navigator = new BeepDataNavigator
{
Theme = "DarkTheme",
UseThemeFont = true
};
// Theme automatically applies to:
// - All navigation buttons (First, Previous, Next, Last)
// - Action buttons (Query, Filter, Email, Print)
// - Position label (txtPosition)
// Manual theme application
navigator.ApplyTheme();
Button Theming
All buttons inherit theme properties:
- Button colors - Background and foreground from theme
- Border styles - Border colors and radius from theme
- Hover effects - Theme-based hover colors
- Disabled states - Proper disabled appearance
Custom Button Styling
// Customize individual button themes after initialization
navigator.btnFirst.Theme = "PrimaryTheme";
navigator.btnPrevious.Theme = "PrimaryTheme";
navigator.btnNext.Theme = "PrimaryTheme";
navigator.btnLast.Theme = "PrimaryTheme";
// Customize action buttons differently
navigator.btnQuery.Theme = "AccentTheme";
navigator.btnFilter.Theme = "AccentTheme";
navigator.btnEmail.Theme = "SuccessTheme";
navigator.btnPrint.Theme = "InfoTheme";
Best Practices
?? Data Integration
- UnitOfWork pattern: Always use proper UnitOfWork implementation for data management
- Data binding: Use DataBindings for automatic synchronization with UI controls
- Error handling: Implement proper error handling for navigation operations
- Performance: Load data efficiently and use pagination for large datasets
?? User Experience
- Consistent placement: Place navigator at bottom of forms for conventional behavior
- Clear feedback: Provide visual feedback for navigation state changes
- Keyboard support: Implement keyboard shortcuts for power users
- Unsaved changes: Always check for unsaved changes before navigation
? Performance
- Lazy loading: Load detail data only when needed
- Efficient queries: Use filtering to limit data loads
- Cache management: Implement proper caching for frequently accessed data
- Background operations: Use async operations for data loading
?? Customization
- Button visibility: Show only relevant action buttons for your use case
- Custom events: Implement custom logic for action button events
- Validation: Add validation logic before allowing navigation
- State management: Maintain proper state across navigation operations
Position the navigator at the bottom of your form and ensure it's always visible. Users expect navigation controls to be consistently placed and easily accessible.
Implement proper data validation and unsaved changes detection to prevent data loss during navigation operations.
Always implement proper error handling for data operations. Network issues, database locks, and other factors can cause navigation operations to fail.