In the last post we created a ScrollViewer Thumbnail feature using a just a bit of Xaml and databinding magic.

Since it's a pretty useful feature, it makes sense to 'Controllerize' it and make it more reusable. Originally, I was just planning to do this using a UserControl as this is a very lightweight way to Controllerize a bit of Xaml. However, creating a full custom control isn't much harder so it makes sense go that route.

We need to add a bunch of files to our new class library to get this started. First, we need a simple class that inherits from Control

public class ScrollViewerThumbnail : Control
{
    static ScrollViewerThumbnail()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerThumbnail), newFrameworkPropertyMetadata(typeof(ScrollViewerThumbnail)));
    }

public ScrollViewer ScrollViewer
    {
        get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
        set { SetValue(ScrollViewerProperty, value); }
    }

// Using a DependencyProperty as the backing store for ScrollViewer. This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ScrollViewerProperty =
    DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(ScrollViewerThumbnail), newUIPropertyMetadata(null));
    
}

There's only two things going on in there. One is a new dependency property of type ScollViewer. We'll use this to specify the ScrollViewer whose content we should thumbnail. The other part is a static constructor that overrides the default style key so we can find our default template.

Next, we need a Generic.xaml file that should sit inside a 'Themes' folder:

Note: the Generic.xaml file's build action must be set to Page in the properties panel.

And here's the contents of our Generic.xaml file - it's just a resource dictionary with a single style that targets our ScrollViewerThumbnail type.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Controls="clr-namespace:TheJoyOfCode.Wpf.Controls">

<Style TargetType="{x:Type Controls:ScrollViewerThumbnail}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Controls:ScrollViewerThumbnail}">
                    <Viewbox DataContext="{TemplateBinding ScrollViewer}" Stretch="Uniform">
                        <Grid>
                            <Rectangle
                                Width="{Binding Content.ActualWidth}" 
                                Height="{Binding Content.ActualHeight}">
                                <Rectangle.Fill>
                                    <VisualBrush Visual="{Binding Content}" />
                                </Rectangle.Fill>
                            </Rectangle>
                            <Border
                                Background="{TemplateBinding HighlightFill}" 
                                Width="{Binding ViewportWidth}" 
                                Height="{Binding ViewportHeight}"
                                HorizontalAlignment="Left" 
                                VerticalAlignment="Top">
                                <Border.RenderTransform>
                                    <TranslateTransform 
                                        X="{Binding HorizontalOffset}" 
                                        Y="{Binding VerticalOffset}" />
                                </Border.RenderTransform>
                            </Border>
                        </Grid>
                    </Viewbox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

This style has only one Setter that sets the Template of the control to a new ControlTemplate. From then on the xaml (inside the ControlTemplate) is almost identical to the previous example with two notable differences.

1. The DataContext of the Viewbox is now a TemplateBinding instead of a normal binding. This directly targets the ScrollViewer dependency property we created earlier.

2. The Background of the highlight is also a TemplateBinding that uses the HighlightFill property of our ScrollViewerThumbnail. Mmmm, but we didn't have a HighlightFill property. We better create that now (inside the ScrollViewerThumbnail class):

public Brush HighlightFill
{
    get { return (Brush)GetValue(HighlightFillProperty); }
    set { SetValue(HighlightFillProperty, value); }
}

public static readonly DependencyProperty HighlightFillProperty = 
    DependencyProperty.Register("HighlightFill", 
        typeof(Brush), 
        typeof(ScrollViewerThumbnail), 
        new UIPropertyMetadata(new SolidColorBrush(Color.FromArgb(128,255,255,0))));

Done. Notice that we specified a default fill of transparent yellow. Cool.

We're almost done. One really important last thing: You must add the following code to your assemblyinfo.cs file so WPF knows where to go looking for our default template:

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
    //(used if a resource is not found in the page, 
    // or application resource dictionaries)
    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
    //(used if a resource is not found in the page, 
    // app, or any theme specific resource dictionaries)
)]

Using the new ScrollViewerThumbnail control

... couldn't be easier!

