【问题标题】:Holding event fires on untouched items in ListView在 ListView 中未触及的项目上触发事件
【发布时间】:2014-07-30 02:28:08
【问题描述】:

我有一个UserControl 和一个订阅Holding 事件的Grid。问题是Holding 事件针对我定位的项目以及ListView 中的其他一些项目触发。顺便说一句,我将控件用作 DataTemplate。

<ListView ItemsSource="{Binding ...}" Margin="0, 0, 0, 0">
    ...
    <ListView.ItemTemplate>
        <DataTemplate>
            <local:MyUserControl/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

用户控制代码隐藏:

    private bool isDescriptionVisible = false;

    private void Grid_Holding(object sender, HoldingRoutedEventArgs e)
    {
        if (!isDescriptionVisible)
        {
            DescriptionFadeIn.Begin();
            isDescriptionVisible = true;
        }
    }

    private void Grid_Tapped(object sender, TappedRoutedEventArgs e)
    {
        if (isDescriptionVisible)
        {
            DescriptionFadeOut.Begin();
            isDescriptionVisible = false;
        }
    }

用户控制内容:

<Grid.Resources>
    <Storyboard x:Name="FadeIn">
            <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DescriptionLayer"
                             Duration="0:0:0.3" To=".8"/>
        </Storyboard>

        <Storyboard x:Name="FadeOut">
            <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DescriptionLayer"
                             Duration="0:0:0.3" To="0"/>
        </Storyboard>
</Grid.Resources>

<Grid Margin="0, 0, 0, 48" Holding="Grid_Holding" Tapped="Grid_Tapped">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="1*"/>
    </Grid.RowDefinitions>
    <Image Source="{Binding Img}" Stretch="UniformToFill" Height="240" Width="450"/>
    <Grid x:Name="DescriptionLayer" Background="Black" Opacity="0">
        <TextBlock Text="{Binding Description}" FontSize="16" TextWrapping="Wrap" Margin="0, 9, 0, 0" MaxHeight="170" TextTrimming="CharacterEllipsis"/>
    </Grid>
    <StackPanel Grid.Row="1" Margin="12">
        <TextBlock Text="{Binding Author}" FontSize="16"/>
        <TextBlock Text="{Binding Title}" FontSize="18"/>
    </StackPanel>
</Grid>

我无法在 DataTemplate 中包含的项目上使用情节提要,因此我不得不将其内容移动到 UserControl。

这个问题是否与虚拟化有关?我该如何解决这个问题?

替代品也可以。

更新

一些 SO 帖子建议回收模式导致物品被重复使用。我已经添加 VirtualizingStackPanel.VirtualizationMode="Standard" 到我的 ListView 但问题出人意料地仍然存在。

所以现在我需要想办法防止其他项目重复相同的不透明度值(它不是数据绑定的,因为它是通过情节提要设置的)。

更新 2

现在,在按住显示的项目时淡入的描述在它消失时完全消失:

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <VirtualizingStackPanel/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

【问题讨论】:

标签: c# xaml windows-phone windows-phone-8.1


【解决方案1】:

我找到了解决方法;我在我的模型中添加了一个名为 DescriptionLayerOpacity 的属性,并在我的视图模型中向我的 ObservableCollection 添加新项目时将其设置为默认值 0

我还添加了 2-way 绑定来更改源属性 (DescriptionLayerOpacity),因此视图会随着情节提要所做的更改而更新:

<Grid ... Opacity="{Binding DescriptionLayerOpacity, Mode=TwoWay}">
    <TextBlock .../>
</Grid>

简而言之,所有 UserControl 的数据都必须进行数据绑定,以避免在 ListView 的其他项目中重复。

这确实不是一个优雅的解决方案,而且还没有经过全面测试。在我找到真正的解决方案之前,这就足够了。

更新:未完全正常工作

我最近发现,在选择其他项目时,某些项目不会响应。为了让这些项目响应,我必须在点击+按住事件之前点击(顺便说一下,两者都是互斥事件)。

更新 2:

删除代码隐藏中的 if 语句后,一切似乎都运行良好。但是绑定到模型中的 opacity 属性以使其工作仍然是一个臭名昭著的解决方案。

