Commits

Михаил Сафонов  committed 31c0c0d

+ Add filter DgvDataSourceComboBoxColumn filter
+ Add emun of comparing operations
+ Add enum localization environments
+ Localization of Operation enum

  • Participants
  • Parent commits 6f84f46

Comments (0)

Files changed (10)

File DgvFilterPopup.csproj

   <ItemGroup>
     <Compile Include="FilterPopup\ColumnFilterEvent.cs" />
     <Compile Include="FilterPopup\ColumnFiltersCollection.cs" />
+    <Compile Include="FilterPopup\Extensions\DgvDataSoureComboBoxColumnFilter.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="FilterPopup\Extensions\DgvDataSoureComboBoxColumnFilter.Designer.cs">
+      <DependentUpon>DgvDataSoureComboBoxColumnFilter.cs</DependentUpon>
+    </Compile>
     <Compile Include="FilterPopup\Extensions\DgvDateRangeColumnFilter.cs">
       <SubType>UserControl</SubType>
     </Compile>
     <Compile Include="FilterPopup\Implementations\DgvTextBoxColumnFilter.Designer.cs">
       <DependentUpon>DgvTextBoxColumnFilter.cs</DependentUpon>
     </Compile>
+    <Compile Include="FilterPopup\OperationsEnum.cs" />
+    <Compile Include="FilterPopup\ResourceEnumConverter.cs" />
+    <Compile Include="FilterPopup\SimpleEnumLocalization.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Resources\EnumLocalization.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>EnumLocalization.resx</DependentUpon>
+    </Compile>
     <Compile Include="Resources\LocalizedStrings.Designer.cs">
       <AutoGen>True</AutoGen>
       <DesignTime>True</DesignTime>
     </Compile>
   </ItemGroup>
   <ItemGroup>
+    <EmbeddedResource Include="FilterPopup\Extensions\DgvDataSoureComboBoxColumnFilter.resx">
+      <DependentUpon>DgvDataSoureComboBoxColumnFilter.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="FilterPopup\Extensions\DgvDateRangeColumnFilter.resx">
       <DependentUpon>DgvDateRangeColumnFilter.cs</DependentUpon>
       <SubType>Designer</SubType>
       <SubType>Designer</SubType>
       <DependentUpon>DgvTextBoxColumnFilter.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Resources\EnumLocalization.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>EnumLocalization.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Resources\EnumLocalization.ru.resx" />
     <EmbeddedResource Include="Resources\LocalizedStrings.resx">
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>LocalizedStrings.Designer.cs</LastGenOutput>
     <None Include="Diagrams\Extensions.cd" />
     <None Include="Diagrams\Implementations.cd" />
   </ItemGroup>
-  <ItemGroup />
+  <ItemGroup>
+    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

File FilterPopup/Extensions/DgvDataSoureComboBoxColumnFilter.Designer.cs

+namespace DgvFilterPopup
+{
+    partial class DgvDataSoureComboBoxColumnFilter
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.comboBox1 = new System.Windows.Forms.ComboBox();
+            this.operatorComboBox = new System.Windows.Forms.ComboBox();
+            this.SuspendLayout();
+            // 
+            // comboBox1
+            // 
+            this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.comboBox1.FormattingEnabled = true;
+            this.comboBox1.Location = new System.Drawing.Point(59, 5);
+            this.comboBox1.Name = "comboBox1";
+            this.comboBox1.Size = new System.Drawing.Size(214, 21);
+            this.comboBox1.Sorted = true;
+            this.comboBox1.TabIndex = 0;
+            this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.Filter_Changed);
+            // 
+            // operatorComboBox
+            // 
+            this.operatorComboBox.FormattingEnabled = true;
+            this.operatorComboBox.Location = new System.Drawing.Point(3, 5);
+            this.operatorComboBox.Name = "operatorComboBox";
+            this.operatorComboBox.Size = new System.Drawing.Size(50, 21);
+            this.operatorComboBox.Sorted = true;
+            this.operatorComboBox.TabIndex = 1;
+            this.operatorComboBox.SelectedIndexChanged += new System.EventHandler(this.Filter_Changed);
+            // 
+            // DgvDataSoureComboBoxColumnFilter
+            // 
+            this.Controls.Add(this.operatorComboBox);
+            this.Controls.Add(this.comboBox1);
+            this.Name = "DgvDataSoureComboBoxColumnFilter";
+            this.Size = new System.Drawing.Size(276, 30);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.ComboBox comboBox1;
+        private System.Windows.Forms.ComboBox operatorComboBox;
+    }
+}

