Skip to content

Commit 47bf499

Browse files
committed
Improve WPF auto-filter UI and filter logic
Enhanced the WPF filter dropdown UI with a custom ContextMenu, improved selection logic for 'Select All' and partial selections, and unified filter state handling. Added a new funnel icon for filtered columns, fixed DPI scaling in PointToScreen, and made filter logic more robust for both WinForm and WPF platforms.
1 parent 24fd39a commit 47bf499

File tree

7 files changed

+751
-343
lines changed

7 files changed

+751
-343
lines changed

DemoWPF/MainWindow.xaml.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ private void AddDemoSheet2()
9696
{"Total", 25, 28, 28, 27, 27},
9797
};
9898

99-
worksheet.AddOutline(RowOrColumn.Row, 3, 4);
99+
var filter = worksheet.CreateColumnFilter(new RangePosition("A1:F7"));
100+
101+
worksheet.AddOutline(RowOrColumn.Row, 3, 4);
100102

101103
var range = worksheet.Ranges["B3:F6"];
102104
worksheet.AddHighlightRange(range);

ReoGrid/CellTypes/DropdownCell.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -663,8 +663,8 @@ public virtual void PushDown()
663663

664664
dropdownPanel.Width = Math.Max(Bounds.Width, MinimumDropdownWidth);
665665
dropdownPanel.Height = DropdownPanelHeight;
666-
667-
dropdownPanel.Show(sheet.workbook.ControlInstance, new System.Windows.Point(p.X, p.Y + Bounds.Height));
666+
var sp = sheet.ControlAdapter.PointToScreen(p);
667+
dropdownPanel.Show(sheet.workbook.ControlInstance, new System.Windows.Point(sp.X, sp.Y + Bounds.Height));
668668
DropdownControl.Focus();
669669
isDropdown = true;
670670
}

ReoGrid/Common/GraphicsToolkit.cs

Lines changed: 87 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public static Point PointAtArc(Rectangle rect, RGFloat angle)
130130
#endregion // Calculation
131131

132132
#region Drawing
133-
public enum TriangleDirection { Left, Up, Right, Down, }
133+
public enum TriangleDirection { Left, Up, Right, Down, DownFilter, }
134134

