#wpf #xaml #storyboard #scrollbar #eventtrigger
#wpf #xaml #Раскадровка #полоса прокрутки #eventtrigger
Вопрос:
У меня странное поведение в моем приложении WPF.
Я пытаюсь запустить анимацию толщины в ScrollBar.ValueChanged
событии для анимации поля элемента, но каждый раз, когда изменяется значение полосы прокрутки, анимация привязывается к СТАРОМУ значению полосы прокрутки.
Что приводит к разрыву между значением полосы прокрутки и элементом margin.
<UserControl.Resources>
<Storyboard x:Key="AnimationScrollTest">
<ThicknessAnimation Storyboard.TargetName="ElementTest" Storyboard.TargetProperty="Margin" Duration="0:0:0:0.3"
To="{Binding ElementName=ScrollBarTest, Path=Value, Converter={StaticResource MyVerticalScrollBarValueToMarginConverter}}" />
</Storyboard>
<UserControl.Resources>
<Grid>
<ScrollBar x:Name="ScrollBarTest" Grid.RowSpan="3" Grid.ColumnSpan="2" Orientation="Vertical" Right" VerticalAlignment="Stretch" SmallChange="10" LargeChange="100" Value="0" Maximum="2000" >
<ScrollBar.Triggers>
<EventTrigger RoutedEvent="ScrollBar.ValueChanged" >
<BeginStoryboard Storyboard="{StaticResource AnimationScrollTest}" />
</EventTrigger>
</ScrollBar.Triggers>
</ScrollBar>
<Border x:Name="ElementTest" Grid.RowSpan="3" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Top" Width="50" Height="50" Background="Red"></Border>
</Grid>
public class VerticalScrollBarValueToMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new Thickness(0, (Double)value, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
- Когда EventTrigger связан с событием ScrollBar.Scroll, это работает хорошо.
- Когда в преобразователе добавляется точка останова, объект value в методе Convert принимает правильное значение
- Если поле ElementTest напрямую привязано к значению полосы прокрутки (без анимации), это тоже работает хорошо.
Есть идеи??
Большое спасибо
PS: извините за плохой английский, я француз!
Комментарии:
1. у вас максимальное значение 2000 в полосе прокрутки — это тоже ожидаемое верхнее поле вашего элемента Border? Я получил вашу проблему, проблема связана с зависающим поведением. давайте посмотрим, как мы можем решить то же самое
2. Максимальное значение 2000 приведено только для примера. В основном приложении это значение динамически вычисляется один раз при событии LayoutUpdated. Заранее спасибо
Ответ №1:
Я попытался решить предыдущую проблему со значением с помощью прикрепленных свойств
xaml
<Grid xmlns:l="clr-namespace:CSharpWPF">
<ScrollBar x:Name="ScrollBarTest"
Grid.RowSpan="3"
Grid.ColumnSpan="2"
Orientation="Vertical"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
SmallChange="10"
LargeChange="100"
Value="0"
Maximum="2000">
</ScrollBar>
<Border x:Name="ElementTest"
Grid.RowSpan="3"
Grid.ColumnSpan="2"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Width="50"
Height="50"
Background="Red"
l:ScrollBarToMarginAnimator.ScrollBar="{Binding ElementName=ScrollBarTest}"></Border>
</Grid>
Я удалил раскадровку отсюда и добавил прикрепленное свойство l:ScrollBarToMarginAnimator.ScrollBar
к целевому элементу и привязал к полосе прокрутки ScrollBarTest
класс для прикрепленного свойства
namespace CSharpWPF
{
public class ScrollBarToMarginAnimator : DependencyObject
{
public static ScrollBar GetScrollBar(DependencyObject obj)
{
return (ScrollBar)obj.GetValue(ScrollBarProperty);
}
public static void SetScrollBar(DependencyObject obj, ScrollBar value)
{
obj.SetValue(ScrollBarProperty, value);
}
// Using a DependencyProperty as the backing store for ScrollBar. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ScrollBarProperty =
DependencyProperty.RegisterAttached("ScrollBar", typeof(ScrollBar), typeof(ScrollBarToMarginAnimator), new PropertyMetadata(null, OnScrollBarChanged));
private static void OnScrollBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollBar sb = e.NewValue as ScrollBar;
if (sb != null)
sb.Scroll = (ss, ee) =>
{
ThicknessAnimation ta = new ThicknessAnimation(new Thickness(0, sb.Value, 0, 0), TimeSpan.FromMilliseconds(300));
(d as FrameworkElement).BeginAnimation(FrameworkElement.MarginProperty, ta);
};
}
}
}
в этом классе я прослушиваю Scroll
событие привязанной полосы прокрутки и инициирую ThicknessAnimation
для Margin
свойства присоединенного элемента.
вышеупомянутое решение прослушает любое изменение в полосе прокрутки и отреагирует соответствующим образом, анимируя край границы.
Несколько полос прокрутки
xaml
<Grid xmlns:l="clr-namespace:CSharpWPF">
<ScrollBar x:Name="vertical"
Grid.RowSpan="3"
Grid.ColumnSpan="2"
Orientation="Vertical"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
SmallChange="10"
LargeChange="100"
Value="0"
Maximum="2000"/>
<ScrollBar x:Name="horizontal"
Grid.RowSpan="3"
Grid.ColumnSpan="2"
Orientation="Horizontal"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
SmallChange="10"
LargeChange="100"
Value="0"
Maximum="2000"/>
<Border x:Name="ElementTest"
Grid.RowSpan="3"
Grid.ColumnSpan="2"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Width="50"
Height="50"
Background="Red"
l:ScrollBarToMarginAnimator.Vertical="{Binding ElementName=vertical}"
l:ScrollBarToMarginAnimator.Horizontal="{Binding ElementName=horizontal}"/>
</Grid>
Я добавил еще одну полосу прокрутки и прикрепил два свойства к границе.
cs
namespace CSharpWPF
{
public class ScrollBarToMarginAnimator : DependencyObject
{
public static ScrollBar GetVertical(DependencyObject obj)
{
return (ScrollBar)obj.GetValue(VerticalProperty);
}
public static void SetVertical(DependencyObject obj, ScrollBar value)
{
obj.SetValue(VerticalProperty, value);
}
// Using a DependencyProperty as the backing store for Vertical. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VerticalProperty =
DependencyProperty.RegisterAttached("Vertical", typeof(ScrollBar), typeof(ScrollBarToMarginAnimator),
new PropertyMetadata(null, (d, e) => AttachAnimation(d, e, true)));
public static ScrollBar GetHorizontal(DependencyObject obj)
{
return (ScrollBar)obj.GetValue(HorizontalProperty);
}
public static void SetHorizontal(DependencyObject obj, ScrollBar value)
{
obj.SetValue(HorizontalProperty, value);
}
// Using a DependencyProperty as the backing store for Horizontal. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HorizontalProperty =
DependencyProperty.RegisterAttached("Horizontal", typeof(ScrollBar), typeof(ScrollBarToMarginAnimator),
new PropertyMetadata(null, (d, e) => AttachAnimation(d, e, false)));
private static void AttachAnimation(DependencyObject d, DependencyPropertyChangedEventArgs e, bool isVertical)
{
ScrollBar sb = e.NewValue as ScrollBar;
if (sb != null)
sb.Scroll = (ss, ee) =>
{
FrameworkElement fw = d as FrameworkElement;
Thickness newMargin = fw.Margin;
if (isVertical)
newMargin.Top = sb.Value;
else
newMargin.Left = sb.Value;
ThicknessAnimation ta = new ThicknessAnimation(newMargin, TimeSpan.FromMilliseconds(300));
fw.BeginAnimation(FrameworkElement.MarginProperty, ta);
};
}
}
}
Я создал два свойства и прикрепил анимацию с помощью флага
Комментарии:
1. Спасибо за этот ответ. Прослушивание события valueChanged, это хороший обходной путь. Однако в основном приложении у меня есть две полосы прокрутки (горизонтальная и вертикальная), анимирующие поле. До этого в XAML я использовал многозначный преобразователь в ThicknessAnimations для привязки значения полосы прокрутки И текущего поля объекта, чтобы создать новое поле с обоими. Но теперь я не знаю, как управлять двумя полосами прокрутки, анимируя границы одного элемента с помощью одного объекта зависимости. Любая помощь?
2. Я помогу вам прикрепить и другую, позвольте мне найти компьютер. Тем временем вы можете попробовать создать другое свойство и прикрепить его к границе, а также присвоить значение другой полосе прокрутки и прикрепить событие для ее анимации. Попробуйте, пока я пытаюсь это сделать.
3. Да, но при событии valueChanged на первой полосе прокрутки я не могу получить значение второй полосы прокрутки для генерации правильной толщины. Прикрепление двух свойств к границе, похоже, создает два объекта зависимостей, каждый с одной полосой прокрутки, а не с другой. Возможно, я сделал что-то не так… 🙂 Я попытался изменить свойство на object[] one и установить для этого свойства множественную привязку, содержащую две полосы прокрутки. Это не работает, object [0] и object[1] полностью настроены в конвертере, и оба имеют значение null в DependencyObject.
4. Отличный ответ, большое спасибо pushpraj! Я просто добавляю RoutedEvent к DependencyObject для управления окончанием анимации.
5. Вы тоже проделали отличную работу! удачного кодирования 🙂