{"id":10095,"date":"2026-03-03T10:40:05","date_gmt":"2026-03-03T09:40:05","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=10095"},"modified":"2026-03-03T10:49:09","modified_gmt":"2026-03-03T09:49:09","slug":"xaml-uniconta-lookupgridcolumn","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/xaml-uniconta-lookupgridcolumn\/","title":{"rendered":"XAML \/ Uniconta LookupGridColumn"},"content":{"rendered":"\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"text\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">&lt;local:LookUpGridColumn x:Name=\"colInvoiceSelect\" FieldName=\"InvoiceSurcharge\" Header=\"Invoice\"  Width=\"120\" ReadOnly=\"false\" AllowEditing=\"true\"\/>\ncolInvoiceSelect.SetOptions&lt;CreditorInvoiceClient>(invoiceItemSource);<\/pre><\/div>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"xml\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">&lt;dxg:GridColumn x:Class=\"Views.Controls.LookUpGridColumn\"\n             xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n             xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\n             xmlns:dxg=\"http:\/\/schemas.devexpress.com\/winfx\/2008\/xaml\/grid\"\n                xmlns:controls=\"clr-namespace:Views.Controls\"\n                x:Name=\"myColumn\"\n                >\n    &lt;dxg:GridColumn.Resources>\n        &lt;controls:DisplayTextConverter x:Key=\"DisplayTextConverter\"\/>\n    &lt;\/dxg:GridColumn.Resources>\n    &lt;dxg:GridColumn.EditSettings>\n        &lt;dxg:LookUpEditSettings x:Name=\"LookupCombo\" ValueMember=\"KeyStr\" DisplayMember=\"KeyName\" AutoPopulateColumns=\"False\" FindMode=\"Always\" FilterCondition=\"Contains\" ImmediatePopup=\"True\"\n                                IsCaseSensitiveFilter=\"False\" \n                                PopupWidth=\"600\"                               \n                                ShowPopupIfReadOnly=\"False\"\n                                ItemsSource=\"{Binding ElementName=myColumn,Path=LookupItems}\"\n                                >\n            &lt;dxg:LookUpEditSettings.StyleSettings>\n                &lt;dxg:SearchLookUpEditStyleSettings AllowGrouping=\"False\"\/>\n            &lt;\/dxg:LookUpEditSettings.StyleSettings>\n            &lt;dxg:LookUpEditSettings.PopupContentTemplate>\n                &lt;ControlTemplate>\n                    &lt;dxg:GridControl Name=\"PART_GridControl\">\n                        &lt;dxg:GridControl.Columns>\n                            &lt;dxg:GridColumn FieldName=\"KeyStr\" Header=\"{Binding Converter={StaticResource GlobalLocalizationValueConverter},ConverterParameter=Key}\" Width=\"150\"\/>\n                            &lt;dxg:GridColumn FieldName=\"KeyName\" Header=\"{Binding Converter={StaticResource GlobalLocalizationValueConverter},ConverterParameter=ItemName}\" Width=\"400\" \/>\n                        &lt;\/dxg:GridControl.Columns>\n                        &lt;dxg:GridControl.View>\n                            &lt;dxg:TableView AutoWidth=\"False\" SearchStringToFilterCriteria=\"CurrentTableView_SearchStringToFilterCriteria\" SearchPanelHighlightResults=\"True\" SearchDelay=\"50\" x:Name=\"CurrentTableView\"\/>\n                        &lt;\/dxg:GridControl.View>\n                    &lt;\/dxg:GridControl>\n                &lt;\/ControlTemplate>\n            &lt;\/dxg:LookUpEditSettings.PopupContentTemplate>\n        &lt;\/dxg:LookUpEditSettings>\n    &lt;\/dxg:GridColumn.EditSettings>\n&lt;\/dxg:GridColumn>\n<\/pre><\/div>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"csharp\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Globalization;\nusing System.Linq;\nusing System.Windows.Data;\nusing DevExpress.Data.Filtering;\nusing DevExpress.Xpf.Grid;\nusing ViewModels.Models;\nusing Uniconta.API.System;\nusing Uniconta.Common;\n\nnamespace Views.Controls\n{\n    public partial class LookUpGridColumn : GridColumn\n    {\n        public LookUpGridColumn() : base()\n        {\n            Loaded += LookUpGridColumn_Loaded;\n            InitializeComponent();\n        }\n\n        public delegate CriteriaOperator FilterHandler(object row);\n        public FilterHandler OnFilter { get; set; }\n\n        public delegate void ChangeHandler(object row, string fieldName, object value, string text);\n        public ChangeHandler OnChange { get; set; }\n\n        private void LookUpGridColumn_Loaded(object sender, System.Windows.RoutedEventArgs e)\n        {\n            if (View is TableView tableView)\n            {\n                tableView.EditorShowMode = DevExpress.Xpf.Core.EditorShowMode.MouseDown;\n                tableView.CellValueChanged += TableView_CellValueChanged;\n\n                if (tableView.Parent is GridControl grid)\n                {\n                    grid.CurrentColumnChanged += Grid_CurrentColumnChanged;\n                    grid.CurrentItemChanged += Grid_CurrentItemChanged;\n                }\n            }\n        }\n\n        private void TableView_CellValueChanged(object sender, CellValueChangedEventArgs e)\n        {\n            OnChange?.Invoke(e.Row, e.Column.FieldName, e.Value, GetText(e.Value));\n        }\n\n        public ObservableCollection&lt;object> LookupItems { get; set; } = new ObservableCollection&lt;object>();\n        public bool SelectionOnCurrenColum { get; private set; }\n        public bool SearchAlsoInKey = true;\n\n        private object currentRow;\n        private List&lt;IdKey> items;\n\n        public void InitControl&lt;T>(CrudAPI api, bool searchAlsoInKey = true) where T : class, UnicontaBaseEntity, IdKey, new()\n        {\n            SetOptions&lt;T>(api.LoadCache&lt;T>().Result);\n            this.SearchAlsoInKey = searchAlsoInKey;\n        }\n\n        public void SetOptions&lt;T>(IEnumerable&lt;IdKey> options) where T : UnicontaBaseEntity\n        {\n            items = options.Select((x, i) => new SimpleIdKey&lt;T>((T)x, x.KeyStr, x.KeyName, i)).Cast&lt;IdKey>().ToList();\n\n            LookupItems.Clear();\n            items.ForEach(i => LookupItems.Add(i));\n            var displayTextConverter = new DisplayTextConverter(GetText);\n            this.LookupCombo.DisplayTextConverter = displayTextConverter;\n        }\n\n        public string GetText(object key)\n        {\n            if (key == null)\n                return null;\n            return items.FirstOrDefault(x => x.KeyStr == key.ToString())?.KeyName;\n        }\n\n        private void Grid_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)\n        {\n            currentRow = e.NewItem;\n            SetFilter();\n        }\n\n        private void SetFilter()\n        {\n            if (SelectionOnCurrenColum)\n            {\n                LookupCombo.FilterCriteria = OnFilter?.Invoke(currentRow) ?? null;\n            }\n        }\n\n        private void Grid_CurrentColumnChanged(object sender, CurrentColumnChangedEventArgs e)\n        {\n            SelectionOnCurrenColum = (e.NewColumn == this);\n            currentRow = e.Source?.CurrentItem;\n            SetFilter();\n        }\n\n        private void CurrentTableView_SearchStringToFilterCriteria(object sender, SearchStringToFilterCriteriaEventArgs e)\n        {\n            if (e.SearchString != null)\n            {\n                CriteriaOperator filter = null;\n                var columns = new List&lt;string> { \"KeyName\" };\n                if (SearchAlsoInKey)\n                    columns.Add(\"KeyStr\");\n\n                columns.ForEach(c =>\n                {\n                    filter |= CriteriaOperator.Or(\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString)),\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString.Replace(\".\", \",\"))),\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString.Replace(\"*\", \"x\"))),\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString.Replace(\".\", \",\").Replace(\"*\", \"x\")))\n                        );\n                });\n\n                e.Filter = filter;\n            }\n        }\n    }\n\n    public class DisplayTextConverter : IValueConverter\n    {\n        public delegate string KeyToTextHandler(object key);\n        public KeyToTextHandler Converter;\n\n        public DisplayTextConverter(KeyToTextHandler converter)\n        {\n            Converter = converter;\n        }\n\n        public DisplayTextConverter()\n        {\n        }\n\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => Converter(value);\n        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) => value;\n    }\n}\n<\/pre><\/div>\n\n\n\n<p>Extended<\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"xml\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">&lt;dxg:GridColumn x:Class=\"Views.Controls.ExtendedLookUpGridColumn\"\n             xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n             xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\n             xmlns:dxg=\"http:\/\/schemas.devexpress.com\/winfx\/2008\/xaml\/grid\"\n                xmlns:controls=\"clr-namespace:Views.Controls\"\n                x:Name=\"myColumn\"\n                >\n    &lt;dxg:GridColumn.Resources>\n        &lt;controls:DisplayTextConverter x:Key=\"DisplayTextConverter\"\/>\n    &lt;\/dxg:GridColumn.Resources>\n    &lt;dxg:GridColumn.EditSettings>\n        &lt;dxg:LookUpEditSettings x:Name=\"LookupCombo\" ValueMember=\"KeyStr\" DisplayMember=\"KeyName\" AutoPopulateColumns=\"False\" FindMode=\"Always\" FilterCondition=\"Contains\" ImmediatePopup=\"True\"\n                                IsCaseSensitiveFilter=\"False\" \n                                PopupWidth=\"600\"\n                                ShowPopupIfReadOnly=\"False\"\n                                ItemsSource=\"{Binding ElementName=myColumn,Path=LookupItems}\"\n                                >\n            &lt;dxg:LookUpEditSettings.StyleSettings>\n                &lt;dxg:SearchLookUpEditStyleSettings AllowGrouping=\"False\"\/>\n            &lt;\/dxg:LookUpEditSettings.StyleSettings>\n            &lt;dxg:LookUpEditSettings.PopupContentTemplate>\n                &lt;ControlTemplate>\n                    &lt;dxg:GridControl Name=\"PART_GridControl\" ColumnsSource=\"{Binding ElementName=myColumn,Path=PopupColumns}\" AutoGenerateColumns=\"None\">\n                        &lt;!--&lt;dxg:GridControl.Columns>\n                            &lt;dxg:GridColumn FieldName=\"KeyStr\" Header=\"{Binding Converter={StaticResource GlobalLocalizationValueConverter}, ConverterParameter=Key}\" Width=\"150\"\/>\n                            &lt;dxg:GridColumn FieldName=\"KeyName\" Header=\"{Binding Converter={StaticResource GlobalLocalizationValueConverter}, ConverterParameter=Description}\" Width=\"400\" \/>\n                        &lt;\/dxg:GridControl.Columns>-->\n                        &lt;dxg:GridControl.View>\n                            &lt;dxg:TableView AutoWidth=\"False\" SearchStringToFilterCriteria=\"CurrentTableView_SearchStringToFilterCriteria\" SearchPanelHighlightResults=\"True\" SearchDelay=\"50\" x:Name=\"CurrentTableView\"\/>\n                        &lt;\/dxg:GridControl.View>\n                    &lt;\/dxg:GridControl>\n                &lt;\/ControlTemplate>\n            &lt;\/dxg:LookUpEditSettings.PopupContentTemplate>\n        &lt;\/dxg:LookUpEditSettings>\n    &lt;\/dxg:GridColumn.EditSettings>\n&lt;\/dxg:GridColumn>\n<\/pre><\/div>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"csharp\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing DevExpress.Data.Filtering;\nusing DevExpress.Xpf.Grid;\n\nnamespace Views.Controls\n{\n    public partial class ExtendedLookUpGridColumn : GridColumn\n    {\n        public ExtendedLookUpGridColumn() : base()\n        {\n            Loaded += LookUpGridColumn_Loaded;\n            InitializeComponent();\n        }\n\n        public delegate CriteriaOperator FilterHandler(object row);\n        public FilterHandler OnFilter { get; set; }\n\n        public delegate void ChangeHandler(object row, string fieldName, object value, string text);\n        public event ChangeHandler OnChange;\n\n        private void LookUpGridColumn_Loaded(object sender, System.Windows.RoutedEventArgs e)\n        {\n            if (View is TableView tableView)\n            {\n                tableView.EditorShowMode = DevExpress.Xpf.Core.EditorShowMode.MouseDown;\n                tableView.CellValueChanged += TableView_CellValueChanged;\n\n                if (tableView.Parent is GridControl grid)\n                {\n                    grid.CurrentColumnChanged += Grid_CurrentColumnChanged;\n                    grid.CurrentItemChanged += Grid_CurrentItemChanged;\n                }\n            }\n        }\n\n        private void TableView_CellValueChanged(object sender, CellValueChangedEventArgs e)\n        {\n            OnChange?.Invoke(e.Row, e.Column.FieldName, e.Value, GetText(e.Value));\n        }\n\n        public ObservableCollection&lt;GridColumn> PopupColumns { get; set; } = new ObservableCollection&lt;GridColumn>();\n\n        public void SetColumns(IEnumerable&lt;GridColumn> columns)\n        {\n            PopupColumns.Clear();\n            foreach (var column in columns)\n                PopupColumns.Add(column);\n        }\n\n        private ILookupDataSource DataSource;\n\n        public ObservableCollection&lt;object> LookupItems { get; set; } = new ObservableCollection&lt;object>();\n        public bool SelectionOnCurrenColum { get; private set; }\n\n        private object currentRow;\n\n        public void SetOptions&lt;T>(IEnumerable&lt;T> options, Func&lt;T, string> keySelector, Func&lt;T, string> nameSelector) where T : class\n        {\n            DataSource = new LookupDataSource&lt;T>(keySelector, nameSelector, options);\n\n            LookupItems.Clear();\n            foreach (var option in options)\n            {\n                LookupItems.Add(option);\n            }\n\n            LookupCombo.DisplayTextConverter = new DisplayTextConverter(GetText);\n        }\n\n        public string GetText(object key)\n        {\n            if (key == null)\n                return null;\n\n            var itemByKey = DataSource.Items.Select(x => new { Key = DataSource.GetKey(x), Name = DataSource.GetName(x) }).ToList();\n            var item = itemByKey.FirstOrDefault(x => x.Key?.ToString() == key?.ToString());\n            if (item == null) return \"\";\n            return item.Name;\n        }\n\n        private void Grid_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)\n        {\n            currentRow = e.NewItem;\n            SetFilter();\n        }\n\n        private void SetFilter()\n        {\n            if (SelectionOnCurrenColum)\n            {\n                LookupCombo.FilterCriteria = OnFilter?.Invoke(currentRow) ?? null;\n            }\n        }\n\n        private void Grid_CurrentColumnChanged(object sender, CurrentColumnChangedEventArgs e)\n        {\n            SelectionOnCurrenColum = (e.NewColumn == this);\n            currentRow = e.Source?.CurrentItem;\n            SetFilter();\n        }\n\n        private void CurrentTableView_SearchStringToFilterCriteria(object sender, SearchStringToFilterCriteriaEventArgs e)\n        {\n            if (e.SearchString != null &amp;&amp; PopupColumns != null)\n            {\n                CriteriaOperator filter = null;\n                var columns = PopupColumns.Select(x => x.FieldName).ToList();\n\n                columns.ForEach(c =>\n                {\n                    filter |= CriteriaOperator.Or(\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString)),\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString.Replace(\".\", \",\"))),\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString.Replace(\"*\", \"x\"))),\n                        CriteriaOperator.Parse(string.Format(\"Contains([{0}], '{1}')\", c, e.SearchString.Replace(\".\", \",\").Replace(\"*\", \"x\")))\n                        );\n                });\n\n                e.Filter = filter;\n            }\n        }\n    }\n\n    public interface ILookupDataSource\n    {\n        ObservableCollection&lt;object> Items { get; }\n        object GetKey(object o);\n        string GetName(object o);\n    }\n\n    public class LookupDataSource&lt;T> : ILookupDataSource where T : class\n    {\n        Func&lt;T, object> keySelector;\n        Func&lt;T, string> nameSelector;\n\n        public LookupDataSource(Func&lt;T, object> keySelector, Func&lt;T, string> nameSelector, IEnumerable&lt;T> items)\n        {\n            this.keySelector = keySelector;\n            this.nameSelector = nameSelector;\n            Items = new ObservableCollection&lt;object>(items);\n        }\n        public ObservableCollection&lt;object> Items { get; private set; }\n        object ILookupDataSource.GetKey(object o) => o is T v ? keySelector(v) : null;\n        string ILookupDataSource.GetName(object o) => o is T v ? nameSelector(v) : \"\";\n    }\n}\n<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Extended<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10095","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/10095","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=10095"}],"version-history":[{"count":3,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/10095\/revisions"}],"predecessor-version":[{"id":10100,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/10095\/revisions\/10100"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=10095"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=10095"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=10095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}