Вы можете получить из Panel
и сделать его Selectable
и установить для TabStop
значение true. Тогда достаточно переопределить ProcessCmdKey
и использовать клавиши со стрелками для прокрутки. Не забудьте также установить для его AutoScroll
значение true.
Панель выбора - прокручиваемая с клавиатуры
using System.Drawing;
using System.Windows.Forms;
class SelectablePanel : Panel
{
const int ScrollSmallChange = 10;
public SelectablePanel()
{
SetStyle(ControlStyles.Selectable, true);
SetStyle(ControlStyles.UserMouse, true);
TabStop = true;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (!Focused)
return base.ProcessCmdKey(ref msg, keyData);
var p = AutoScrollPosition;
switch (keyData)
{
case Keys.Left:
AutoScrollPosition = new Point(-ScrollSmallChange - p.X, -p.Y);
return true;
case Keys.Right:
AutoScrollPosition = new Point(ScrollSmallChange - p.X, -p.Y);
return true;
case Keys.Up:
AutoScrollPosition = new Point(-p.X, -ScrollSmallChange - p.Y);
return true;
case Keys.Down:
AutoScrollPosition = new Point(-p.X, ScrollSmallChange - p.Y);
return true;
default:
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
Начните с первого символа пост, переступая через каждого персонажа. Каждый раз, когда вы пересекаете персонажа, увеличивайте счетчик. Когда вы найдете символ «<», прекратите увеличивать счетчик, пока не нажмете «>». Ваша позиция, когда счетчик достигает 250, - это то место, где вы действительно хотите отрезать.
Если я правильно понимаю проблему, вы хотите сохранить форматирование HTML, но не считать его частью длины строки, которую вы сохраняете.
Вы можете сделать это с помощью кода, который реализует простой конечный автомат .
2 состояния: InTag, OutOfTag
InTag:
- Переходит к OutOfTag, если встречается символ>
- Идет к себе любой другой встреченный персонаж
OutOfTag:
- Переходит к InTag, если встречается символ<
- Идет к себе, встречается любой другой символ
Ваше начальное состояние будет OutOfTag.
Вы реализуете конечный автомат, обрабатывая 1 символ за раз. Обработка каждого персонажа приводит вас в новое состояние.
Когда вы пропускаете текст через конечный автомат, вы также хотите сохранить выходной буфер и длину, с которой он встречался, доступной (чтобы вы знали, когда остановиться).
Вот реализация, которую я придумал, в C #:
public static string TrimToLength(string input, int length)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
if (input.Length <= length)
return input;
bool inTag = false;
int targetLength = 0;
for (int i = 0; i < input.Length; i++)
{
char c = input[i];
if (c == '>')
{
inTag = false;
continue;
}
if (c == '<')
{
inTag = true;
continue;
}
if (inTag || char.IsWhiteSpace(c))
{
continue;
}
targetLength++;
if (targetLength == length)
{
return ConvertToXhtml(input.Substring(0, i + 1));
}
}
return input;
}
И несколько модульных тестов, которые я использовал через TDD :
[Test]
public void Html_TrimReturnsEmptyStringWhenNullPassed()
{
Assert.That(Html.TrimToLength(null, 1000), Is.Empty);
}
[Test]
public void Html_TrimReturnsEmptyStringWhenEmptyPassed()
{
Assert.That(Html.TrimToLength(string.Empty, 1000), Is.Empty);
}
[Test]
public void Html_TrimReturnsUnmodifiedStringWhenSameAsLength()
{
string source = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
"<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
"<br/>" +
"In an attempt to make a bit more space in the office, kitchen, I";
Assert.That(Html.TrimToLength(source, 250), Is.EqualTo(source));
}
[Test]
public void Html_TrimWellFormedHtml()
{
string source = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
"<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
"<br/>" +
"In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in a box and donated to an office in more need of mugs than us. <br/><br/>" +
"In the meantime we have a nice selection of white Ikea mugs, some random Starbucks mugs, and others that have made their way into the office over the years. Hopefully that will suffice. <br/><br/>" +
"</div>";
string expected = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
"<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
"<br/>" +
"In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in";
Assert.That(Html.TrimToLength(source, 250), Is.EqualTo(expected));
}
[Test]
public void Html_TrimMalformedHtml()
{
string malformedHtml = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
"<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
"<br/>" +
"In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in a box and donated to an office in more need of mugs than us. <br/><br/>" +
"In the meantime we have a nice selection of white Ikea mugs, some random Starbucks mugs, and others that have made their way into the office over the years. Hopefully that will suffice. <br/><br/>";
string expected = "<div lang=\"en\" class=\"textBody localizable\" id=\"pageBody_en\">" +
"<img photoid=\"4041\" src=\"http://xxxxxxxx/imagethumb/562103830000/4041/300x300/False/mugs.jpg\" style=\"float: right;\" class=\"photoRight\" alt=\"\"/>" +
"<br/>" +
"In an attempt to make a bit more space in the office, kitchen, I've pulled out all of the random mugs and put them onto the lunch room table. Unless you feel strongly about the ownership of that Cheyenne Courier mug from 1992 or perhaps that BC Tel Advanced Communications mug from 1997, they will be put in";
Assert.That(Html.TrimToLength(malformedHtml, 250), Is.EqualTo(expected));
}
Разве не самым быстрым способом было бы использовать метод jQuery text ()
?
Например:
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
var text = $('ul').text();
Дало бы значение OneTwoThree в текстовой
переменной. Это позволит вам получить фактическую длину текста без включенного HTML.
Я знаю, что это немного позже опубликованной даты, но у меня была похожая проблема, и вот как я ее решил. Меня беспокоит скорость регулярного выражения по сравнению с взаимодействием через массив.
Также, если у вас есть пробел перед тегом html, и после этого это не исправляет
private string HtmlTrimmer(string input, int len)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
if (input.Length <= len)
return input;
// this is necissary because regex "^" applies to the start of the string, not where you tell it to start from
string inputCopy;
string tag;
string result = "";
int strLen = 0;
int strMarker = 0;
int inputLength = input.Length;
Stack stack = new Stack(10);
Regex text = new Regex("^[^<&]+");
Regex singleUseTag = new Regex("^<[^>]*?/>");
Regex specChar = new Regex("^&[^;]*?;");
Regex htmlTag = new Regex("^<.*?>");
while (strLen < len)
{
inputCopy = input.Substring(strMarker);
//If the marker is at the end of the string OR
//the sum of the remaining characters and those analyzed is less then the maxlength
if (strMarker >= inputLength || (inputLength - strMarker) + strLen < len)
break;
//Match regular text
result += text.Match(inputCopy,0,len-strLen);
strLen += result.Length - strMarker;
strMarker = result.Length;
inputCopy = input.Substring(strMarker);
if (singleUseTag.IsMatch(inputCopy))
result += singleUseTag.Match(inputCopy);
else if (specChar.IsMatch(inputCopy))
{
//think of as 1 character instead of 5
result += specChar.Match(inputCopy);
++strLen;
}
else if (htmlTag.IsMatch(inputCopy))
{
tag = htmlTag.Match(inputCopy).ToString();
//This only works if this is valid Markup...
if(tag[1]=='/') //Closing tag
stack.Pop();
else //not a closing tag
stack.Push(tag);
result += tag;
}
else //Bad syntax
result += input[strMarker];
strMarker = result.Length;
}
while (stack.Count > 0)
{
tag = stack.Pop().ToString();
result += tag.Insert(1, "/");
}
if (strLen == len)
result += "...";
return result;
}