none
WPF Анимация размера TextBlock RRS feed

  • Вопрос

  • В стек панели добавлен текстблок. Высота его зависит от наполнения текста. Как правильно анимировать его высоту от 0 до нужного размера, что бы был виден весь текст? При такой анимации:

                Dim Anim2 As New DoubleAnimation
                Anim2.From = 0
                Anim2.To = TextBlock.ActualHeight
                Anim2.Duration = TimeSpan.FromSeconds(0.4)
                Storyboard.SetTarget(Anim2, TextBlock)
                Storyboard.SetTargetProperty(Anim2, New PropertyPath(HeightProperty))

    Высота текст блока будет нулевой.


    • Изменено Siompc 17 июня 2012 г. 11:24
    17 июня 2012 г. 11:24

Ответы

  • Ха. Исследование показало что все намного проще. Вот полный код тестового проекта. Берите его за основу.

    XAML:

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ScrollViewer Margin="5" Padding="5" MouseDown="ScrollViewer_MouseDown">
                <StackPanel Name="AnimationStackPanel"></StackPanel>
            </ScrollViewer>
        </Grid>
    </Window>

    VB:

    Class MainWindow 
        Private Sub ScrollViewer_MouseDown(sender As System.Object, e As System.Windows.Input.MouseButtonEventArgs)
            Dim txtblc As New TextBlock
            txtblc.Height = 0
            txtblc.TextWrapping = TextWrapping.Wrap
            txtblc.Text = StrDup(CInt(Int(Rnd() * 200)), "m") & StrDup(CInt(Int(Rnd() * 200)), "M")
            txtblc.Foreground = New SolidColorBrush(Color.FromRgb(CByte(Int(Rnd() * 255)), CByte(Int(Rnd() * 255)), CByte(Int(Rnd() * 255))))
            AddHandler txtblc.SizeChanged, AddressOf txtblc_SizeChanged
            AnimationStackPanel.Children.Add(txtblc)
        End Sub
        Private Sub txtblc_SizeChanged(sender As TextBlock, e As System.Windows.SizeChangedEventArgs)
            Dim anim As New Animation.DoubleAnimation
            anim.From = 0
            anim.To = sender.ActualHeight
            anim.Duration = TimeSpan.FromSeconds(0.4)
            sender.BeginAnimation(TextBlock.HeightProperty, anim)
        End Sub
    End Class
    

    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    • Помечено в качестве ответа Siompc 19 июня 2012 г. 12:12
    18 июня 2012 г. 15:12
    Отвечающий

