- [DEVEXPRESS] Hỗ trợ tìm kiếm highlight không dấu và không khoảng cách trên Gridview Filter
- [C#] Chia sẻ source code phần mềm Image Downloader tải hàng loạt hình ảnh từ danh sách link url
- [C#] Chụp hình và quay video từ camera trên winform
- [C#] Chia sẽ full source code tách file Pdf thành nhiều file với các tùy chọn
- Giới thiệu về Stock Tracker Widget - Công cụ theo dõi cổ phiếu và cảnh báo giá tăng giảm bằng C# và WPF
- [VB.NET] Chia sẻ công cụ nhập số tiền tự động định dạng tiền tệ Việt Nam
- [VB.NET] Hướng dẫn fill dữ liệu từ winform vào Microsoft word
- [VB.NET] Hướng dẫn chọn nhiều dòng trên Datagridview
- Hướng Dẫn Đăng Nhập Nhiều Tài Khoản Zalo Trên Máy Tính Cực Kỳ Đơn Giản
- [C#] Chia sẻ source code phần mềm đếm số trang tập tin file PDF
- [C#] Cách Sử Dụng DeviceId trong C# Để Tạo Khóa Cho Ứng Dụng
- [SQLSERVER] Loại bỏ Restricted User trên database MSSQL
- [C#] Hướng dẫn tạo mã QRcode Style trên winform
- [C#] Hướng dẫn sử dụng temp mail service api trên winform
- [C#] Hướng dẫn tạo mã thanh toán VietQR Pay không sử dụng API trên winform
- [C#] Hướng Dẫn Tạo Windows Service Đơn Giản Bằng Topshelf
- [C#] Chia sẻ source code đọc dữ liệu từ Google Sheet trên winform
- [C#] Chia sẻ source code tạo mã QR MOMO đa năng Winform
- [C#] Chia sẻ source code phần mềm lên lịch tự động chạy ứng dụng Scheduler Task Winform
- [C#] Hướng dẫn download file từ Minio Server Winform
[DEVEXPRESS] Hỗ trợ tìm kiếm highlight không dấu và không khoảng cách trên Gridview Filter
Xin chào các bạn, bài viết hôm nay mình tiếp tục chia sẻ các bạn source code cách tìm kiếm không dấu và không khoảng cách trên Gridview Devexpress.
[DEVEXPRESS] Tìm kiếm không dấu và không khoảng cách trên Gridview Filter
Dưới đây là hình ảnh demo ứng dụng:
Như hình ảnh trên là mình demo 2 component: GridView và SearchLookupEdit, các bạn thấy mình gõ như sau:
- Từ khóa tìm kiếm: "nguphuc" -> "Ngũ phúc"
- Từ khóa tìm kiếm: "honai3" -> "Hố Nai 3"
Với tính năng tìm kiếm này giúp cho người dùng tìm kiếm 1 cách nhanh chóng và dễ dàng trên main form các bạn chỉ cần gọi hàm enable hoặc disable tính năng này thôi, sử dụng rất linh động.
Trên hàm, mình cũng viết sẵn cho các bạn có thể truyền màu sắc highlight text: background color và foreground color.
Video Demo:
Source code Frmmain.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using DevExpress.Data;
using DevExpress.XtraEditors;
using GridViewFiltering;
namespace GridViewFiltering
{
public partial class MainForm : XtraForm {
public MainForm() {
InitializeComponent();
}
private async void MainForm_Load(object sender, EventArgs e) {
var data = await new EmployeeDataExample.EmployeeData().GetEmployeesAsync();
this.GridControl.DataSource = data;
searchLookUpEdit1.Properties.DataSource = data;
searchLookUpEdit1.Properties.ValueMember = "Name";
searchLookUpEdit1.Properties.DisplayMember = "Name";
}
private void btnEnable_Click(object sender, EventArgs e)
{
GridSearchHighlighter.EnableSmartHighlight(this.GridView, Color.Pink, Color.OrangeRed, 30);
GridSearchHighlighter.EnableSmartHighlight(this.searchLookUpEdit1View, Color.Blue);
XtraMessageBox.Show("Đã bật");
}
private void btnDisable_Click(object sender, EventArgs e)
{
GridSearchHighlighter.DisableSmartHighlight(this.GridView);
GridSearchHighlighter.DisableSmartHighlight(this.searchLookUpEdit1View);
XtraMessageBox.Show("Đã tắt");
}
}
}
Source code GridSearchHighlighter.cs
using System;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.Data;
using DevExpress.Data.Filtering;
using DevExpress.Data.Filtering.Helpers;
using DevExpress.Utils.Extensions;
namespace GridViewFiltering
{
public enum HighlightPreset
{
Yellow,
LightBlue,
LightGreen,
LightPink,
LightOrange,
LightPurple,
LightCyan,
LightCoral,
LightSalmon,
LightSkyBlue,
LightSeaGreen,
LightSteelBlue,
Gold,
Silver,
Rose,
Lavender,
Mint,
Peach,
Apricot,
Cream
}
public class HighlightColorScheme
{
public Color BackgroundColor { get; set; }
public Color ForegroundColor { get; set; }
public int BackgroundOpacity { get; set; } = 50;
public HighlightColorScheme(Color backgroundColor, Color foregroundColor, int backgroundOpacity = 50)
{
BackgroundColor = backgroundColor;
ForegroundColor = foregroundColor;
BackgroundOpacity = Math.Max(0, Math.Min(255, backgroundOpacity));
}
public Color GetBackgroundColor()
{
return Color.FromArgb(BackgroundOpacity, BackgroundColor);
}
}
public static class GridSearchHighlighter
{
private static readonly Dictionary<object, HighlightColorScheme> _highlightSchemes = new Dictionary<object, HighlightColorScheme>();
private static readonly Dictionary<HighlightPreset, HighlightColorScheme> _presetSchemes = new Dictionary<HighlightPreset, HighlightColorScheme>();
private static bool _isCustomFunctionRegistered = false;
static GridSearchHighlighter()
{
InitializePresetSchemes();
RegisterCustomFunction();
}
private static void RegisterCustomFunction()
{
if (!_isCustomFunctionRegistered)
{
try
{
CriteriaOperator.RegisterCustomFunction(new RemoveDiacriticsFunction());
_isCustomFunctionRegistered = true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Failed to register RemoveDiacriticsFunction: {ex.Message}");
}
}
}
private static void InitializePresetSchemes()
{
_presetSchemes[HighlightPreset.Yellow] = new HighlightColorScheme(Color.Yellow, Color.Black, 60);
_presetSchemes[HighlightPreset.LightBlue] = new HighlightColorScheme(Color.LightBlue, Color.DarkBlue, 70);
_presetSchemes[HighlightPreset.LightGreen] = new HighlightColorScheme(Color.LightGreen, Color.DarkGreen, 70);
_presetSchemes[HighlightPreset.LightPink] = new HighlightColorScheme(Color.LightPink, Color.DarkMagenta, 70);
_presetSchemes[HighlightPreset.LightOrange] = new HighlightColorScheme(Color.Orange, Color.DarkOrange, 60);
_presetSchemes[HighlightPreset.LightPurple] = new HighlightColorScheme(Color.Plum, Color.Purple, 70);
_presetSchemes[HighlightPreset.LightCyan] = new HighlightColorScheme(Color.LightCyan, Color.DarkCyan, 70);
_presetSchemes[HighlightPreset.LightCoral] = new HighlightColorScheme(Color.LightCoral, Color.DarkRed, 70);
_presetSchemes[HighlightPreset.LightSalmon] = new HighlightColorScheme(Color.LightSalmon, Color.DarkRed, 70);
_presetSchemes[HighlightPreset.LightSkyBlue] = new HighlightColorScheme(Color.LightSkyBlue, Color.DarkBlue, 70);
_presetSchemes[HighlightPreset.LightSeaGreen] = new HighlightColorScheme(Color.LightSeaGreen, Color.DarkGreen, 70);
_presetSchemes[HighlightPreset.LightSteelBlue] = new HighlightColorScheme(Color.LightSteelBlue, Color.DarkBlue, 70);
_presetSchemes[HighlightPreset.Gold] = new HighlightColorScheme(Color.Gold, Color.DarkGoldenrod, 60);
_presetSchemes[HighlightPreset.Silver] = new HighlightColorScheme(Color.Silver, Color.DarkSlateGray, 70);
_presetSchemes[HighlightPreset.Rose] = new HighlightColorScheme(Color.MistyRose, Color.DarkRed, 70);
_presetSchemes[HighlightPreset.Lavender] = new HighlightColorScheme(Color.Lavender, Color.Purple, 70);
_presetSchemes[HighlightPreset.Mint] = new HighlightColorScheme(Color.MintCream, Color.DarkGreen, 70);
_presetSchemes[HighlightPreset.Peach] = new HighlightColorScheme(Color.PeachPuff, Color.DarkOrange, 70);
_presetSchemes[HighlightPreset.Apricot] = new HighlightColorScheme(Color.PeachPuff, Color.Orange, 70);
_presetSchemes[HighlightPreset.Cream] = new HighlightColorScheme(Color.Crimson, Color.Black, 60);
}
public static void EnableSmartHighlight(object control, Color? highlightColor = null)
{
Color color = highlightColor ?? Color.Yellow;
var scheme = new HighlightColorScheme(color, Color.Black, 60);
EnableSmartHighlight(control, scheme);
}
public static void EnableSmartHighlight(object control, HighlightPreset preset)
{
if (!_presetSchemes.ContainsKey(preset))
throw new ArgumentException($"Preset {preset} not found");
var scheme = _presetSchemes[preset];
EnableSmartHighlight(control, scheme);
}
public static void EnableSmartHighlight(object control, Color backgroundColor, Color foregroundColor, int backgroundOpacity = 50)
{
var scheme = new HighlightColorScheme(backgroundColor, foregroundColor, backgroundOpacity);
EnableSmartHighlight(control, scheme);
}
public static void EnableSmartHighlight(object control, HighlightColorScheme scheme)
{
if (control is GridView gridView)
{
EnableForGridView(gridView, scheme);
}
else if (control is GridLookUpEdit gridLookUp)
{
EnableForGridView(gridLookUp.Properties.View, scheme);
}
else if (control is SearchLookUpEdit searchLookUp)
{
EnableForGridView(searchLookUp.Properties.View, scheme);
}
else
{
throw new ArgumentException("Control must be GridView, GridLookUpEdit, or SearchLookUpEdit");
}
}
public static void DisableSmartHighlight(object control)
{
if (control is GridView gridView)
{
DisableForGridView(gridView);
}
else if (control is GridLookUpEdit gridLookUp)
{
DisableForGridView(gridLookUp.Properties.View);
}
else if (control is SearchLookUpEdit searchLookUp)
{
DisableForGridView(searchLookUp.Properties.View);
}
}
private static void EnableForGridView(GridView gridView, HighlightColorScheme scheme)
{
if (gridView == null) return;
gridView.OptionsFind.HighlightFindResults = false;
gridView.CustomDrawCell -= GridView_CustomDrawCell;
gridView.CustomDrawCell += GridView_CustomDrawCell;
gridView.SubstituteFilter -= GridView_SubstituteFilter;
gridView.SubstituteFilter += GridView_SubstituteFilter;
_highlightSchemes[gridView] = scheme;
}
private static void DisableForGridView(GridView gridView)
{
if (gridView == null) return;
gridView.CustomDrawCell -= GridView_CustomDrawCell;
gridView.SubstituteFilter -= GridView_SubstituteFilter;
gridView.OptionsFind.HighlightFindResults = true;
_highlightSchemes.Remove(gridView);
}
private static void GridView_CustomDrawCell(object sender, RowCellCustomDrawEventArgs e)
{
var gridView = sender as GridView;
if (gridView == null || !_highlightSchemes.ContainsKey(gridView)) return;
if (gridView.IsFilterRow(e.RowHandle))
return;
string cellText = e.DisplayText;
string searchText = null;
if (e.Column != null && e.Column.FilterInfo != null && e.Column.FilterInfo.Value != null)
{
searchText = e.Column.FilterInfo.Value.ToString();
}
if (string.IsNullOrEmpty(searchText))
{
searchText = gridView.FindFilterText;
}
if (string.IsNullOrEmpty(searchText) || string.IsNullOrEmpty(cellText))
return;
var matchRanges = FindAllMatchPositions(cellText, searchText);
if (matchRanges.Count == 0)
return;
e.Handled = true;
var g = e.Graphics;
var font = e.Appearance.Font;
var textColor = e.Appearance.GetForeColor();
var rect = e.Bounds;
float x = rect.X;
var stringFormat = new StringFormat(StringFormatFlags.MeasureTrailingSpaces);
var scheme = _highlightSchemes[gridView];
int lastIdx = 0;
foreach (var (start, length) in matchRanges)
{
if (start > lastIdx)
{
string before = cellText.Substring(lastIdx, start - lastIdx);
var beforeSize = g.MeasureString(before, font, int.MaxValue, stringFormat);
g.DrawString(before, font, new SolidBrush(textColor), x, rect.Y, stringFormat);
x += beforeSize.Width;
}
string match = cellText.Substring(start, length);
var matchSize = g.MeasureString(match, font, int.MaxValue, stringFormat);
var highlightRect = new RectangleF(x, rect.Y, matchSize.Width, rect.Height);
g.FillRectangle(new SolidBrush(scheme.GetBackgroundColor()), highlightRect);
g.DrawString(match, font, new SolidBrush(scheme.ForegroundColor), x, rect.Y, stringFormat);
x += matchSize.Width;
lastIdx = start + length;
}
if (lastIdx < cellText.Length)
{
string after = cellText.Substring(lastIdx);
g.DrawString(after, font, new SolidBrush(textColor), x, rect.Y, stringFormat);
}
}
private static List<(int start, int length)> FindAllMatchPositions(string original, string search)
{
if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(search))
return new List<(int, int)>();
var normOriginal = RemoveDiacriticsAndSpaces(original).ToLowerInvariant();
var normSearch = RemoveDiacriticsAndSpaces(search).ToLowerInvariant();
if (string.IsNullOrEmpty(normSearch))
return new List<(int, int)>();
var result = new List<(int, int)>();
var mapping = CreateCharacterMapping(original);
int normIdx = 0;
while (normIdx <= normOriginal.Length - normSearch.Length)
{
if (normOriginal.Substring(normIdx, normSearch.Length) == normSearch)
{
if (normIdx < mapping.Count)
{
int realStart = mapping[normIdx];
int normEnd = normIdx + normSearch.Length;
int realEnd = (normEnd < mapping.Count) ? mapping[normEnd] : original.Length;
var matchRange = TrimWhitespaceFromMatch(original, realStart, realEnd - realStart);
if (matchRange.length > 0)
{
result.Add(matchRange);
}
}
normIdx += normSearch.Length;
}
else
{
normIdx++;
}
}
return result;
}
public static string RemoveDiacriticsAndSpaces(string src)
{
if (src == null) return string.Empty;
var sb = new StringBuilder();
foreach (var c in src.Normalize(NormalizationForm.FormKD))
{
var cat = CharUnicodeInfo.GetUnicodeCategory(c);
if (cat != UnicodeCategory.NonSpacingMark &&
cat != UnicodeCategory.SpacingCombiningMark &&
cat != UnicodeCategory.EnclosingMark &&
!char.IsWhiteSpace(c))
{
if (c == 'đ') sb.Append('d');
else if (c == 'Đ') sb.Append('D');
else sb.Append(c);
}
}
return sb.ToString();
}
private static (int start, int length) TrimWhitespaceFromMatch(string original, int start, int length)
{
if (start < 0 || start >= original.Length || length <= 0)
return (-1, 0);
int end = start + length;
if (end > original.Length)
end = original.Length;
int realStart = start;
while (realStart < end && char.IsWhiteSpace(original[realStart]))
{
realStart++;
}
int realEnd = end;
while (realEnd > realStart && char.IsWhiteSpace(original[realEnd - 1]))
{
realEnd--;
}
int realLength = realEnd - realStart;
return realLength > 0 ? (realStart, realLength) : (-1, 0);
}
private static List<int> CreateCharacterMapping(string original)
{
var mapping = new List<int>();
for (int i = 0; i < original.Length; i++)
{
char c = original[i];
var cat = CharUnicodeInfo.GetUnicodeCategory(c);
if (cat != UnicodeCategory.NonSpacingMark &&
cat != UnicodeCategory.SpacingCombiningMark &&
cat != UnicodeCategory.EnclosingMark &&
!char.IsWhiteSpace(c))
{
mapping.Add(i);
}
}
return mapping;
}
private static int FindRealIndex(string original, string normalized, int normIndex)
{
if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(normalized) || normIndex < 0)
return -1;
var mapping = CreateCharacterMapping(original);
if (normIndex >= 0 && normIndex < mapping.Count)
return mapping[normIndex];
if (normIndex >= mapping.Count)
return original.Length;
return -1;
}
public static HighlightPreset[] GetAvailablePresets()
{
return (HighlightPreset[])Enum.GetValues(typeof(HighlightPreset));
}
public static HighlightColorScheme GetPresetScheme(HighlightPreset preset)
{
if (!_presetSchemes.ContainsKey(preset))
throw new ArgumentException($"Preset {preset} not found");
return _presetSchemes[preset];
}
public static void ChangeHighlightColor(object control, HighlightPreset preset)
{
if (!_highlightSchemes.ContainsKey(control))
throw new InvalidOperationException("Control is not registered for highlighting");
var scheme = GetPresetScheme(preset);
_highlightSchemes[control] = scheme;
}
public static void ChangeHighlightColor(object control, Color backgroundColor, Color foregroundColor, int backgroundOpacity = 50)
{
if (!_highlightSchemes.ContainsKey(control))
throw new InvalidOperationException("Control is not registered for highlighting");
var scheme = new HighlightColorScheme(backgroundColor, foregroundColor, backgroundOpacity);
_highlightSchemes[control] = scheme;
}
public static HighlightColorScheme GetCurrentScheme(object control)
{
if (!_highlightSchemes.ContainsKey(control))
return null;
return _highlightSchemes[control];
}
public static void EnsureCustomFunctionRegistered()
{
RegisterCustomFunction();
}
public static bool IsCustomFunctionRegistered()
{
return _isCustomFunctionRegistered;
}
private static void GridView_SubstituteFilter(object sender, SubstituteFilterEventArgs e)
{
e.Filter = GridFilterSubstitutor.Substitute(e.Filter);
}
}
public class GridFilterSubstitutor : ClientCriteriaLazyPatcherBase.AggregatesCommonProcessingBase
{
private static CriteriaOperator WrapIntoCustomFunction(CriteriaOperator param)
{
return new FunctionOperator(FunctionOperatorType.Custom,
new ConstantValue("RemoveDiacritics"), param);
}
public static CriteriaOperator Substitute(CriteriaOperator source)
{
return new GridFilterSubstitutor().Process(source);
}
public override CriteriaOperator Visit(FunctionOperator theOperator)
{
if (theOperator.OperatorType == FunctionOperatorType.StartsWith ||
theOperator.OperatorType == FunctionOperatorType.EndsWith ||
theOperator.OperatorType == FunctionOperatorType.Contains)
return new FunctionOperator(theOperator.OperatorType,
WrapIntoCustomFunction(theOperator.Operands[0]),
WrapIntoCustomFunction(theOperator.Operands[1]));
return base.Visit(theOperator);
}
}
public sealed class RemoveDiacriticsFunction : ICustomFunctionOperator
{
public object Evaluate(params object[] operands)
{
var src = (string)operands[0];
if (src == null)
return string.Empty;
var sb = new StringBuilder();
foreach (var c in src.Normalize(NormalizationForm.FormKD))
{
if (!IsDiacritic(c) && !char.IsWhiteSpace(c))
{
if (c == 'đ') sb.Append('d');
else if (c == 'Đ') sb.Append('D');
else sb.Append(c);
}
}
return sb.ToString();
}
private bool IsDiacritic(char symbol)
{
var category = CharUnicodeInfo.GetUnicodeCategory(symbol);
return category == UnicodeCategory.NonSpacingMark ||
category == UnicodeCategory.SpacingCombiningMark ||
category == UnicodeCategory.EnclosingMark;
}
public string Name => "RemoveDiacritics";
public Type ResultType(params Type[] operands) => typeof(string);
}
}
Thanks for watching!