Scrollviewer边缘模糊效果,opacitymask无法正常工作

Wil*_*Haw 4 wpf scrollviewer

我想在不使用滚动条的情况下为触摸屏应用程序创建自定义scrollviewer控件.为了让用户知道他们可以滚动内容,我使用opacitymask以线性渐变淡化滚动查看器的底部和顶部.这一切都很好,除了滚动查看器之外的opacitymask应用于文本块的问题!

我的意思是,我希望将渐弱效果应用于scrollviewer的顶部1%和底部1%,然后滚动查看器的中间部分将可见.然而,问题是,即使我在文本块上设置了OpacityMask ="{x:Null}",滚动查看器中的控件也会发生这种效果.

我已经尝试将opacitymask应用到scrollviewer的外部,但同样的问题发生了.Opacitymask属性是否也适用于孩子?有没有更好的方法来做这种褪色效果?

这是我正在使用的代码:

<Grid Width="200" Height="130">
    <ScrollViewer BorderThickness="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Padding="2"
                           HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Hidden" >
        <ScrollViewer.OpacityMask>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="Transparent" Offset="0" />
                <GradientStop Color="Black" Offset="0.1" />
                <GradientStop Color="Black" Offset="0.9" />
                <GradientStop Color="Transparent" Offset="1" />
            </LinearGradientBrush>
        </ScrollViewer.OpacityMask>
        <TextBlock Margin="0,10" Style="{StaticResource textSmall}" TextWrapping="Wrap">
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
        </TextBlock>
    </ScrollViewer>
</Grid>
Run Code Online (Sandbox Code Playgroud)

Nat*_*han 6

我知道这是一个较老的问题,但我遇到了这个问题,因为我试图做类似的事情.所以我想我会为下一个人发布我的解决方案.对我的解决方案的任何反馈表示赞赏

在我们的应用程序中,我们的大多数ScrollViewer控件都位于非滚动纹理之上,因此我们希望可滚动内容在ScrollViewer边缘淡入到该背景中,但只有在该方向上有更多内容时才会这样.此外,我们至少有一个2轴可滚动区域,用户可以在每个方向上平移.它也必须在那种情况下工作.我们的应用程序也没有真正的滚动条,但我已经离开了我在这里提出的解决方案(它不会影响解决方案).

该解决方案的特点:

  1. 如果ScrollViewer的那一侧内容当前不可见,则会淡化ScrollViewer中内容的边缘.

  2. 滚动靠近内容边缘时,减小淡入淡出效果的强度.

  3. 可以控制褪色边缘的外观.具体来说,你可以控制:

    1. 褪色边缘的厚度
    2. 内容在最外边缘的不透明程度(或淡化程度如何"强烈")
    3. 在边缘附近滚动时,淡入淡出效果消失的速度有多快

基本思想是在ScrollViewer模板中的可滚动内容上控制不透明蒙板.不透明蒙版包含透明外边框和内部不透明边框,其中应用了BlurEffect以获得边缘处的渐变效果.然后,当您向左滚动以控制渐变沿着特定边缘出现的"深度"时,会操纵内边框的边距.

此解决方案是ScrollViewer的子类,需要您指定对ScrollViewer模板的更改.ScrollContentPresenter需要包装在名为"PART_ScrollContentPresenterContainer"的边框内.

FadingScrollViewer类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;

namespace ScrollViewerTest
{
  public class FadingScrollViewer : ScrollViewer
  {
    private const string PART_SCROLL_PRESENTER_CONTAINER_NAME = "PART_ScrollContentPresenterContainer";

    public double FadedEdgeThickness { get; set; }
    public double FadedEdgeFalloffSpeed { get; set; }
    public double FadedEdgeOpacity { get; set; }

    private BlurEffect InnerFadedBorderEffect { get; set; }
    private Border InnerFadedBorder { get; set; }
    private Border OuterFadedBorder { get; set; }



    public FadingScrollViewer()
    {
      this.FadedEdgeThickness = 20;
      this.FadedEdgeFalloffSpeed = 4.0;
      this.FadedEdgeOpacity = 0.0;

      this.ScrollChanged += FadingScrollViewer_ScrollChanged;
      this.SizeChanged += FadingScrollViewer_SizeChanged;
    }



    private void FadingScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
      if (this.InnerFadedBorder == null)
        return;

      var topOffset = CalculateNewMarginBasedOnOffsetFromEdge(this.VerticalOffset); ;
      var bottomOffset = CalculateNewMarginBasedOnOffsetFromEdge(this.ScrollableHeight - this.VerticalOffset);
      var leftOffset = CalculateNewMarginBasedOnOffsetFromEdge(this.HorizontalOffset);
      var rightOffset = CalculateNewMarginBasedOnOffsetFromEdge(this.ScrollableWidth - this.HorizontalOffset);

      this.InnerFadedBorder.Margin = new Thickness(leftOffset, topOffset, rightOffset, bottomOffset);
    }