<Grid>
    <ScrollViewer x:Name="scrollViewer" HorizontalScrollBarVisibility="Auto">
        <!-- Your ScrollViewer content here as normal -->
    </ScrollViewer>
    <Controls:ScrollViewerThumbnail ScrollViewer="{Binding ElementName=scrollViewer}" 
        Width="150" Height="150" 
        HorizontalAlignment="Right" VerticalAlignment="Bottom" 
        Margin="10" />
</Grid>

We just bind the ScrollViewer proprety of the ScrollViewerThumbnail to a ScrollViewer. Done.

Next we'll look at making the ScrollViewerThumbnail interactive.

=============================================================================

In the last post we took the simple ScrollViewer thumbnail and controllerized it. This time, we're going to make it interactive.

And, because WPF totally rocks, it's stupidly easy to do.

Normally, we'd do lots of mouse capture, remember the original location and the new location to calculate the delta etc etc. With WPF, we can simply use the Thumb control that fires a DragDelta event if the user makes a drag gesture over the control. Huzzah.

So, we need to quickly change the ScrollViewerThumbnail's template in Generic.xaml to look like this:

<ControlTemplate TargetType="{x:Type Controls:ScrollViewerThumbnail}">
    <Viewbox DataContext="{TemplateBinding ScrollViewer}" Stretch="Uniform">
        <Grid>
            <Rectangle
                Width="{Binding Content.ActualWidth}" 
                Height="{Binding Content.ActualHeight}">
                <Rectangle.Fill>
                    <VisualBrush Visual="{Binding Content}" />
                </Rectangle.Fill>
            </Rectangle>
            <Thumb Name="PART_Highlight"
                Background="{TemplateBinding HighlightFill}"
                Width="{Binding ViewportWidth}" 
                Height="{Binding ViewportHeight}"
                HorizontalAlignment="Left" 
                VerticalAlignment="Top">
                <Thumb.RenderTransform>
                    <TranslateTransform 
                        X="{Binding HorizontalOffset}" 
                        Y="{Binding VerticalOffset}" />
                </Thumb.RenderTransform>
                <Thumb.Template>
                    <ControlTemplate TargetType="Thumb">
                        <Border Background="{TemplateBinding Background}" />
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>
        </Grid>
    </Viewbox>
</ControlTemplate>

Note that we've changed the highlight from a Border to a Thumb. We've named it PART_Highlight and I've given the thumb a ControlTemplate that is a simple Border (Thumbs can't have content).

Next, we need to find the PART_Highlight in our ScrollViewerThumbnail template and attach a handler to it's DragDelta event. We can't just use this.FindName("PART_Highlight") because it's part of a template. We have to override the OnApplyTemplate method of the ScrollViewerThumbnail class and use this.Template.FindName("PART_Highlight"):

private const string PART_Highlight = "PART_Highlight";

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

var partHighlight = (Thumb)this.Template.FindName(PART_Highlight, this);
    partHighlight.DragDelta += partHighlight_DragDelta;
}

Next, we need to move the ScrollViewer when the DragDelta event occurs.

void partHighlight_DragDelta(object sender, DragDeltaEventArgs e)
{
    ScrollViewer.ScrollToVerticalOffset(ScrollViewer.VerticalOffset + e.VerticalChange);
    ScrollViewer.ScrollToHorizontalOffset(ScrollViewer.HorizontalOffset + e.HorizontalChange);
}

And we're done. Seriously! I'm blown away that this can be achieved with just four-ish lines of code.

Just click on the Arrows icon to display a popup showing the ScrollViewer thumbnail.

PS - you can use the mouse wheel to change the size of the ScrollViewerThumbnail control too.

