From 1bfa5b6854444e9e04fed20da1590cd8be60aad8 Mon Sep 17 00:00:00 2001
From: NaBian <836904362@qq.com>
Date: Thu, 19 Sep 2019 00:31:18 +0800
Subject: [PATCH] add ColLayoutConverter
---
.../UserControl/Controls/GridDemoCtl.xaml | 4 +-
.../HandyControl_Shared.projitems | 2 +
.../Tools/Converter/ColLayoutConverter.cs | 161 ++++++++++++
.../Tools/Extension/ColLayout.cs | 54 ++++
.../Tools/Helper/TokenizerHelper.cs | 233 ++++++++++++++++++
5 files changed, 452 insertions(+), 2 deletions(-)
create mode 100644 src/Shared/HandyControl_Shared/Tools/Converter/ColLayoutConverter.cs
create mode 100644 src/Shared/HandyControl_Shared/Tools/Helper/TokenizerHelper.cs
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/GridDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/GridDemoCtl.xaml
index eccc1566..df298e28 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/GridDemoCtl.xaml
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/GridDemoCtl.xaml
@@ -139,10 +139,10 @@
-
+
-
+
diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
index 34d3cec7..5ef63b50 100644
--- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
+++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
@@ -41,6 +41,7 @@
+
@@ -275,6 +276,7 @@
+
diff --git a/src/Shared/HandyControl_Shared/Tools/Converter/ColLayoutConverter.cs b/src/Shared/HandyControl_Shared/Tools/Converter/ColLayoutConverter.cs
new file mode 100644
index 00000000..c4bbfb88
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Tools/Converter/ColLayoutConverter.cs
@@ -0,0 +1,161 @@
+using System;
+using System.ComponentModel;
+using System.ComponentModel.Design.Serialization;
+using System.Globalization;
+using System.Security;
+using System.Text;
+using HandyControl.Tools.Extension;
+
+namespace HandyControl.Tools.Converter
+{
+ public class ColLayoutConverter : TypeConverter
+ {
+ public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType)
+ {
+ switch (Type.GetTypeCode(sourceType))
+ {
+ case TypeCode.Int16:
+ case TypeCode.UInt16:
+ case TypeCode.Int32:
+ case TypeCode.UInt32:
+ case TypeCode.Int64:
+ case TypeCode.UInt64:
+ case TypeCode.Single:
+ case TypeCode.Double:
+ case TypeCode.Decimal:
+ case TypeCode.String:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) =>
+ destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string);
+
+ public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source)
+ {
+ if (source == null) throw GetConvertFromException(null);
+
+ switch (source)
+ {
+ case string s:
+ return FromString(s, cultureInfo);
+ case double d:
+ return new ColLayout((int)d);
+ default:
+ return new ColLayout(Convert.ToInt32(source, cultureInfo));
+ }
+ }
+
+ [SecurityCritical]
+ public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType)
+ {
+ if (value == null) throw new ArgumentNullException(nameof(value));
+
+ if (destinationType == null) throw new ArgumentNullException(nameof(destinationType));
+
+ if (!(value is ColLayout)) throw new ArgumentException("UnexpectedParameterType");
+
+ var th = (ColLayout)value;
+ if (destinationType == typeof(string)) return ToString(th, cultureInfo);
+ if (destinationType == typeof(InstanceDescriptor))
+ {
+ var ci = typeof(ColLayout).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) });
+ return new InstanceDescriptor(ci, new object[] { th.Xs, th.Sm, th.Md, th.Lg, th.Xl, th.Xxl });
+ }
+
+ throw new ArgumentException("CannotConvertType");
+ }
+
+ private static string ToString(ColLayout th, CultureInfo cultureInfo)
+ {
+ var listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
+
+ // Initial capacity [128] is an estimate based on a sum of:
+ // 72 = 6x double (twelve digits is generous for the range of values likely)
+ // 4 = 4x separator characters
+ var sb = new StringBuilder(128);
+
+ sb.Append(th.Xs.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(th.Sm.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(th.Md.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(th.Lg.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(th.Xl.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(th.Xxl.ToString(cultureInfo));
+ return th.ToString();
+ }
+
+ private static ColLayout FromString(string s, CultureInfo cultureInfo)
+ {
+ var th = new TokenizerHelper(s, cultureInfo);
+ var lengths = new int[6];
+ var i = 0;
+
+ while (th.NextToken())
+ {
+ if (i >= 6)
+ {
+ i = 7; // Set i to a bad value.
+ break;
+ }
+
+ lengths[i] = th.GetCurrentToken().Value();
+ i++;
+ }
+
+ switch (i)
+ {
+ case 1:
+ return new ColLayout(lengths[0]);
+ case 2:
+ return new ColLayout
+ {
+ Xs = lengths[0],
+ Sm = lengths[1]
+ };
+ case 3:
+ return new ColLayout
+ {
+ Xs = lengths[0],
+ Sm = lengths[1],
+ Md = lengths[2]
+ };
+ case 4:
+ return new ColLayout
+ {
+ Xs = lengths[0],
+ Sm = lengths[1],
+ Md = lengths[2],
+ Lg = lengths[3]
+ };
+ case 5:
+ return new ColLayout
+ {
+ Xs = lengths[0],
+ Sm = lengths[1],
+ Md = lengths[2],
+ Lg = lengths[3],
+ Xl = lengths[4]
+ };
+ case 6:
+ return new ColLayout
+ {
+ Xs = lengths[0],
+ Sm = lengths[1],
+ Md = lengths[2],
+ Lg = lengths[3],
+ Xl = lengths[4],
+ Xxl = lengths[5]
+ };
+ }
+
+ throw new FormatException("InvalidStringColLayout");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Shared/HandyControl_Shared/Tools/Extension/ColLayout.cs b/src/Shared/HandyControl_Shared/Tools/Extension/ColLayout.cs
index f0573647..91785099 100644
--- a/src/Shared/HandyControl_Shared/Tools/Extension/ColLayout.cs
+++ b/src/Shared/HandyControl_Shared/Tools/Extension/ColLayout.cs
@@ -1,9 +1,14 @@
using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Text;
using System.Windows.Markup;
using HandyControl.Data;
+using HandyControl.Tools.Converter;
namespace HandyControl.Tools.Extension
{
+ [TypeConverter(typeof(ColLayoutConverter))]
public class ColLayout : MarkupExtension
{
public static readonly int ColMaxCellCount = 24;
@@ -30,6 +35,31 @@ namespace HandyControl.Tools.Extension
public int Xxl { get; set; } = 2;
+ public ColLayout()
+ {
+
+ }
+
+ public ColLayout(int uniformWidth)
+ {
+ Xs = uniformWidth;
+ Sm = uniformWidth;
+ Md = uniformWidth;
+ Lg = uniformWidth;
+ Xl = uniformWidth;
+ Xxl = uniformWidth;
+ }
+
+ public ColLayout(int xs, int sm, int md, int lg, int xl, int xxl)
+ {
+ Xs = xs;
+ Sm = sm;
+ Md = md;
+ Lg = lg;
+ Xl = xl;
+ Xxl = xxl;
+ }
+
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new ColLayout
@@ -67,5 +97,29 @@ namespace HandyControl.Tools.Extension
}
return ColLayoutStatus.Xxl;
}
+
+ public override string ToString()
+ {
+ var cultureInfo = CultureInfo.CurrentCulture;
+ var listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
+
+ // Initial capacity [128] is an estimate based on a sum of:
+ // 72 = 6x double (twelve digits is generous for the range of values likely)
+ // 4 = 4x separator characters
+ var sb = new StringBuilder(128);
+
+ sb.Append(Xs.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(Sm.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(Md.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(Lg.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(Xl.ToString(cultureInfo));
+ sb.Append(listSeparator);
+ sb.Append(Xxl.ToString(cultureInfo));
+ return sb.ToString();
+ }
}
}
\ No newline at end of file
diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/TokenizerHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/TokenizerHelper.cs
new file mode 100644
index 00000000..2911bf39
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Tools/Helper/TokenizerHelper.cs
@@ -0,0 +1,233 @@
+//---------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//
+// File: TokenizerHelper.cs
+//
+// Description: This file contains the implementation of TokenizerHelper.
+// This class should be used by most - if not all - MIL parsers.
+//
+// History:
+// 05/19/2003 : Microsoft - Created it
+// 05/20/2003 : Microsoft - Moved to Shared
+//
+//---------------------------------------------------------------------------
+
+using System;
+using System.Globalization;
+
+namespace HandyControl.Tools
+{
+ internal class TokenizerHelper
+ {
+ private char _quoteChar;
+
+ private char _argSeparator;
+
+ private string _str;
+
+ private int _strLen;
+
+ private int _charIndex;
+
+ private int _currentTokenIndex;
+
+ private int _currentTokenLength;
+
+ public bool FoundSeparator { get; private set; }
+
+ public TokenizerHelper(string str, IFormatProvider formatProvider)
+ {
+ var numberSeparator = GetNumericListSeparator(formatProvider);
+ Initialize(str, '\'', numberSeparator);
+ }
+
+ private void Initialize(string str, char quoteChar, char separator)
+ {
+ _str = str;
+ _strLen = str?.Length ?? 0;
+ _currentTokenIndex = -1;
+ _quoteChar = quoteChar;
+ _argSeparator = separator;
+
+ // immediately forward past any whitespace so
+ // NextToken() logic always starts on the first
+ // character of the next token.
+ while (_charIndex < _strLen)
+ {
+ if (!char.IsWhiteSpace(_str, _charIndex))
+ {
+ break;
+ }
+
+ ++_charIndex;
+ }
+ }
+
+ public string GetCurrentToken() =>
+ _currentTokenIndex < 0 ? null : _str.Substring(_currentTokenIndex, _currentTokenLength);
+
+ internal bool NextToken() => NextToken(false);
+
+ public bool NextToken(bool allowQuotedToken) => NextToken(allowQuotedToken, _argSeparator);
+
+ public bool NextToken(bool allowQuotedToken, char separator)
+ {
+ _currentTokenIndex = -1; // reset the currentTokenIndex
+ FoundSeparator = false; // reset
+
+ // If we're at end of the string, just return false.
+ if (_charIndex >= _strLen)
+ {
+ return false;
+ }
+
+ var currentChar = _str[_charIndex];
+
+ // setup the quoteCount
+ var quoteCount = 0;
+
+ // If we are allowing a quoted token and this token begins with a quote,
+ // set up the quote count and skip the initial quote
+ if (allowQuotedToken &&
+ currentChar == _quoteChar)
+ {
+ quoteCount++; // increment quote count
+ ++_charIndex; // move to next character
+ }
+
+ var newTokenIndex = _charIndex;
+ var newTokenLength = 0;
+
+ // loop until hit end of string or hit a , or whitespace
+ // if at end of string ust return false.
+ while (_charIndex < _strLen)
+ {
+ currentChar = _str[_charIndex];
+
+ // if have a QuoteCount and this is a quote
+ // decrement the quoteCount
+ if (quoteCount > 0)
+ {
+ // if anything but a quoteChar we move on
+ if (currentChar == _quoteChar)
+ {
+ --quoteCount;
+
+ // if at zero which it always should for now
+ // break out of the loop
+ if (0 == quoteCount)
+ {
+ ++_charIndex; // move past the quote
+ break;
+ }
+ }
+ }
+ else if (char.IsWhiteSpace(currentChar) || currentChar == separator)
+ {
+ if (currentChar == separator)
+ {
+ FoundSeparator = true;
+ }
+ break;
+ }
+
+ ++_charIndex;
+ ++newTokenLength;
+ }
+
+ // if quoteCount isn't zero we hit the end of the string
+ // before the ending quote
+ if (quoteCount > 0)
+ {
+ throw new InvalidOperationException("TokenizerHelperMissingEndQuote");
+ }
+
+ ScanToNextToken(separator); // move so at the start of the nextToken for next call
+
+ // finally made it, update the _currentToken values
+ _currentTokenIndex = newTokenIndex;
+ _currentTokenLength = newTokenLength;
+
+ if (_currentTokenLength < 1)
+ {
+ throw new InvalidOperationException("TokenizerHelperEmptyToken");
+ }
+
+ return true;
+ }
+
+ private void ScanToNextToken(char separator)
+ {
+ // if already at end of the string don't bother
+ if (_charIndex >= _strLen) return;
+
+ var currentChar = _str[_charIndex];
+
+ // check that the currentChar is a space or the separator. If not
+ // we have an error. this can happen in the quote case
+ // that the char after the quotes string isn't a char.
+ if (currentChar != separator && !char.IsWhiteSpace(currentChar))
+ {
+ throw new InvalidOperationException("TokenizerHelperExtraDataEncountered");
+ }
+
+ // loop until hit a character that isn't
+ // an argument separator or whitespace.
+ // !!!Todo: if more than one argSet throw an exception
+ var argSepCount = 0;
+ while (_charIndex < _strLen)
+ {
+ currentChar = _str[_charIndex];
+
+ if (currentChar == separator)
+ {
+ FoundSeparator = true;
+ ++argSepCount;
+ _charIndex++;
+
+ if (argSepCount > 1)
+ {
+ throw new InvalidOperationException("TokenizerHelperEmptyToken");
+ }
+ }
+ else if (char.IsWhiteSpace(currentChar))
+ {
+ ++_charIndex;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // if there was a separatorChar then we shouldn't be
+ // at the end of string or means there was a separator
+ // but there isn't an arg
+
+ if (argSepCount > 0 && _charIndex >= _strLen)
+ {
+ throw new InvalidOperationException("TokenizerHelperEmptyToken");
+ }
+ }
+
+ internal static char GetNumericListSeparator(IFormatProvider provider)
+ {
+ var numericSeparator = ',';
+
+ // Get the NumberFormatInfo out of the provider, if possible
+ // If the IFormatProvider doesn't not contain a NumberFormatInfo, then
+ // this method returns the current culture's NumberFormatInfo.
+ var numberFormat = NumberFormatInfo.GetInstance(provider);
+
+ // Is the decimal separator is the same as the list separator?
+ // If so, we use the ";".
+ if (numberFormat.NumberDecimalSeparator.Length > 0 && numericSeparator == numberFormat.NumberDecimalSeparator[0])
+ {
+ numericSeparator = ';';
+ }
+
+ return numericSeparator;
+ }
+ }
+}
\ No newline at end of file