File FilterPopup/Extensions/DgvDataSoureComboBoxColumnFilter.cs

+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+
+namespace DgvFilterPopup
+{
+    public partial class DgvDataSoureComboBoxColumnFilter : DgvFilterPopup.DgvBaseColumnFilter
+    {
+        public DgvDataSoureComboBoxColumnFilter()
+        {
+            InitializeComponent();
+        }
+
+        protected override void OnFilterInitializing(object sender, CancelEventArgs e)
+        {
+            FilterHost.RegisterComboBox(operatorComboBox);
+            FilterHost.RegisterComboBox(comboBox1);
+
+            operatorComboBox.DataSource = new Operation[]
+                                               {
+                                                   Operation.Equal,
+                                                   Operation.NotEqual,
+                                                   Operation.IsNull,
+                                                   Operation.NotNull
+                                               };
+            operatorComboBox.SelectedIndex = 0;
+
+            base.OnFilterInitializing(sender, e);
+        }
+
+        protected override void OnFilterExpressionBuilding(object sender, CancelEventArgs e)
+        {
+            base.OnFilterExpressionBuilding(sender, e);
+
+            string resultFilter = String.Empty;
+            string resultFilterDisplay = OriginalDataGridViewColumnHeaderText;
+
+            var filterValue = GetSelectedValue();
+            Operation operation = GetSelectedOperation();
+
+            if (operation == Operation.IsNull)
+                resultFilter = GetNullCondition(this.DataGridViewColumn.DataPropertyName);
+            if (operation == Operation.NotNull)
+                resultFilter = GetNotNullCondition(this.DataGridViewColumn.DataPropertyName);
+
+            if (resultFilter != "")
+            {
+                FilterExpression = resultFilter;
+                FilterCaption = resultFilterDisplay + "\n" + SimpleEnumLocalization.ConvertToString(operation);
+                FilterManager.RebuildFilter();
+                return;
+            }
+
+            if (String.IsNullOrEmpty(filterValue.ToString()))
+            {
+                Active = false;
+                FilterManager.RebuildFilter();
+                return;
+            }
+
+            string expressionOperator = "";
+            if (operation == Operation.Equal)
+                expressionOperator = "=";
+            else if (operation == Operation.NotEqual)
+                expressionOperator = "<>";
+            else
+                System.Diagnostics.Debug.Fail("Unknow operator " + operation);
+
+            if (ColumnDataType == typeof(string))
+            {
+                string escapedValue = StringEscape(filterValue.ToString());
+                resultFilter = "[" + DataGridViewColumn.DataPropertyName + "] " + expressionOperator + " '" + escapedValue + "'";
+                resultFilterDisplay += "\n" + SimpleEnumLocalization.ConvertToString(operation) + " " + comboBox1.Text;
+            }
+            else
+            {
+                string formattedValue = FormatValue(filterValue, this.ColumnDataType);
+                if (formattedValue != "")
+                {
+                    resultFilter = "[" + this.DataGridViewColumn.DataPropertyName + "] " + expressionOperator + " " + formattedValue;
+                    resultFilterDisplay += "\n" + SimpleEnumLocalization.ConvertToString(operation) + " " + comboBox1.Text;
+                }
+            }
+
+            if (resultFilter != "")
+            {
+                FilterExpression = resultFilter;
+                FilterCaption = resultFilterDisplay;
+                FilterManager.RebuildFilter();
+            }
+        }
+
+
+        public object DataSource
+        {
+            get
+            {
+                return comboBox1.DataSource;
+            }
+            set
+            {
+                comboBox1.DataSource = value;
+            }
+        }
+
+        public string DisplayMember
+        {
+            get
+            {
+                return comboBox1.DisplayMember;
+            }
+            set
+            {
+                comboBox1.DisplayMember = value;
+            }
+        }
+
+        public string ValueMember
+        {
+            get
+            {
+                return comboBox1.ValueMember;
+            }
+            set
+            {
+                comboBox1.ValueMember = value;
+            }
+        }
+
+
+
+        private void Filter_Changed(object sender, EventArgs e)
+        {
+            if (FilterApplySoon && Visible)
+            {
+                Active = true;
+                FilterExpressionBuild();
+            }
+        }
+
+        private Operation GetSelectedOperation()
+        {
+            return (Operation)operatorComboBox.SelectedValue;
+        }
+
+        private object GetSelectedValue()
+        {
+            return comboBox1.SelectedValue;
+        }
+    }
+}

