add ColLayoutConverter

This commit is contained in:
NaBian committed 2019-09-19 00:31:18 +08:00
1 parent 6e1d294202
commit 1bfa5b6854
5 files changed
+452 -2

No files matched your search

@@ -139,10 +139,10 @@
</hc:Row>
<TextBlock Text="{x:Static langs:Lang.ResponsiveLayout}" Margin="0,32,0,0" Style="{StaticResource TextBlockLargeBold}" HorizontalAlignment="Left"/>
<hc:Row Margin="0,20,0,0" Gutter="10">
<hc:Col Layout="{extension:ColLayout Xs=8, Sm=6, Md=4, Lg=3, Xl=1}">
<hc:Col Layout="8,6,4,3,1">
<Border Background="{DynamicResource BorderBrush}" Height="36" CornerRadius="4"/>
</hc:Col>
<hc:Col Layout="{extension:ColLayout Xs=4, Sm=6, Md=8, Lg=9, Xl=11}">
<hc:Col Layout="4 6 8 9 11">
<Border Background="{DynamicResource SecondaryBorderBrush}" Height="36" CornerRadius="4"/>
</hc:Col>
<hc:Col Layout="{extension:ColLayout Xs=4, Sm=6, Md=8, Lg=9, Xl=11}">
@@ -41,6 +41,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Interactivity\Commands\CloseWindowCommand.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interactivity\Commands\OpenLinkCommand.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Converter\BorderCircularClipConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Converter\ColLayoutConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Converter\CornerRadiusSplitConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Converter\Number2PercentageConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Converter\RectangleCircularConverter.cs" />
@@ -275,6 +276,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\ResourceHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\SecurityHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\SingleOpenHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\TokenizerHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\ValidateHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\VisualHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Tools\Helper\WindowHelper.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<int>();
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");
}
}
}
@@ -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();
}
}
}
@@ -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;
}
}
}