Все ответы

  • Добрый день.

    На VB не пишу, поэтому на уровне идеи. Попробуйте создать TextBox такой же ширины, но чтобы он не был виден пользователю. Вставьте в него текст, и уже из него берите ActualHeight

    17 июня 2012 г. 15:56
    Отвечающий
  • Высота неизвестна. Текст меняется, соответственно меняется и высота элемента...
    17 июня 2012 г. 17:32
  • Я и говорю, сделайте еще один TextBox в который вставляйте текст параллельно с первым. Ну а на изменение его высоты, уже имя для него ActualHeight, анимируйте изменение высоты первого TextBox который видит пользователь. Только у второго размер шрифта и ширина должны быть как у первого и пользователям его показывать не надо...

    17 июня 2012 г. 18:36
    Отвечающий
  • Алексей я как понимаю если TextBox будет скрыт, то он вернет ActualHeight равный 0, так что это не пройдет.

    Siompc а что именно вы хотите сделать я не пойму. Вам нужно что бы когда добавлялся новый текст в TextBox он плавно расширялся под размер текста?


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    18 июня 2012 г. 3:04
    Отвечающий
  • Алексей я как понимаю если TextBox будет скрыт, то он вернет ActualHeight равный 0, так что это не пройдет.


    Я неверно не очень удачно объяснил. У второго TextBox не свойство Visibility болджно быть Hiden, а он должен быть спрятан под другими компонентами, чтобы не быть выдимым пользователю. Например ему можно задать Margin="-3000, 0, 0, 0"
    18 июня 2012 г. 3:52
    Отвечающий
  • Вам нужно что бы когда добавлялся новый текст в TextBox он плавно расширялся под размер текста?

    Извиняюсь, не заметил что речь о TextBlock и Алексея сбил :)

    Тут есть ряд подводных камней. Во первых что бы текст переносился по строкам нужно TextBlock выставить значение TextWrapping="Wrap". Но тогда он автоматом будет занимать нужную ширину без анимации. Если же это свойство не устанавливать то и с анимацией текст будет в одну строчку.

    Другое дело если вы ставите фиксированную высоту и не хотите что бы элемент расширялся сам, а делать это в ручную анимацией. Тогда ActualHeight вам не поможет потому что он показывает размер уже расширенного элемента. Вам нужно запросить ширину содержимого. Вот примерный код:

        Private Sub Window_MouseDown(sender As System.Object, e As System.Windows.Input.MouseButtonEventArgs)
            Dim anim As New Animation.DoubleAnimation
            anim.From = MyTextBlock.Height
            anim.To = VisualTreeHelper.GetContentBounds(MyTextBlock).Height + 5
            anim.Duration = TimeSpan.FromSeconds(0.4)
            MyTextBlock.BeginAnimation(TextBlock.HeightProperty, anim)
        End Sub

    Но тут сново есть пару НО. Что бы VisualTreeHelper сработал элемент должен визуализироваться на форме с усеченным содержимым, то есть на момент вызова VisualTreeHelper вы уже должны видеть элемент с обрезанным текстом, а иначе он вернет "бесконечность". Поэтому как видите я вызвал этот код в событии MouseDown формы, а текст забросил в TextBlock в событии Loaded. Второй важный момент - код VisualTreeHelper.GetContentBounds(MyTextBlock).Height вернул немного меньше чем надо, поэтому как видите я добавил 5.

    В конечном итоге я рекомендую вам отказаться от затеи в нынешнем виде. Что бы элемент красиво расширялся под текст его нужно переписывать самому как новый контрол, а мой вариант и вариант Алексея это костыли да изолента.


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    18 июня 2012 г. 5:40
    Отвечающий
  • У textblock TextWrapping="Wrap". Анимируется так - при создании, Opacity элемента = 0. После добавления в стек панель элемент должен плавно появиться (Opacity to 1) и плавно увеличиться (Height = ???) до такого размера, что бы весь текст при переносе был виден. В цикле создается несколько таких элементов с разным текстом, в итоге получается плавная анимация заполнения стек панели. Анимация высоты начинается от 0... Как лучше сделать?
    18 июня 2012 г. 10:27
  • Итак в класс окна добавляем такой код (анимацию прозрачности добавите сами):

    Private Sub txtblc_SizeChanged(sender As TextBlock, e As System.Windows.SizeChangedEventArgs)
            Dim anim As New Animation.DoubleAnimation
            anim.From = 0
            anim.To = VisualTreeHelper.GetContentBounds(sender).Height
            anim.Duration = TimeSpan.FromSeconds(0.4)
            sender.BeginAnimation(TextBlock.HeightProperty, anim)
        End Sub

    В место где создаются TextBlock'и добавляем такую строку:

    AddHandler txtblc.SizeChanged, AddressOf txtblc_SizeChanged

    Условно я считаю что новые TextBlock'и именуются txtblc. Теперь пара важных моментов. Если вы создаете их в цикле, то они не будут появятся пока цикл не закончится, та как поток будет блокирован.

    И второй важный момент - при любом изменении размеров контейнеров (окна в том числе) анимация будет происходить снова.


    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    18 июня 2012 г. 12:12
    Отвечающий
  • Пишет "-бесконечность" не является допустимым значением для свойства "To" 

    Не хочет

    18 июня 2012 г. 14:24
  • Ха. Исследование показало что все намного проще. Вот полный код тестового проекта. Берите его за основу.

    XAML:

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ScrollViewer Margin="5" Padding="5" MouseDown="ScrollViewer_MouseDown">
                <StackPanel Name="AnimationStackPanel"></StackPanel>
            </ScrollViewer>
        </Grid>
    </Window>

    VB:

    Class MainWindow 
        Private Sub ScrollViewer_MouseDown(sender As System.Object, e As System.Windows.Input.MouseButtonEventArgs)
            Dim txtblc As New TextBlock
            txtblc.Height = 0
            txtblc.TextWrapping = TextWrapping.Wrap
            txtblc.Text = StrDup(CInt(Int(Rnd() * 200)), "m") & StrDup(CInt(Int(Rnd() * 200)), "M")
            txtblc.Foreground = New SolidColorBrush(Color.FromRgb(CByte(Int(Rnd() * 255)), CByte(Int(Rnd() * 255)), CByte(Int(Rnd() * 255))))
            AddHandler txtblc.SizeChanged, AddressOf txtblc_SizeChanged
            AnimationStackPanel.Children.Add(txtblc)
        End Sub
        Private Sub txtblc_SizeChanged(sender As TextBlock, e As System.Windows.SizeChangedEventArgs)
            Dim anim As New Animation.DoubleAnimation
            anim.From = 0
            anim.To = sender.ActualHeight
            anim.Duration = TimeSpan.FromSeconds(0.4)
            sender.BeginAnimation(TextBlock.HeightProperty, anim)
        End Sub
    End Class
    

    Женат на WPF. Тайно встречаюсь с WinRT. Не сложилось с C#!

    • Помечено в качестве ответа Siompc 19 июня 2012 г. 12:12
    18 июня 2012 г. 15:12
    Отвечающий
  • Спасибо, разобрался :) Да, что бы анимация не повторялась при изменении размера контейнера убираем событие 
    RemoveHandler Element.SizeChanged, AddressOf Me_SizeChanged после начала анимации :)

    • Изменено Siompc 19 июня 2012 г. 12:12
    19 июня 2012 г. 12:12