Controllerizing the ScrollViewer Thumbnail的更多相关文章

  1. 获取文件的缩略图Thumbnail和通过 AQS - Advanced Query Syntax 搜索本地文件

    演示如何获取文件的缩略图 FileSystem/ThumbnailAccess.xaml <Page x:Class="XamlDemo.FileSystem.ThumbnailAcc ...

  2. WPF CheckBox样式 ScrollViewer样式 WrapPanel、StackPanel、Grid布局

    本节讲述布局,顺带加点样式给大家看看~单纯学布局,肯定是枯燥的~哈哈 那如上界面,该如何设计呢? 1.一些布局元素经常用到.Grid StackPanel Canvas WrapPanel等.如上这种 ...

  3. WPF 自定义滑动ScrollViewer

    自定义滑动滚动条 预期目标:横向滚动条,可以左右滑动,用鼠标按住(触摸)然后释放可以实现快速滑动. 我们有几种方案: 1.ScrollViewer,修改其中的横向滚动条,将其中的背景设置为透明. 但是 ...

  4. Windows下Thumbnail的开发总结

    一.引言 Windows Thumbnail Handler是Windows平台下用来为关联的文件类型提供内容预览图的一套COM接口.通过实现Thumbnail相关的COM接口,就可以为为自定义的文件 ...

  5. 微信支付报错:Invalid thumbnail dimensions: 0x0

    微信支付的 android 端在支付的时候闪退, 调试信息: 10-08 10:17:08.459 2127-3946/? E/ActivityManager: Invalid thumbnail d ...

  6. WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Scr ...

  7. ItemsSource绑定后ScrollViewer不复位

    ItemsSource绑定后ScrollViewer不复位 ItemsSource绑定后ScrollViewer不复位,有的时候我们需要这一效果,但大多数情况下我们是想让它复位的. 在WPF中也有这个 ...

  8. wpf,ListBox,ScrollViewer内容向左向右偏移指定位置

    public partial class Example : UserControl { private ScrollViewer myScrollViewer; public Example() { ...

  9. Change the Windows 7 Taskbar Thumbnail and List Mode

    Manually in Registry Editor 1. Open the Start Menu, then type regedit in the search boxand press Ent ...

随机推荐

  1. rpx

    rpx(responsive pixel): 可以根据屏幕宽度进行自适应.规定屏幕宽为750rpx.如在iPhone6上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = ...

  2. ITOO高校云平台V3.1--项目总结(二)

    自身责任要明白 心态要明白 布置任务要有反馈 总结 今天下午.举办了一场ITOO高校云平台3.1总结大会,针对3.1开发的过程中统计上来的问题进行讨论. 通过讨论统计上来的问题,映射到自身,看看自己还 ...

  3. 网页与APP中那些优美的登陆表单

    我从Dribbble收集了20个漂亮的登陆表单案例.希望你看后能从中受益,并对你以后的登陆表单设计有帮助.设计一个登陆表单是非常容易,但大多设计都很糟糕.毫无亮点.无论如何,这篇Dribbble案例集 ...

  4. Tsk4.5异步

    public async void LoadData<T>(WhereClip where,OrderByClip order) where T : Entity, new() { try ...

  5. 【Unity】9.1 导入粒子系统组件

    分类:Unity.C#.VS2015 创建日期:2016-05-02 一.简介 Unity 5.x已经自带了粒子系统组件,新建工程后只需要直接导入它就可以使用了. 二.导入Unity自带的粒子系统组件 ...

  6. 菜鸟学EJB(一)——第一个实例

    EJB用了那么长时间了,从来没写过关于它的东西,挺对不住它的.今天先写一个简单的小实例,虽然小但是却能体现出EJB的核心——分布式.我们可以将业务逻辑的接口跟实现部署到一台机器上,将调用它们的客户端部 ...

  7. 技术范儿的 Keep 发力AI赛道,为什么“虚拟教练”会更懂你?

    http://www.tmtpost.com/3363367.html 摘要: 虚拟教练技术会整合到一些业务场景和硬件产品中收费,但是收费的具体情况彭跃辉还暂未透露. 图片来源于Unsplash 自去 ...

  8. glob 文件或目录查找

    glob模块是最简单的模块之一,内容非常少.用它可以查找符合特定规则的文件或目录(含相对或绝对路径).跟使用windows下的文件搜索差不多. 在python中,glob模块是用来查找匹配的文件的 在 ...

  9. 设置Chrome忽略网站证书错误

    本人在XP下使用Chrome.总是莫名其妙的提示整数错误,一部分https网站无法直接访问.网上找了下,把解决思路记录下来. 解决这个问题很简单,只需要修改你平时用来启动Chrome的快捷方式就可以忽 ...

  10. IP段对应表

    IP段对应表   IP总数 子网掩码 C段个数 /30 4 255.255.255.252 1/64 /29 8 255.255.255.248 1/32 /28 16 255.255.255.240 ...