【讨论】:

    【解决方案2】:

    我喜欢你在这里尝试的方式。对于触摸屏设备,我们没有 MouseOver 事件,因此这是您想要显示一些额外信息而又不会弄乱 UI 的方式之一。

    但是,一个更简洁的解决方案是创建一个可重用的Behavior,它封装了所有 UI 逻辑和动画。

    为此,您首先需要包含参考 Behaviors SDK (XAML)

    Behavior 的实现很简单。 AssociatedObject 将是接收触摸事件的Grid,然后您需要创建一个DependencyProperty TextBlockName 来检索描述TextBlock 实例。获得实例后,您只需用 C# 为其编写动画即可。

    public class ShowHideDescriptionBehavior : DependencyObject, IBehavior
    {
        public string TextBlockName
        {
            get { return (string)GetValue(TextBlockNameProperty); }
            set { SetValue(TextBlockNameProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for TextBlockName.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextBlockNameProperty =
            DependencyProperty.Register("TextBlockName", typeof(string), typeof(ShowHideDescriptionBehavior), new PropertyMetadata(string.Empty));
    
        public DependencyObject AssociatedObject { get; set; }
    
        public void Attach(DependencyObject associatedObject)
        {
            this.AssociatedObject = associatedObject;
            var panel = (Panel)this.AssociatedObject;
    
            panel.Holding += AssociatedObject_Holding;
            panel.Tapped += AssociatedObject_Tapped;
        }
    
        private void AssociatedObject_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
        {
            var animation = new DoubleAnimation
            {
                Duration = TimeSpan.FromMilliseconds(300),
                To = 0
            };
            Storyboard.SetTarget(animation, this.DescriptionTextBlock);
            Storyboard.SetTargetProperty(animation, "Opacity");
    
            var storyboard = new Storyboard();
            storyboard.Children.Add(animation);
            storyboard.Begin();
        }
    
        private void AssociatedObject_Holding(object sender, Windows.UI.Xaml.Input.HoldingRoutedEventArgs e)
        {
            var animation = new DoubleAnimation
            {
                Duration = TimeSpan.FromMilliseconds(300),
                To = 0.8
            };
            Storyboard.SetTarget(animation, this.DescriptionTextBlock);
            Storyboard.SetTargetProperty(animation, "Opacity");
    
            var storyboard = new Storyboard();
            storyboard.Children.Add(animation);
            storyboard.Begin();
        }
    
        private TextBlock _descriptionTextBlock;
        private TextBlock DescriptionTextBlock
        {
            get
            {
                if (_descriptionTextBlock == null)
                {
                    var panel = (Panel)this.AssociatedObject;
                    // todo: add validation
                    _descriptionTextBlock = (TextBlock)panel.FindName(this.TextBlockName);
                }
    
                return _descriptionTextBlock;
            }
        }
    
        public void Detach()
        {
            var panel = (Panel)this.AssociatedObject;
    
            panel.Holding -= AssociatedObject_Holding;
            panel.Tapped -= AssociatedObject_Tapped;
        }
    }
    

    然后可以将此Behavior 附加到您的DataTemplate 的顶级Grid。请注意,您不再需要 UserControl 来包装它。

    另外,请确保您有 Grid 的背景颜色,以便它可以接收触摸事件。我做的最后一件事是将ListViewItemContainerStyleHorizontalContentAlignment 更改为Stretch,这样Grid 就会一直延伸到右侧。

    <Page.Resources>
        <DataTemplate x:Key="GroupTemplate">
            <Grid Background="Transparent" Margin="12">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Interactivity:Interaction.Behaviors>
                    <local:ShowHideDescriptionBehavior TextBlockName="Description" />
                </Interactivity:Interaction.Behaviors>
                <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
                    <Image Source="{Binding Img}" Height="110" Width="110"/>
                </Border>
                <StackPanel Grid.Column="1" Margin="10,0,0,0">
                    <TextBlock Text="{Binding Author}" Style="{StaticResource TitleTextBlockStyle}"/>
                    <TextBlock x:Name="Description" Opacity="0" Text="{Binding Description}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </Page.Resources>
    
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{Binding Source={StaticResource SampleDataSource}}">
        <ListView ItemTemplate="{StaticResource GroupTemplate}" ItemsSource="{Binding Groups}">
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
    </Grid>
    

    【讨论】:

    • 谢谢!我现在将此作为我的方法的基础。 ListView 的虚拟化仍然迫使我绑定不透明度数据以防止其他 ListView 项中的重复数据,但我仍然更喜欢这种解决方案。
    【解决方案3】:

    在此处查看解决方案:http://social.msdn.microsoft.com/Forums/windowsapps/en-US/95f49050-368e-4ddb-962e-6be62e7e3d57/context-menus-in-listview-windows-phone-81?forum=wpdevelop

    我不确定这是否正是您的问题,但听起来UserControl 没有收到保持事件,因为它被ListView 吸收了? 上面的解决方案描述了需要将DataTemplates Grid 包装在ListViewItem 中,并为其附加一个Hold 事件。也许这会有所帮助?

    【讨论】:

    • 我已将我的 Grid 包装在 ListViewItem 中,并将 Hold 事件处理程序移到那里,但不幸的是,问题仍然存在。我遇到的这个问题真的很奇怪。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多