You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Avalonia中LibVLCSharp的VideoView在ScrollViewer内滚动异常问题

Avalonia中ScrollViewer内LibVLCSharp VideoView滚动时位置异常的问题

在Avalonia的UserControl中,ScrollViewer内部使用LibVLCSharp的VideoView时出现以下问题:滚动ScrollViewer时,VideoView(及其内部控件)会停留在固定位置不跟随滚动,其他元素滚动正常;但拖动窗口后,VideoView又会回到正确的布局位置。

已尝试过调用Render()等重绘方法,以及滚动时将窗口上下移动1像素的临时方案,但这些方法都不适合滚动场景,寻求有效解决办法。

对应的XAML代码如下:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="using:LauncherSIPAvalonia.ViewModels"
             xmlns:views="using:LauncherSIPAvalonia.Views"
             xmlns:lvs="using:LibVLCSharp.Avalonia"
             mc:Ignorable="d"
             x:Class="LauncherSIPAvalonia.Views.LearnMore.LearnMoreView">
  <ScrollViewer>
    <StackPanel Margin="40"
                Orientation="Vertical">
      <Grid HorizontalAlignment="Left"
            ColumnDefinitions="1.2* *">
        <Grid RowDefinitions="3* *"
              Grid.Column="0">
          <Border CornerRadius="6"
                  Width="640"
                  Height="360">
            <lvs:VideoView x:Name="Video"
                            Width="640"
                            Height="360"
                            IsVisible="False">
              <Grid RowDefinitions="8* *"
                    x:Name="VideoViewInnerControls"
                    IsVisible="False">
                <Border Grid.Row="1"
                        Background="rgba(0, 0, 0, 0.7)"/>
                <Grid Grid.Row="1"
                      RowDefinitions="* 2*">
                  <Rectangle Fill="#7F1D1D"
                              x:Name="ProgressBarBackground"
                              Grid.Row="0"
                              PointerPressed="ProgressBar_PointerPressed"/>
                  <Rectangle x:Name="ProgressBar"
                              Fill="#B91C1C"
                              HorizontalAlignment="Left"
                             MaxWidth="640"
                              Grid.Row="0"
                              PointerPressed="ProgressBar_PointerPressed"/>
                  <Grid Grid.Row="1"
                        ColumnDefinitions="* * * 15*">
                    <Button Grid.Column="0"
                            Width="32"
                            Height="32"
                            Classes="Toolbar"
                            Click="PlayControl_Click">
                      <Image x:Name="PlayControl"
                              Width="32"
                              Height="32"/>
                    </Button>
                    <Button Grid.Column="1"
                            Width="32"
                            Height="32"
                            Classes="Toolbar"
                            Click="VolumeControl_Click">
                      <Image Source="/Assets/volume.png"
                              x:Name="VolumeControl"
                              Width="16"
                              Height="16"/>
                    </Button>
                  </Grid>
                </Grid>
              </Grid>
            </lvs:VideoView>
          </Border>
          <Image x:Name="ScreenshotImage"
                  IsVisible="True"
                 Stretch="UniformToFill"
                 Width="640"
                 Height="360"/>
          <Grid Grid.Row="1"
                Height="84"
                Width="640"
                Margin="0 15"
                HorizontalAlignment="Stretch">
            <Border Background="rgba(0, 0, 0, 0.3)"
                    CornerRadius="0 0 6 6"/>
            <ScrollViewer HorizontalScrollBarVisibility="Visible"
                          VerticalScrollBarVisibility="Disabled"
                          HorizontalAlignment="Stretch">
              <UniformGrid x:Name="ListOfMedia"
                          Rows="1"
                          HorizontalAlignment="Stretch">
              </UniformGrid>
            </ScrollViewer>
          </Grid>
        </Grid>
        <Grid Margin="20 0"
              Grid.Column="1"
              RowDefinitions="2.84* *">
          <StackPanel Grid.Row="0"
                      Orientation="Vertical"
                      Spacing="10">
            <TextBlock x:Name="TitleBlock"
                     FontSize="24"
                     LineHeight="32"
                     FontWeight="SemiBold"
                     TextWrapping="Wrap"/>
            <TextBlock x:Name="DescriptionBlock"
                       FontSize="16"
                       LineHeight="28"
                       FontWeight="Regular"
                       TextWrapping="Wrap"/>
          </StackPanel>
          <Grid Grid.Row="1"
                RowDefinitions="* 2*">
            <Grid Grid.Row="0">
              <StackPanel Orientation="Horizontal"
                          HorizontalAlignment="Right"
                          Spacing="10">
                <TextBlock x:Name="PriceBlock"
                           HorizontalAlignment="Right"
                           FontSize="28"
                           FontWeight="SemiBold"/>
                <TextBlock x:Name="OriginalPriceBlock"
                           HorizontalAlignment="Right"
                           FontSize="28"
                           FontWeight="SemiBold"
                           TextDecorations="Strikethrough"
                           Foreground="#9CA3AF"
                           IsVisible="False"/>
              </StackPanel>

            </Grid>
            
            <Button Grid.Row="1"
                    HorizontalAlignment="Stretch"
                    Height="50"
                    Click="Interact_Click"
                    Name="ActionButton">
              <TextBlock FontSize="18"
                         FontWeight="SemiBold"
                         HorizontalAlignment="Center"
                         VerticalAlignment="Center"
                         Name="ActionLabel">
                Купить
              </TextBlock>
            </Button>
          </Grid>
        </Grid>
      </Grid>
      
      <StackPanel Spacing="6">
        <TextBlock x:Name="TagsBlock"
                   FontSize="14">
          <Run Text="Теги:" FontWeight="Bold"/>
        </TextBlock>
        <StackPanel Orientation="Horizontal"
                    Spacing="6">
          <StackPanel Orientation="Vertical"
                      Name="SystemRequirementsItems"
                      Spacing="6">
            <TextBlock FontWeight="Bold"
                       Padding=" 0 0 0 4"
                       FontSize="14">
              Системные требования:
            </TextBlock>
          </StackPanel>
          <StackPanel Orientation="Vertical"
                      Name="MinimalSystemRequirements"
                      Width="300"
                      Spacing="6">
            <TextBlock FontWeight="SemiBold"
                       Padding=" 0 0 0 4"
                       FontSize="14">
              Минимальные
            </TextBlock>
          </StackPanel>
          <StackPanel Orientation="Vertical"
                      Name="RecommendedSystemRequirements"
                      Width="300"
                      Spacing="6">
            <TextBlock FontWeight="SemiBold"
                       Padding=" 0 0 0 4"
                       FontSize="14">
              Рекомендуемые
            </TextBlock>
          </StackPanel>
        </StackPanel>
        
      </StackPanel>
    </StackPanel>
  </ScrollViewer>
 </UserControl>