File FilterPopup/Extensions/DgvDataSoureComboBoxColumnFilter.resx

+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

File FilterPopup/OperationsEnum.cs

+using System;
+using System.ComponentModel;
+
+namespace DgvFilterPopup
+{
+    /// <summary>
+    /// Compare operations
+    /// </summary>
+    [TypeConverter(typeof(SimpleEnumLocalization))]
+    public enum Operation
+    {
+        /// <summary>
+        /// Field starts with given value
+        /// </summary>
+        StartsWith,
+        /// <summary>
+        /// Field ends with given value
+        /// </summary>
+        EndsWith,
+        /// <summary>
+        /// Field contains given value
+        /// </summary>
+        Contains,
+        /// <summary>
+        /// Field equals with given value
+        /// </summary>
+        Equal,
+        /// <summary>
+        /// Field not equals with given value
+        /// </summary>
+        NotEqual,
+        /// <summary>
+        /// Field greater than given value
+        /// </summary>
+        Greater,
+        /// <summary>
+        /// Field less than given value
+        /// </summary>
+        Less,
+        /// <summary>
+        /// Field greater or equal than given value
+        /// </summary>
+        GreaterOrEqual,
+        /// <summary>
+        /// Field less or equal than given value
+        /// </summary>
+        LessOrEqual,
+        /// <summary>
+        /// Field contains in given range without equal to endpoints
+        /// </summary>
+        BetweenExclude,
+        /// <summary>
+        /// Field contains in given range with equal to endpoints
+        /// </summary>
+        BetweenInclude,
+        /// <summary>
+        /// Field value is null
+        /// </summary>
+        IsNull,
+        /// <summary>
+        /// Field value is not null
+        /// </summary>
+        NotNull
+    }
+}

File FilterPopup/ResourceEnumConverter.cs