135135
public static void FillTriangle(PlatformGraphics g, RGFloat size, Point loc, TriangleDirection dir = TriangleDirection.Down)
136136
{
@@ -154,63 +154,110 @@ public static void FillTriangle(PlatformGraphics g, RGFloat size, Point loc, Tri
154154
RGFloat x = loc.X;
155155
RGFloat y = loc.Y;
156156

157-
switch (dir)
158-
{
159-
case TriangleDirection.Up:
160-
loc.X -= size / 2;
161-
for (x = 0; x < size / 2; x++)
162-
{
157+
switch (dir)
158+
{
159+
case TriangleDirection.Up:
160+
loc.X -= size / 2;
161+
for (x = 0; x < size / 2; x++)
162+
{
163163
#if WINFORM || WPF
164-
g.DrawLine(p, new Point(loc.X + x, y), new Point(loc.X + size - x - 1, y));
164+
g.DrawLine(p, new Point(loc.X + x, y), new Point(loc.X + size - x - 1, y));
165165
#elif ANDROID
166166
g.DrawLine(loc.X + x, y, loc.X + size - x - 1, y, p);
167167
#endif
168-
y--;
169-
}
170-
break;
171-
172-
case TriangleDirection.Down:
173-
loc.X -= size / 2 - 1;
174-
y--;
175-
for (x = 0; x < size / 2; x++)
176-
{
168+
y--;
169+
}
170+
break;
171+
172+
case TriangleDirection.Down:
173+
loc.X -= size / 2 - 1;
174+
y--;
175+
for (x = 0; x < size / 2; x++)
176+
{
177177
#if WINFORM || WPF
178-
g.DrawLine(p, new Point(loc.X + x, y), new Point(loc.X + size - x, y));
178+
g.DrawLine(p, new Point(loc.X + x, y), new Point(loc.X + size - x, y));
179179
#elif ANDROID
180180
g.DrawLine(loc.X + x, y, loc.X + size - x, y, p);
181181
#endif
182-
y++;
183-
}
184-
break;
182+
y++;
183+
}
184+
break;
185185

186-
case TriangleDirection.Left:
187-
loc.Y -= size / 2;
188-
for (y = 0; y < size / 2; y++)
189-
{
186+
case TriangleDirection.DownFilter:
187+
loc.X -= size / 2;
188+
loc.Y -= size / 2;
189+
190+
// Draw the outline of the funnel
191+
// Upper wide opening
192+
int topWidth = (int)size;
193+
int bottomWidth = (int)(size / 4);
194+
int funnelHeight = (int)(size / 2);
195+
int neckHeight = (int)(size / 2 + 1); // Extended funnel neck
196+
197+
#if WINFORM || WPF
198+
// Draw four side lines of the funnel to create a hollow effect
199+
// Top left to bottom left
200+
g.DrawLine(p, new Point(loc.X, loc.Y), new Point(loc.X + (topWidth - bottomWidth) / 2, loc.Y + funnelHeight));
201+
// Top right to bottom right
202+
g.DrawLine(p, new Point(loc.X + topWidth, loc.Y), new Point(loc.X + (topWidth + bottomWidth) / 2, loc.Y + funnelHeight));
203+
// Bottom connecting line
204+
g.DrawLine(p, new Point(loc.X + (topWidth - bottomWidth) / 2, loc.Y + funnelHeight),
205+
new Point(loc.X + (topWidth + bottomWidth) / 2, loc.Y + funnelHeight));
206+
// Top connecting line (optional, for closing the top)
207+
g.DrawLine(p, new Point(loc.X, loc.Y), new Point(loc.X + topWidth, loc.Y));
208+
209+
// Draw extended neck lines
210+
Point neckTopLeft = new Point(loc.X + (topWidth - bottomWidth) / 2, loc.Y + funnelHeight);
211+
Point neckTopRight = new Point(loc.X + (topWidth + bottomWidth) / 2, loc.Y + funnelHeight);
212+
Point neckBottomLeft = new Point(neckTopLeft.X, neckTopLeft.Y + neckHeight);
213+
Point neckBottomRight = new Point(neckTopRight.X, neckTopRight.Y + neckHeight);
214+
215+
// Left neck line
216+
g.DrawLine(p, neckTopLeft, neckBottomLeft);
217+
// Right neck line
218+
g.DrawLine(p, neckTopRight, neckBottomRight);
219+
220+
// Draw square dot (1x1 pixel) at bottom left corner
221+
Point dotPosition = new Point(neckBottomLeft.X - 3, neckBottomLeft.Y - 1);
222+
#endif
223+
224+
#if WPF
225+
// Keep original for WPF platform
226+
g.DrawRectangle(RGBrushes.Black, p, new Rectangle(dotPosition.X, dotPosition.Y, 1, 1));
227+
#elif WINFORM
228+
g.DrawRectangle(p, dotPosition.X, dotPosition.Y, 1, 1);
229+
#elif ANDROID
230+
231+
#endif
232+
break;
233+
case TriangleDirection.Left:
234+
loc.Y -= size / 2;
235+
for (y = 0; y < size / 2; y++)
236+
{
190237
#if WINFORM || WPF
191-
g.DrawLine(p, new Point(x, loc.Y + y), new Point(x, loc.Y + size - y - 1));
238+
g.DrawLine(p, new Point(x, loc.Y + y), new Point(x, loc.Y + size - y - 1));
192239
#elif ANDROID
193240
g.DrawLine(x, loc.Y + y, x, loc.Y + size - y - 1, p);
194241
#endif
195-
x--;
196-
}
197-
break;
198-
199-
case TriangleDirection.Right:
200-
loc.Y -= size / 2;
201-
for (y = 0; y < size / 2; y++)
202-
{
242+
x--;
243+
}
244+
break;
245+
246+
case TriangleDirection.Right:
247+
loc.Y -= size / 2;
248+
for (y = 0; y < size / 2; y++)
249+
{
203250
#if WINFORM || WPF
204-
g.DrawLine(p, new Point(x, loc.Y + y), new Point(x, loc.Y + size - y - 1));
251+
g.DrawLine(p, new Point(x, loc.Y + y), new Point(x, loc.Y + size - y - 1));
205252
#elif ANDROID
206253
g.DrawLine(x, loc.Y + y, x, loc.Y + size - y - 1, p);
207254
#endif
208-
x++;
209-
}
210-
break;
255+
x++;
256+
}
257+
break;
211258

212-
}
213-
}
259+
}
260+
}
214261