解决方案

1. 切换VideoView的渲染模式

LibVLCSharp的VideoView默认使用硬件渲染,这类渲染元素在ScrollViewer中可能无法正确响应滚动偏移。可以尝试强制启用软件渲染:

// 在初始化MediaPlayer后添加
Video.MediaPlayer.SetRendererOptions(new RendererOptions { HardwareAcceleration = HardwareAcceleration.Disabled });

如果使用的LibVLCSharp.Avalonia版本支持,也可以直接在XAML中设置:

<lvs:VideoView RenderOptions="Software" ... />

2. 将VideoView移出ScrollViewer,通过绑定同步位置

把VideoView放在ScrollViewer的同级容器,通过绑定ScrollViewer的VerticalOffset来手动同步位置,模拟跟随滚动的效果:

步骤1:修改XAML结构

<Grid>
  <!-- 原ScrollViewer,添加名称便于绑定 -->
  <ScrollViewer x:Name="MainScrollViewer">
    <StackPanel Margin="40" Orientation="Vertical">
      <Grid HorizontalAlignment="Left" ColumnDefinitions="1.2* *">
        <Grid RowDefinitions="3* *" Grid.Column="0">
          <!-- 保留占位Border,维持原有布局结构 -->
          <Border CornerRadius="6" Width="640" Height="360"/>
          <!-- 原有的Image、下方媒体列表等元素 -->
          <Image x:Name="ScreenshotImage" IsVisible="True" Stretch="UniformToFill" Width="640" Height="360"/>
          <Grid Grid.Row="1" Height="84" Width="640" Margin="0 15" HorizontalAlignment="Stretch">
            <Border Background="rgba(0, 0, 0, 0.3)" CornerRadius="0 0 6 6"/>
            <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Stretch">
              <UniformGrid x:Name="ListOfMedia" Rows="1" HorizontalAlignment="Stretch"/>
            </ScrollViewer>
          </Grid>
        </Grid>
        <!-- 右侧的标题、价格等内容 -->
        <Grid Margin="20 0" Grid.Column="1" RowDefinitions="2.84* *">
          <!-- 原右侧内容不变 -->
        </Grid>
      </Grid>
      <!-- 下方的标签、系统要求等内容 -->
    </StackPanel>
  </ScrollViewer>
  <!-- VideoView放在Grid顶层,绝对定位 -->
  <lvs:VideoView x:Name="Video" Width="640" Height="360" IsVisible="False"
                 Margin="{Binding ElementName=MainScrollViewer, Path=VerticalOffset, Converter={StaticResource ScrollOffsetConverter}}">
    <!-- 原有的内部控件不变 -->
  </lvs:VideoView>
</Grid>

步骤2:实现滚动偏移转换器

public class ScrollOffsetConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double verticalOffset)
        {
            // 计算VideoView的初始Top位置(对应原StackPanel的Margin 40),减去滚动偏移量
            return new Thickness(40, 40 - verticalOffset, 0, 0);
        }
        return new Thickness();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

步骤3:注册转换器

在UserControl的资源中添加转换器:

<UserControl.Resources>
  <local:ScrollOffsetConverter x:Key="ScrollOffsetConverter"/>
</UserControl.Resources>

3. 监听滚动事件,强制更新VideoView位置

在ScrollViewer的ScrollChanged事件中,手动调整VideoView的渲染变换,抵消滚动偏移:

步骤1:绑定滚动事件

<ScrollViewer x:Name="MainScrollViewer" ScrollChanged="MainScrollViewer_ScrollChanged">

步骤2:实现事件处理逻辑

private void MainScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    if (Video == null) return;
    
    // 使用TranslateTransform抵消滚动的垂直偏移
    var transform = Video.RenderTransform as TranslateTransform ?? new TranslateTransform();
    transform.Y -= e.VerticalChange;
    Video.RenderTransform = transform;
    
    // 强制触发控件重绘
    Video.InvalidateVisual();
}

内容的提问来源于stack exchange,提问作者Александр Карпов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:17:02