Proposal: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout
Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout
Summary
This proposal introduces a new DensityValue struct to the .NET MAUI Grid layout engine to improve layout precision across density-independent units (dp) and ensure pixel-aligned rendering. It addresses layout inconsistencies caused by fractional pixel results, especially in high-DPI environments.
Problem Statement
In high-DPI environments, evenly dividing space in dp often results in fractional pixel values that don’t map cleanly to integers. For example:
- A container with a width of
293.4dpat a density of2.625results in770.175px. - Dividing this across 3 columns gives
256.725pxper column. - Independent rounding can lead to
256 + 256 + 256 = 768, causing layout gaps or overflow.
Android mitigates this by accumulating rounding error and correcting it in the final element. MAUI Grid currently lacks this behavior, leading to:
- Overlapping content
- Jittery rendering
- Clipped visuals
This is especially problematic with * (star) sizing, where users expect equal visual distribution.
Proposal
Introduce a DensityValue struct that tracks:
- The original logical unit (dp)
- The corresponding physical unit (px), based on current display density
This allows the Grid to:
- Perform layout in dp for consistency
- Internally track exact pixel dimensions
- Accumulate and resolve rounding discrepancies (e.g., assign remainder pixels to the last column/row)
- Avoid layout drift from naive float division
Benefits
- More accurate star-based layouts
- Eliminates layout jitter from float rounding
- Aligns with native Android/iOS layout behavior
- Enables future support for sub-pixel rendering or layout debugging
Implementation Details
- Introduce a
DensityValuestruct with methods to resolve dp to px - Replace direct use of
double/floatin Grid layout calculations withDensityValue - Accumulate pixel widths and apply final rounding corrections at the row/column level
- Initially keep the struct internal; consider public exposure later
Example Struct
struct DensityValue
{
public double Dp { get; }
public double Density { get; }
public double RawPx => Dp * Density;
private const double Epsilon = 0.00001;
}
Sample Scenarios
Scenario 1: 293.4dp Across 3 Columns (Density: 2.625)
- Total px:
770.175 - Ideal per column:
256.725px
Allocation:
- Column 1: 256px
- Column 2: 256px
- Column 3: 258px ← absorbs rounding error
- Total: 770px (0.175px under)
Scenario 2: 290dp Across 3 Columns (Density: 3.0)
- Total px:
870 - Ideal per column:
290px
Allocation:
- Column 1: 290px
- Column 2: 290px
- Column 3: 290px
- Total: 870px — perfect match
Scenario 3: 300dp Across 4 Columns (Density: 2.625)
- Total px:
787.5 - Ideal per column:
196.875px
Allocation:
- Column 1: 196px
- Column 2: 196px
- Column 3: 196px
- Column 4: 199px ← absorbs rounding error
- Total: 787px (0.5px under)
Conclusion
A centralized mechanism like DensityValue ensures intentional, consistent handling of rounding errors, improving layout fidelity and aligning with platform-native expectations.