215262
#endregion
216263

ReoGrid/Data/AutoFilter.cs

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121

2222
#if WINFORM || ANDROID
2323
using RGFloat = System.Single;
24+
using RGPoint = unvell.ReoGrid.Graphics.Point;
2425
#elif WPF
2526
using RGFloat = System.Double;
27+
using RGPoint = unvell.ReoGrid.Graphics.Point;
28+
using System.Windows.Controls;
2629
#elif iOS
2730
using RGFloat = System.Double;
2831
#endif // WINFORM
@@ -110,25 +113,25 @@ public void Apply()
110113
if (columnHeader == null) { c++; continue; }
111114

112115
var columnFilterBody = columnHeader.Body as AutoColumnFilterBody;
113-
if (columnFilterBody == null || columnFilterBody.IsSelectAll)
116+
if (columnFilterBody == null || columnFilterBody.IsSelectAll == true)
114117
{
115118
c++; continue;
116119
}
117120

118121
var cell = this.Worksheet.GetCell(r, c);
119-
if (cell == null) { c++; continue; }
122+
//if (cell == null) { c++; continue; }
120123

121-
var text = cell.DisplayText;
122-
if (string.IsNullOrEmpty(text)) text = LanguageResource.Filter_Blanks;
124+
var text = cell == null ? string.Empty : cell.DisplayText;
125+
if (string.IsNullOrEmpty(text)) text = LanguageResource.Filter_Blanks;
123126

124127
if (!columnFilterBody.SelectedTextItems.Contains(text))
125128
{
126129
// hide the row
127130
return false;
128131
}
129132

130-
c += cell.Colspan;
131-
}
133+
c += cell == null ? 1 : cell.Colspan;
134+
}
132135

133136
// show the row
134137
return true;
@@ -245,23 +248,29 @@ public void OnPaint(CellDrawingContext dc, Size headerSize)
245248
{
246249
Rectangle bounds = GetColumnFilterButtonRect(headerSize);
247250

248-
SolidColor color1 = controlStyle.GetColHeadStartColor(isHover: false, isInvalid: false,
249-
isSelected: IsDropdown, isFullSelected: false);
251+
SolidColor color1 = controlStyle.GetColHeadStartColor(isHover: false, isInvalid: false,
252+
isSelected: IsDropdown, isFullSelected: !(IsSelectAll == true));
250253

251-
SolidColor color2 = controlStyle.GetColHeadEndColor(isHover: false, isInvalid: false,
252-
isSelected: IsDropdown, isFullSelected: false);
254+
SolidColor color2 = controlStyle.GetColHeadEndColor(isHover: false, isInvalid: false,
255+
isSelected: IsDropdown, isFullSelected: !(IsSelectAll == true));
253256

254-
var g = dc.Graphics;
257+
var g = dc.Graphics;
255258

256259
g.FillRectangleLinear(color1, color2, 90f, bounds);
257260

258261
g.DrawRectangle(bounds, unvell.ReoGrid.Rendering.StaticResources.SystemColor_ControlDark);
259262

260-
unvell.Common.GraphicsToolkit.FillTriangle(dc.Graphics.PlatformGraphics,
261-
Math.Min(7 * dc.Worksheet.renderScaleFactor, 7.0f),
262-
new Point(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2),
263-
unvell.Common.GraphicsToolkit.TriangleDirection.Down);
264-
}
263+
if (IsSelectAll == true)
264+
Common.GraphicsToolkit.FillTriangle(dc.Graphics.PlatformGraphics,
265+
Math.Min(7 * dc.Worksheet.renderScaleFactor, 7.0f),
266+
new Point(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2),
267+
Common.GraphicsToolkit.TriangleDirection.Down);
268+
else
269+
Common.GraphicsToolkit.FillTriangle(dc.Graphics.PlatformGraphics,
270+
Math.Min(7 * dc.Worksheet.renderScaleFactor, 7.0f),
271+
new Point(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2),
272+
Common.GraphicsToolkit.TriangleDirection.DownFilter);
273+
}
265274
}
266275

267276
/// <summary>
@@ -332,16 +341,19 @@ internal Rectangle GetColumnFilterButtonRect(Size size)
332341
/// </summary>
333342
public System.Windows.Forms.ContextMenuStrip ContextMenuStrip { get; set; }
334343
#elif WPF
335-
344+
/// <summary>
345+
/// Get or set the context menu of column filter.
346+
/// </summary>
347+
public ContextMenu ContextMenuStrip { get; set; }
336348
#endif
337349

