在WPF上使用ViewLocator来自动定位View

在 Avalonia 中,ViewLocator 是一个用于解析与特定视图模型对应的视图(用户界面)的机制。
例如,给定一个名为 MyApplication.ViewModels.ExampleViewModel 的视图模型,视图定位器将查找一个名为 MyApplication.Views.ExampleView 的视图。
视图定位器通常与DataContext属性一起使用,该属性用于将视图与其视图模型关联起来。

而在WPF中,我们也可以通过继承ResourceDictionary,创建 DataTemplate 来实现这个功能。

创建 ViewLocator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ViewLocator: ResourceDictionary
{
public static ViewLocator Instance = new ViewLocator();

private ViewLocator()
{
}

public DataTemplate Locate<TVM,TView>()
{
return Locate(typeof(TVM),typeof(TView));
}

public DataTemplate Locate(Type t1,Type t2)
{
DataTemplate dt = new DataTemplate()
{
DataType = t1,
};
dt.VisualTree = new FrameworkElementFactory(t2);
try
{
this.Add(dt.DataTemplateKey, dt);

}
catch (System.ArgumentException)
{
//nothing
}
return dt;
}
}

使用 ViewLocator

关联 View 和 ViewModel

1
ViewLocator.Instance.Locate<FooViewModel,FooView>();

放置一个 ContentControl 到想要切换视图的区域,或者直接对 Window 使用。

1
<ContentControl x:Name="content_area" Resources="{x:Static local:ViewLocator.Instance}" />

切换视图

1
content_area.Content = new FooViewModel();

如果一切正常,此时 ContentControl 里显示的就是 FooView 控件的内容,而且 FooView 的 DataContext 也会自动关联 FooViewModel 。

为 WPF 的 ScrollViewer 添加滚动动画

在 WPF 中,ScrollViewer 默认没有直接支持滚动动画的属性。不过,我们可以通过自定义附加属性和动画来实现滚动动画效果。

定义附加属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

public static class ScrollViewerBehavior
{
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(ScrollViewerBehavior),
new UIPropertyMetadata(0.0, OnHorizontalOffsetChanged));

public static void SetHorizontalOffset(FrameworkElement target, double value) =>
target.SetValue(HorizontalOffsetProperty, value);

public static double GetHorizontalOffset(FrameworkElement target) =>
(double)target.GetValue(HorizontalOffsetProperty);

private static void OnHorizontalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) =>
(target as ScrollViewer)?.ScrollToHorizontalOffset((double)e.NewValue);

public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(ScrollViewerBehavior),
new UIPropertyMetadata(0.0, OnVerticalOffsetChanged));

public static void VerticalOffset(FrameworkElement target, double value) =>
target.SetValue(VerticalOffsetProperty, value);

public static double GetVerticalOffset(FrameworkElement target) =>
(double)target.GetValue(VerticalOffsetProperty);

private static void OnVerticalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) =>
(target as ScrollViewer)?.ScrollToVerticalOffset((double)e.NewValue);

}

设置动画:

1
2
3
4
5
6
7
8
9
10
<Storyboard x:Key="ScrollStoryboard">
<DoubleAnimation Storyboard.TargetName="ScrollViewer"
Storyboard.TargetProperty="(YourNamespace:ScrollViewerBehavior.HorizontalOffset)"
From="0" To="500" Duration="0:0:0.6">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>