    private double CalculateNewMarginBasedOnOffsetFromEdge(double edgeOffset)
    {
      var innerFadedBorderBaseMarginThickness = this.FadedEdgeThickness / 2.0;
      var calculatedOffset = (innerFadedBorderBaseMarginThickness) - (1.5 * (this.FadedEdgeThickness - (edgeOffset / this.FadedEdgeFalloffSpeed)));

      return Math.Min(innerFadedBorderBaseMarginThickness, calculatedOffset);
    }



    private void FadingScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      if (this.OuterFadedBorder == null || this.InnerFadedBorder == null || this.InnerFadedBorderEffect == null)
        return;

      this.OuterFadedBorder.Width = e.NewSize.Width;
      this.OuterFadedBorder.Height = e.NewSize.Height;

      double innerFadedBorderBaseMarginThickness = this.FadedEdgeThickness / 2.0;
      this.InnerFadedBorder.Margin = new Thickness(innerFadedBorderBaseMarginThickness);
      this.InnerFadedBorderEffect.Radius = this.FadedEdgeThickness;
    }



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

      BuildInnerFadedBorderEffectForOpacityMask();
      BuildInnerFadedBorderForOpacityMask();
      BuildOuterFadedBorderForOpacityMask();
      SetOpacityMaskOfScrollContainer();
    }



    private void BuildInnerFadedBorderEffectForOpacityMask()
    {
      this.InnerFadedBorderEffect = new BlurEffect()
        {
          RenderingBias = RenderingBias.Performance,
        };
    }



    private void BuildInnerFadedBorderForOpacityMask()
    {
      this.InnerFadedBorder = new Border()
        {
          Background = Brushes.Black,
          HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
          VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
          Effect = this.InnerFadedBorderEffect,
        };
    }



    private void BuildOuterFadedBorderForOpacityMask()
    {
      byte fadedEdgeByteOpacity = (byte)(this.FadedEdgeOpacity * 255);

      this.OuterFadedBorder = new Border()
        {
          Background = new SolidColorBrush(Color.FromArgb(fadedEdgeByteOpacity, 0, 0, 0)),
          ClipToBounds = true,
          Child = this.InnerFadedBorder,
        };
    }



    private void SetOpacityMaskOfScrollContainer()
    {
      var opacityMaskBrush = new VisualBrush()
        {
          Visual = this.OuterFadedBorder
        };

      var scrollContentPresentationContainer = this.Template.FindName(PART_SCROLL_PRESENTER_CONTAINER_NAME, this) as UIElement;

      if (scrollContentPresentationContainer == null)
        return;

      scrollContentPresentationContainer.OpacityMask = opacityMaskBrush;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这是使用控件的XAML,对所需的默认ScrollViewer模板进行了最小的更改(它是ScrollContentPresenter周围的边框).

<local:FadingScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" Margin="10" FadedEdgeThickness="20" FadedEdgeOpacity="0" FadedEdgeFalloffSpeed="4">
  <local:FadingScrollViewer.Template>
    <ControlTemplate TargetType="{x:Type ScrollViewer}">
      <Grid x:Name="Grid" Background="{TemplateBinding Background}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*"/>
          <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="*"/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Border x:Name="PART_ScrollContentPresenterContainer">
          <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
        </Border>

        <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
        <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
      </Grid>
    </ControlTemplate>
  </local:FadingScrollViewer.Template>


  <!-- Your content here -->

</local:FadingScrollViewer>
Run Code Online (Sandbox Code Playgroud)

请注意FadedScrollViewer上的这些附加属性:FadedEdgeThickness,FadedEdgeOpacity和FadedEdgeFalloffSpeed

  • FadedEdgeThickness:您希望淡入淡出的厚度(以像素为单位)
  • FadedEdgeOpacity:你想要淡化的最外边缘是多么不透明.0 =边缘处完全透明,1 =边缘处根本不会褪色
  • FadedEdgeFalloffSpeed:控制褪色边缘在接近它时消失的速度.值越高,淡出越慢.


Ben*_*ier 2

您可以在同一网格内的 ScrollViewer 顶部添加一个具有透明渐变的控件,并将其“IsHitTestVisible”设置为 false 以实现此效果。以下是示例的修改版本,在 ScrollViewer 顶部使用 Canvas 控件:

<Grid Width="200" Height="130">
            <ScrollViewer BorderThickness="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Padding="2"
                           HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Hidden"  Background="LightBlue">
                <TextBlock Margin="10,30,10,30" TextWrapping="Wrap" OpacityMask="Black" VerticalAlignment="Center">
                Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
                </TextBlock>
            </ScrollViewer>
            <Canvas Width="200" Height="130" Focusable="False" IsEnabled="False" IsHitTestVisible="False">
                <Canvas.Background>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                        <GradientStop Color="#FFFFFFFF" Offset="0" />
                        <GradientStop Color="#00FFFFFF" Offset="0.1" />
                        <GradientStop Color="#00FFFFFF" Offset="0.9" />
                        <GradientStop Color="#FFFFFFFF" Offset="1" />
                    </LinearGradientBrush>
                </Canvas.Background>
            </Canvas>
        </Grid>
Run Code Online (Sandbox Code Playgroud)

我为 ScrollViewer 添加了背景颜色,并为 TextBlock 添加了较大的上边距,以便很容易确认所需的效果。输出如下: 替代文本