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
?? Design Tip

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.

? Success Pattern

Implement proper data validation and unsaved changes detection to prevent data loss during navigation operations.

?? Important Note

Always implement proper error handling for data operations. Network issues, database locks, and other factors can cause navigation operations to fail.