Замена отсутствующей опции TextTrimming "CharacterEllipsis" в Silverlight
Silverlight (по крайней мере, начиная с версии 4) не имеет CharacterEllipsis
вариант для TextTrimming
, который имеет WPF. Это может быть использовано на TextBlock
, Это означает, что если недостаточно места для отображения "Это невероятно", я мог бы обрезать до "Это...", но не до "Это невероятно...", что мы бы предпочли.
Хотя я бы попробовал реализовать нашу собственную функцию обрезки текста. В принципе, это не так сложно. Довольно глупый способ - измерить пиксели для строки, сравнить с доступной шириной и манипулировать строкой, обрезая последний символ и добавляя "..." в цикле, пока текст по-прежнему не помещается. Вот пример того, как это может работать:
// Not perfect but good enough for us
private bool AutoTrim(string fullText, TextBlock textBlock, double maxWidth)
{
double factor = maxWidth / textBlock.ActualWidth;
if (factor > 1)
return false;
int newTextLength = (int)Math.Floor((double)fullText.Length * factor);
string trimTest;
do
{
trimTest = fullText.Substring(0, newTextLength--);
textBlock.Text = trimTest + "..."; // problematic...
factor = maxWidth / textBlock.ActualWidth;
}
while (factor < 1 && newTextLength > 0);
return true;
}
Но делать это в коде позади (или в пределах Behavior
) приводит к некоторым проблемам: например, когда мы хотим обновить отображаемый текст и установить текстовые блоки TextBlock1.Text = ...
Свойство, это на самом деле может изменить нашу viewModel, если текст привязан к свойству ViewModel. Другие проблемы возникают, когда мы заметили, что view и viewModel могут работать по какой-то причине (мы заметили это в ListBox).
У вас есть лучшее представление о том, как решить эту проблему хорошим способом?
4 ответа
DynamicTextBox Робби Ингебретсена делает это, оборачивая TextBlock в пользовательский элемент управления и измеряя доступный размер. Он соответствует режиму обрезки текста CharacterEllipsis в WPF. Режим WordEllipsis был добавлен в Windows Phone 7 Mango, но здесь это не сильно помогает.
Дэн Уолин использовал конвертер до того, как TextTrimming="WordEllipsis" был добавлен в Silverlight 4. Вы можете найти его здесь: http://weblogs.asp.net/dwahlin/archive/2010/05/05/text-trimming-in-silverlight-4.aspx
Вот как я обошел отсутствие опции CharacterEllipsis. Мое решение тоже не идеально, но оно до сих пор работало для меня.
Сначала я добавил следующий вспомогательный метод:
public static void AutoTrimTextBlock(TextBlock textBlock, double maxWidth)
{
if (!string.IsNullOrWhiteSpace(textBlock.Text))
{
var currentWidth = textBlock.ActualWidth;
if (currentWidth > maxWidth)
{
if (textBlock.Text.Length > 2)
{
int substrLength = textBlock.Text.Length - 1;
if (textBlock.Text[substrLength] == '…')
substrLength--;
textBlock.Text = textBlock.Text.Substring(0, substrLength) + '…';
}
else if (textBlock.Text.Length == 2)
{
if (textBlock.Text[1] == '…')
textBlock.Text = "…";
else
textBlock.Text = textBlock.Text[0].ToString() + '…';
}
else //implies: if (length == 1)
{
textBlock.Text = string.Empty;
}
}
}
}
Затем я обновил свой XAML, чтобы он выглядел так:
<Grid x:Name="MyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Column0" Width="Auto"/>
<ColumnDefinition x:Name="Column1" Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="SomeOtherText" Text="{Binding OtherString}"/>
<TextBlock Grid.Column="1" x:Name="MyTextBlock"
TextWrapping="NoWrap" <!--Disable text wrapping-->
TextTrimming="None" <!--Disable built-in text trimming-->
Text="{Binding MyString, Mode=OneWay}" <!--OneWay binding avoids writing trimmed text back to view model-->
LayoutUpdated="MyTextBlock_LayoutUpdated"/> <!--LayoutUpdated event will trigger custom text trimming-->
</Grid>
Наконец, в коде я добавил следующее:
void MyTextBlock_LayoutUpdated(object sender, System.EventArgs e)
{
// Calculate maximum width for MyTextBlock.
// I did it by checking the parent column width,
// but you can do it any way you like.
double maxWidth = Column1.ActualWidth - MyTextBlock.Margin.Left - MyTextBlock.Margin.Right;
// Start trimming
AutoTrimTextBlock(MyTextBlock, maxWidth);
}
Результат: при каждом изменении свойства MyString вызывается обработчик события LayoutUpdated и вызывается метод AutoTrimTextBlock(). Если MyTextBlock слишком широкий, его свойство Text обрезается и добавляется "…". Это вызывает другое событие LayoutUpdated. Процесс повторяется до тех пор, пока ширина MyTextBlock не станет меньше указанного максимума.
Как я уже сказал, он не идеален и не особенно элегантен, но в примерах, подобных приведенным выше, работает нормально.
Мне не нравится идея использовать событие LayoutUpdated, но я не смог найти другое подходящее. TextChanged не существует для TextBlock, к сожалению:(
Пожалуйста, дайте мне знать, если я могу что-то улучшить.
private bool TrimExtraCharacters(TextBlock textBlock)
{
if (textBlock != null && textBlock.ActualWidth > 0.1 && !string.IsNullOrWhiteSpace(textBlock.Text))
{
if (textBlock.ActualWidth > textBlock.MaxWidth)
{
textBlock.Text += '…';
int lastLetterIndex = textBlock.Text.Length -2;
do
{
textBlock.Text = textBlock.Text.Remove(lastLetterIndex, 1);
--lastLetterIndex;
} while (textBlock.ActualWidth > textBlock.MaxWidth);
}
return true;
}
return false;
}