338-
internal List<string> selectedTextItems = new List<string>();
350+
internal List<string> selectedTextItems = new List<string>();
339351

340352
/// <summary>
341353
/// Get or set whether or not this column is marked as SelectAll.
342354
/// The column is marked as SelectAll will be ignored during filter.
343355
/// </summary>
344-
public bool IsSelectAll { get; set; }
356+
public bool? IsSelectAll { get; set; }
345357

346358
private TextFilterCollection textItemsCollection;
347359

@@ -421,7 +433,7 @@ public void Add(string item)
421433
if (!this.columnFilter.selectedTextItems.Contains(item))
422434
{
423435
this.columnFilter.selectedTextItems.Add(item);
424-
this.columnFilter.IsSelectAll = false;
436+
//this.columnFilter.IsSelectAll = false;
425437
}
426438
}
427439

@@ -431,7 +443,7 @@ public void Add(string item)
431443
public void Clear()
432444
{
433445
this.columnFilter.selectedTextItems.Clear();
434-
columnFilter.IsSelectAll = false;
446+
//columnFilter.IsSelectAll = false;
435447
}
436448

437449
/// <summary>
@@ -477,7 +489,7 @@ public bool IsReadOnly
477489
/// <returns>true if item exist and has been removed successfully</returns>
478490
public bool Remove(string item)
479491
{
480-
this.columnFilter.IsSelectAll = false;
492+
//this.columnFilter.IsSelectAll = false;
481493
return this.columnFilter.selectedTextItems.Remove(item);
482494
}
483495

@@ -488,7 +500,7 @@ public bool Remove(string item)
488500
public void AddRange(IEnumerable<string> items)
489501
{
490502
this.columnFilter.selectedTextItems.AddRange(items);
491-
this.columnFilter.IsSelectAll = false;
503+
//this.columnFilter.IsSelectAll = false;
492504
}
493505
}
494506
#endregion // TextFilterCollection
@@ -506,11 +518,11 @@ public List<string> GetDistinctItems()
506518
int maxRow = this.ColumnHeader.Worksheet.MaxContentRow;
507519

508520
this.ColumnHeader.Worksheet.IterateCells(this.autoFilter.ApplyRange.Row,
509-
this.ColumnHeader.Index, this.autoFilter.ApplyRange.Rows, 1, true,
521+
this.ColumnHeader.Index, this.autoFilter.ApplyRange.Rows, 1, false,
510522
(r, c, cell) =>
511-
{
512-
var str = cell.DisplayText;
513-
if (string.IsNullOrEmpty(str)) str = LanguageResource.Filter_Blanks;
523+
{
524+
var str = cell == null ? string.Empty : cell.DisplayText;
525+
if (string.IsNullOrEmpty(str)) str = LanguageResource.Filter_Blanks;
514526

515527
if (!items.Contains(str))
516528
{
@@ -544,7 +556,7 @@ public void OnDataChange(int startRow, int endRow)
544556
/// </summary>
545557
public event EventHandler FilterButtonPressed;
546558

547-
internal bool RaiseFilterButtonPress(AutoColumnFilterBody headerBody, Point point)
559+
internal bool RaiseFilterButtonPress(AutoColumnFilterBody headerBody, RGPoint point)
548560
{
549561
if (headerBody.ColumnHeader == null) return false;
550562

@@ -562,11 +574,9 @@ internal bool RaiseFilterButtonPress(AutoColumnFilterBody headerBody, Point poin
562574
#if WINFORM
563575
unvell.ReoGrid.WinForm.ColumnFilterContextMenu.ShowFilterPanel(headerBody, (System.Drawing.Point)point);
564576
#elif WPF
565-
var ctx = new System.Windows.Controls.ContextMenu();
566-
ctx.Items.Add(new System.Windows.Controls.MenuItem() { Header = "Item" });
567-
ctx.IsOpen = true;
577+
unvell.ReoGrid.WPF.ColumnFilterContextMenu.ShowFilterPanel(headerBody, point);
568578
#endif // WPF
569-
return true;
579+
return true;
570580
}
571581

572582
return false;

0 commit comments

Comments
 (0)