UWP 图片剪切旋转工具
好久没撸随笔了,明天终于放假休息了。。准备去进行信仰充值,看《魔兽》去(话说surface phone 好久出,让我这个做UWP的也充点信仰。。)
先上下效果图:
在设计中,遇到一个问题,就是如果添加了剪切蒙版之后会挡住后面的ScrollViewer里面的Image,一些事件将无法监听。
后面想了个办法,把ScrollViewer的模板给修改了。
开源有益,先上代码
下面是Theme
<Style x:Key="ImageToolScrollViewerStyle" TargetType="ScrollViewer"> <Setter Property="HorizontalScrollMode" Value="Enabled"/> <Setter Property="VerticalScrollMode" Value="Enabled"/> <!--<Setter Property="IsHorizontalRailEnabled" Value="False"/> <Setter Property="IsVerticalRailEnabled" Value="False"/>--> <Setter Property="IsTabStop" Value="False"/> <Setter Property="ZoomMode" Value="Enabled"/> <Setter Property="HorizontalAlignment" Value="Stretch"/> <Setter Property="VerticalAlignment" Value="Stretch"/> <Setter Property="HorizontalContentAlignment" Value="Stretch"/> <Setter Property="VerticalContentAlignment" Value="Stretch"/> <Setter Property="VerticalScrollBarVisibility" Value="Hidden"/> <Setter Property="HorizontalScrollBarVisibility" Value="Hidden"/> <Setter Property="Padding" Value="0"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="BorderBrush" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ScrollViewer"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ScrollingIndicatorStates"> <VisualStateGroup.Transitions> <VisualTransition From="MouseIndicator" To="NoIndicator"> <Storyboard> <FadeOutThemeAnimation BeginTime="0:0:3" TargetName="ScrollBarSeparator"/> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="VerticalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0:0:3"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>None</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="HorizontalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0:0:3"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>None</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition From="TouchIndicator" To="NoIndicator"> <Storyboard> <FadeOutThemeAnimation TargetName="ScrollBarSeparator"/> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="VerticalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0:0:0.5"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>None</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="HorizontalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0:0:0.5"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>None</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualTransition> </VisualStateGroup.Transitions> <VisualState x:Name="NoIndicator"> <Storyboard> <FadeOutThemeAnimation TargetName="ScrollBarSeparator"/> </Storyboard> </VisualState> <VisualState x:Name="TouchIndicator"> <Storyboard> <FadeOutThemeAnimation TargetName="ScrollBarSeparator"/> <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="VerticalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="HorizontalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="MouseIndicator"> <Storyboard> <FadeInThemeAnimation TargetName="ScrollBarSeparator"/> <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="VerticalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IndicatorMode" Storyboard.TargetName="HorizontalScrollBar"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid Background="{TemplateBinding Background}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ScrollContentPresenter x:Name="ScrollContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding ContentTemplate}" Margin="{TemplateBinding Padding}" Grid.RowSpan="2"/> <ScrollBar x:Name="VerticalScrollBar" Grid.Column="1" HorizontalAlignment="Right" IsTabStop="False" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{TemplateBinding VerticalOffset}" ViewportSize="{TemplateBinding ViewportHeight}"/> <ScrollBar x:Name="HorizontalScrollBar" IsTabStop="False" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{TemplateBinding HorizontalOffset}" ViewportSize="{TemplateBinding ViewportWidth}"/> <Border x:Name="ScrollBarSeparator" Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}" Grid.Column="1" Grid.Row="1"/> <Canvas x:Name="CropSelectionCanvas" Grid.ColumnSpan="2" Visibility="{Binding CropSelectionVisibility}"> <Path x:Name="nonselectRegion" Fill="#88FFFFFF" > <Path.Data> <GeometryGroup> <RectangleGeometry Rect="{Binding OuterRect}"> </RectangleGeometry> <RectangleGeometry Rect="{Binding SelectedRect}"> </RectangleGeometry> </GeometryGroup> </Path.Data> </Path> <Path x:Name="selectRegion" Fill="Transparent" Stroke="{ThemeResource ApplicationForegroundThemeBrush}" StrokeThickness="1"> <Path.Data> <RectangleGeometry Rect="{Binding SelectedRect}"/> </Path.Data> </Path> <Rectangle x:Name="horizontalLine" Canvas.Left="{Binding SelectedRect.Left}" Canvas.Top="{Binding HorizontalLineCanvasTop}" Height="1" Width="{Binding SelectedRect.Width}" Fill="{ThemeResource ApplicationForegroundThemeBrush}"/> <Rectangle x:Name="verticalLine" Canvas.Left="{Binding VerticalLineCanvasLeft}" Canvas.Top="{Binding SelectedRect.Top}" Width="1" Height="{Binding SelectedRect.Height}" Fill="{ThemeResource ApplicationForegroundThemeBrush}"/> <Rectangle x:Name="horizontalLine1" Canvas.Left="{Binding SelectedRect.Left}" Canvas.Top="{Binding HorizontalLine1CanvasTop}" Height="1" Width="{Binding SelectedRect.Width}" Fill="{ThemeResource ApplicationForegroundThemeBrush}"/> <Rectangle x:Name="verticalLine1" Canvas.Left="{Binding VerticalLine1CanvasLeft}" Canvas.Top="{Binding SelectedRect.Top}" Width="1" Height="{Binding SelectedRect.Height}" Fill="{ThemeResource ApplicationForegroundThemeBrush}"/> </Canvas> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="local:ImageTool"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:ImageTool"> <Grid > <Grid.Resources> <Style TargetType="Ellipse"> <Setter Property="Height" Value="30"/> <Setter Property="Width" Value="30"/> <Setter Property="Fill" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}"/> <Setter Property="Stroke" Value="{ThemeResource ApplicationForegroundThemeBrush}"/> <Setter Property="StrokeThickness" Value="1"/> <Setter Property="RenderTransform"> <Setter.Value> <CompositeTransform TranslateX="-15" TranslateY="-15" /> </Setter.Value> </Setter> </Style> </Grid.Resources> <Image x:Name="sourceImage" Visibility="Visible" Opacity="0"/> <Canvas x:Name="imageCanvas" HorizontalAlignment="Stretch" IsHitTestVisible="{Binding CropSelectionVisibility,Converter={StaticResource InversedBooleanToVisibilityConverter}}" VerticalAlignment="Stretch"> <ScrollViewer x:Name="scrollViewer" MinZoomFactor="1" MaxZoomFactor="2" Style="{StaticResource ImageToolScrollViewerStyle}"> <Grid x:Name="imageGrid" > <Image x:Name="editImage" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </ScrollViewer> <Ellipse x:Name="topLeftThumb" Visibility="{Binding CropSelectionVisibility}" Canvas.Left="{Binding SelectedRect.Left}" Canvas.Top="{Binding SelectedRect.Top}"/> <Ellipse x:Name="topRightThumb" Visibility="{Binding CropSelectionVisibility}" Canvas.Left="{Binding SelectedRect.Right}" Canvas.Top="{Binding SelectedRect.Top}"/> <Ellipse x:Name="bottomLeftThumb" Visibility="{Binding CropSelectionVisibility}" Canvas.Left="{Binding SelectedRect.Left}" Canvas.Top="{Binding SelectedRect.Bottom}"/> <Ellipse x:Name="bottomRightThumb" Visibility="{Binding CropSelectionVisibility}" Canvas.Left="{Binding SelectedRect.Right}" Canvas.Top="{Binding SelectedRect.Bottom}"/> </Canvas> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
可以看到我把蒙版加到了ScrollViewer的模板里面。这样就不会阻止一些事件了。
需要的朋友可以在这里下载代码。
有啥问题,或者更好的想法可以跟我讲,大家一起进步。
放假几天有空把另外2个控件也写了。先放图给大家see see。
VirtualizedVariableSizedGridView 主要给平板,PC写的,phone上面跟ListView一样。
DataGrid, Touch模式支持下拉刷新,行列锁定,sort等功能