+//
+// COPYRIGHT:   Copyright 2007 
+//              Infralution
+//              www.infralution.com
+//
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
+using System.ComponentModel;
+namespace Infralution.Localization
+{
+    /// <summary>
+    /// Defines a type converter for enum values that converts enum values to 
+    /// and from string representations using resources
+    /// </summary>
+    /// <remarks>
+    /// This class makes localization of display values for enums in a project easy.  Simply
+    /// derive a class from this class and pass the ResourceManagerin the constructor.  
+    /// 
+    /// <code lang="C#" escaped="true" >
+    /// class LocalizedEnumConverter : ResourceEnumConverter
+    /// {
+    ///    public LocalizedEnumConverter(Type type)
+    ///        : base(type, Properties.Resources.ResourceManager)
+    ///    {
+    ///    }
+    /// }    
+    /// </code>
+    /// 
+    /// <code lang="Visual Basic" escaped="true" >
+    /// Public Class LocalizedEnumConverter
+    /// 
+    ///    Inherits ResourceEnumConverter
+    ///    Public Sub New(ByVal sType as Type)
+    ///        MyBase.New(sType, My.Resources.ResourceManager)
+    ///    End Sub
+    /// End Class    
+    /// </code>
+    /// 
+    /// Then define the enum values in the resource editor.   The names of
+    /// the resources are simply the enum value prefixed by the enum type name with an
+    /// underscore separator eg MyEnum_MyValue.  You can then use the TypeConverter attribute
+    /// to make the LocalizedEnumConverter the default TypeConverter for the enums in your
+    /// project.
+    /// </remarks>
+    public class ResourceEnumConverter : System.ComponentModel.EnumConverter
+    {
+        private class LookupTable : Dictionary<string, object> { }
+        private Dictionary<CultureInfo, LookupTable> _lookupTables = new Dictionary<CultureInfo, LookupTable>();
+        private System.Resources.ResourceManager _resourceManager;
+        private bool _isFlagEnum = false;
+        private Array _flagValues;
+
+        /// <summary>
+        /// Get the lookup table for the given culture (creating if necessary)
+        /// </summary>
+        /// <param name="culture"></param>
+        /// <returns></returns>
+        private LookupTable GetLookupTable(CultureInfo culture)
+        {
+            LookupTable result = null;
+            if (culture == null)
+                culture = CultureInfo.CurrentCulture;
+
+            if (!_lookupTables.TryGetValue(culture, out result))
+            {
+                result = new LookupTable();
+                foreach (object value in GetStandardValues())
+                {
+                    string text = GetValueText(culture, value);
+                    if (text != null)
+                    {
+                        result.Add(text, value);
+                    }
+                }
+                _lookupTables.Add(culture, result);
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Return the text to display for a simple value in the given culture
+        /// </summary>
+        /// <param name="culture">The culture to get the text for</param>
+        /// <param name="value">The enum value to get the text for</param>
+        /// <returns>The localized text</returns>
+        private string GetValueText(CultureInfo culture, object value)
+        {
+            Type type = value.GetType();
+            string resourceName = string.Format("{0}_{1}", type.Name, value.ToString());
+            string result = _resourceManager.GetString(resourceName, culture);
+            if (result == null)
+                result = resourceName;
+            return result;
+        }
+
+        /// <summary>
+        /// Return true if the given value is can be represented using a single bit
+        /// </summary>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        private bool IsSingleBitValue(ulong value)
+        {
+            switch (value)
+            {
+                case 0:
+                    return false;
+                case 1:
+                    return true;
+            }
+            return ((value & (value - 1)) == 0);
+        }
+
+        /// <summary>
+        /// Return the text to display for a flag value in the given culture
+        /// </summary>
+        /// <param name="culture">The culture to get the text for</param>
+        /// <param name="value">The flag enum value to get the text for</param>
+        /// <returns>The localized text</returns>
+        private string GetFlagValueText(CultureInfo culture, object value)
+        {
+            // if there is a standard value then use it
+            //
+            if (Enum.IsDefined(value.GetType(), value))
+            {
+                return GetValueText(culture, value);
+            }
+
+            // otherwise find the combination of flag bit values
+            // that makes up the value
+            //
+            ulong lValue = Convert.ToUInt32(value);
+            string result = null;
+            foreach (object flagValue in _flagValues)
+            {
+                ulong lFlagValue = Convert.ToUInt32(flagValue);
+                if (IsSingleBitValue(lFlagValue))
+                {
+                    if ((lFlagValue & lValue) == lFlagValue)
+                    {
+                        string valueText = GetValueText(culture, flagValue);
+                        if (result == null)
+                        {
+                            result = valueText;
+                        }
+                        else
+                        {
+                            result = string.Format("{0}, {1}", result, valueText);
+                        }
+                    }
+                }
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Return the Enum value for a simple (non-flagged enum)
+        /// </summary>
+        /// <param name="culture">The culture to convert using</param>
+        /// <param name="text">The text to convert</param>
+        /// <returns>The enum value</returns>
+        private object GetValue(CultureInfo culture, string text)
+        {
+            LookupTable lookupTable = GetLookupTable(culture);
+            object result = null;
+            lookupTable.TryGetValue(text, out result);
+            return result;
+        }
+
+        /// <summary>
+        /// Return the Enum value for a flagged enum
+        /// </summary>
+        /// <param name="culture">The culture to convert using</param>
+        /// <param name="text">The text to convert</param>
+        /// <returns>The enum value</returns>
+        private object GetFlagValue(CultureInfo culture, string text)
+        {
+            LookupTable lookupTable = GetLookupTable(culture);
+            string[] textValues = text.Split(',');
+            ulong result = 0;
+            foreach (string textValue in textValues)
+            {
+                object value = null;
+                string trimmedTextValue = textValue.Trim();
+                if (!lookupTable.TryGetValue(trimmedTextValue, out value))
+                {
+                    return null;
+                }
+                result |= Convert.ToUInt32(value);
+            }
+            return Enum.ToObject(EnumType, result);
+        }
+
+        /// <summary>
+        /// Create a new instance of the converter using translations from the given resource manager
+        /// </summary>
+        /// <param name="type"></param>
+        /// <param name="resourceManager"></param>
+        public ResourceEnumConverter(Type type, System.Resources.ResourceManager resourceManager)
+            : base(type)
+        {
+            _resourceManager = resourceManager;
+            object[] flagAttributes = type.GetCustomAttributes(typeof(FlagsAttribute), true);
+            _isFlagEnum = flagAttributes.Length > 0;
+            if (_isFlagEnum)
+            {
+                _flagValues = Enum.GetValues(type);
+            }
+        }
+
+        /// <summary>
+        /// Convert string values to enum values
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="culture"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
+        {
+            if (value is string)
+            {
+                object result = (_isFlagEnum) ?
+                    GetFlagValue(culture, (string)value): GetValue(culture, (string)value);
+                if (result == null)
+                {
+                    result = base.ConvertFrom(context, culture, value);
+                }
+                return result;
+            }
+            else
+            {
+                return base.ConvertFrom(context, culture, value);
+            }
+        }
+
+        /// <summary>
+        /// Convert the enum value to a string
+        /// </summary>
+        /// <param name="context"></param>
+        /// <param name="culture"></param>
+        /// <param name="value"></param>
+        /// <param name="destinationType"></param>
+        /// <returns></returns>
+        public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
+        {
+            if (value != null && destinationType == typeof(string))
+            {
+                object result = (_isFlagEnum) ? 
+                    GetFlagValueText(culture, value) : GetValueText(culture, value);
+                return result;
+            }
+            else
+            {
+                return base.ConvertTo(context, culture, value, destinationType);
+            }
+        }
+
+        /// <summary>
+        /// Convert the given enum value to string using the registered type converter
+        /// </summary>
+        /// <param name="value">The enum value to convert to string</param>
+        /// <returns>The localized string value for the enum</returns>
+        static public string ConvertToString(Enum value)
+        {
+            TypeConverter converter = TypeDescriptor.GetConverter(value.GetType());
+            return converter.ConvertToString(value);
+        }
+
+        /// <summary>
+        /// Return a list of the enum values and their associated display text for the given enum type
+        /// </summary>
+        /// <param name="enumType">The enum type to get the values for</param>
+        /// <param name="culture">The culture to get the text for</param>
+        /// <returns>
+        /// A list of KeyValuePairs where the key is the enum value and the value is the text to display
+        /// </returns>
+        /// <remarks>
+        /// This method can be used to provide localized binding to enums in ASP.NET applications.   Unlike 
+        /// windows forms the standard ASP.NET controls do not use TypeConverters to convert from enum values
+        /// to the displayed text.   You can bind an ASP.NET control to the list returned by this method by setting
+        /// the DataValueField to "Key" and theDataTextField to "Value". 
+        /// </remarks>
+        static public List<KeyValuePair<Enum, string>> GetValues(Type enumType, CultureInfo culture)
+        {
+            List<KeyValuePair<Enum, string>> result = new List<KeyValuePair<Enum, string>>();
+            TypeConverter converter = TypeDescriptor.GetConverter(enumType);
+            foreach (Enum value in Enum.GetValues(enumType))
+            {
+                KeyValuePair<Enum, string> pair = new KeyValuePair<Enum, string>(value, converter.ConvertToString(null, culture, value));
+                result.Add(pair); 
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Return a list of the enum values and their associated display text for the given enum type in the current UI Culture
+        /// </summary>
+        /// <param name="enumType">The enum type to get the values for</param>
+        /// <returns>
+        /// A list of KeyValuePairs where the key is the enum value and the value is the text to display
+        /// </returns>
+        /// <remarks>
+        /// This method can be used to provide localized binding to enums in ASP.NET applications.   Unlike 
+        /// windows forms the standard ASP.NET controls do not use TypeConverters to convert from enum values
+        /// to the displayed text.   You can bind an ASP.NET control to the list returned by this method by setting
+        /// the DataValueField to "Key" and theDataTextField to "Value". 
+        /// </remarks>
+        static public List<KeyValuePair<Enum, string>> GetValues(Type enumType)
+        {
+            return GetValues(enumType, CultureInfo.CurrentUICulture);
+        }
+    }
+}

File FilterPopup/SimpleEnumLocalization.cs

+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace DgvFilterPopup
+{
+    sealed public class SimpleEnumLocalization : Infralution.Localization.ResourceEnumConverter
+    {
+        public SimpleEnumLocalization(Type type)
+            : base(type, Resources.EnumLocalization.ResourceManager)
+        {}
+    }
+}

File Resources/EnumLocalization.Designer.cs

+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.17929
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DgvFilterPopup.Resources {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class EnumLocalization {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal EnumLocalization() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DgvFilterPopup.Resources.EnumLocalization", typeof(EnumLocalization).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to (...).
+        /// </summary>
+        internal static string Operation_BetweenExclude {
+            get {
+                return ResourceManager.GetString("Operation_BetweenExclude", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to [...].
+        /// </summary>
+        internal static string Operation_BetweenInclude {
+            get {
+                return ResourceManager.GetString("Operation_BetweenInclude", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to =.
+        /// </summary>
+        internal static string Operation_Equal {
+            get {
+                return ResourceManager.GetString("Operation_Equal", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to &gt;.
+        /// </summary>
+        internal static string Operation_Greater {
+            get {
+                return ResourceManager.GetString("Operation_Greater", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to ≥.
+        /// </summary>
+        internal static string Operation_GreaterOrEqual {
+            get {
+                return ResourceManager.GetString("Operation_GreaterOrEqual", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to = Ø.
+        /// </summary>
+        internal static string Operation_IsNull {
+            get {
+                return ResourceManager.GetString("Operation_IsNull", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to &lt;.
+        /// </summary>
+        internal static string Operation_Less {
+            get {
+                return ResourceManager.GetString("Operation_Less", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to ≤.
+        /// </summary>
+        internal static string Operation_LessOrEqual {
+            get {
+                return ResourceManager.GetString("Operation_LessOrEqual", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to ≠.
+        /// </summary>
+        internal static string Operation_NotEqual {
+            get {
+                return ResourceManager.GetString("Operation_NotEqual", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to ≠ Ø.
+        /// </summary>
+        internal static string Operation_NotNull {
+            get {
+                return ResourceManager.GetString("Operation_NotNull", resourceCulture);
+            }
+        }
+    }
+}

File Resources/EnumLocalization.resx

+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Operation_BetweenExclude" xml:space="preserve">
+    <value>(...)</value>
+  </data>
+  <data name="Operation_BetweenInclude" xml:space="preserve">
+    <value>[...]</value>
+  </data>
+  <data name="Operation_Equal" xml:space="preserve">
+    <value>=</value>
+  </data>
+  <data name="Operation_Greater" xml:space="preserve">
+    <value>&gt;</value>
+  </data>
+  <data name="Operation_GreaterOrEqual" xml:space="preserve">
+    <value>≥</value>
+  </data>
+  <data name="Operation_IsNull" xml:space="preserve">
+    <value>= Ø</value>
+  </data>
+  <data name="Operation_Less" xml:space="preserve">
+    <value>&lt;</value>
+  </data>
+  <data name="Operation_LessOrEqual" xml:space="preserve">
+    <value>≤</value>
+  </data>
+  <data name="Operation_NotEqual" xml:space="preserve">
+    <value>≠</value>
+  </data>
+  <data name="Operation_NotNull" xml:space="preserve">
+    <value>≠ Ø</value>
+  </data>
+</root>

File Resources/EnumLocalization.ru.resx

+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Operation_Contains" xml:space="preserve">
+    <value>Содержит</value>
+  </data>
+  <data name="Operation_EndsWith" xml:space="preserve">
+    <value>Заканчивается на</value>
+  </data>
+  <data name="Operation_StartsWith" xml:space="preserve">
+    <value>Начинается с</value>
+  </data>
+</root>