Das Halten von Ereignissen wird für unberührte Elemente in ListView ausgelöst
-
21-12-2019 - |
Frage
Ich habe ein UserControl
mit einem Grid
das abonniert ist Holding
Ereignis.Das Problem ist, dass die Holding
Das Ereignis wird für den Gegenstand ausgelöst, den ich anvisiert habe, sowie für einige andere Gegenstände im ListView
.Ich verwende das Steuerelement übrigens als DataTemplate.
<ListView ItemsSource="{Binding ...}" Margin="0, 0, 0, 0">
...
<ListView.ItemTemplate>
<DataTemplate>
<local:MyUserControl/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code-Behind für die Benutzersteuerung:
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;
}
}
Inhalte der Benutzerkontrolle:
<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>
Ich konnte Storyboards nicht für Elemente verwenden, die in einem DataTemplate enthalten waren, sodass ich gezwungen war, den Inhalt in ein UserControl zu verschieben.
Hat dieses Problem mit der Virtualisierung zu tun?Wie kann ich das beheben?
Alternativen reichen aus.
AKTUALISIEREN:
Einige SO-Beiträge deuteten darauf hin, dass der Recyclingmodus dazu führte, dass Gegenstände wiederverwendet wurden.Ich habe hinzugefügtVirtualizingStackPanel.VirtualizationMode="Standard"
zu meiner ListView, aber das Problem bleibt überraschenderweise weiterhin bestehen.
Jetzt muss ich also einen Weg finden, um zu verhindern, dass andere Elemente denselben Deckkraftwert wiederholen (der nicht datengebunden ist, da er über ein Storyboard festgelegt wird).
UPDATE 2:
Jetzt verschwindet die Beschreibung, die beim Halten des angezeigten Elements eingeblendet wird, vollständig, wenn es nicht mehr sichtbar ist:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Lösung
Mir gefällt, wie Sie es hier versuchen.Für Touchscreen-Geräte haben wir keine MouseOver
Ereignis, daher ist dies eine der Möglichkeiten, wenn Sie zusätzliche Informationen anzeigen möchten, ohne die Benutzeroberfläche durcheinander zu bringen.
Eine viel sauberere Lösung wäre jedoch die Schaffung eines wiederverwendbaren Produkts Behavior
das die gesamte UI-Logik und Animationen kapselt.
Dazu müssen Sie zunächst die Referenz angeben Behaviors SDK (XAML)
.
Der Behavior
Die Umsetzung ist unkompliziert.Der AssociatedObject
wäre das Grid
das die Berührungsereignisse empfängt, und dann müssen Sie ein erstellen DependencyProperty
TextBlockName
um die Beschreibung abzurufen TextBlock
Beispiel.Sobald Sie die Instanz haben, müssen Sie nur noch die Animationen dafür in C# schreiben.
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;
}
}
Das Behavior
kann dann an Ihr angeschlossen werden DataTemplate
's höchstes Niveau Grid
.Beachten Sie, dass Sie keine benötigen UserControl
um es noch mehr einzupacken.
Stellen Sie außerdem sicher, dass Sie eine Hintergrundfarbe für haben Grid
sodass es Berührungsereignisse empfangen kann.Das Letzte, was ich getan habe, war, das zu ändern ListView
'S ItemContainerStyle
'S HorizontalContentAlignment
Zu Stretch
also die Grid
wird ganz nach rechts gestreckt.
<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>
Andere Tipps
Ich habe eine Problemumgehung gefunden;Ich habe eine Eigenschaft namens hinzugefügt DescriptionLayerOpacity
zu meinem Modell und setze es auf einen Standardwert von 0
wenn ich einen neuen Artikel zu meinem hinzufüge ObservableCollection
in meinem Ansichtsmodell.
Ich habe auch eine bidirektionale Bindung hinzugefügt, um die Quelleigenschaft zu ändern (DescriptionLayerOpacity
), damit die Ansicht mit den von den Storyboards vorgenommenen Änderungen aktualisiert wird:
<Grid ... Opacity="{Binding DescriptionLayerOpacity, Mode=TwoWay}">
<TextBlock .../>
</Grid>
Kurz gesagt: Alle Daten des UserControl mussten datengebunden sein, um eine Wiederholung in den anderen Elementen in der ListView zu vermeiden.
Dies ist wirklich keine elegante Lösung und wurde noch nicht vollständig getestet.Bis ich eine echte Lösung finde, wird das ausreichen.
Aktualisieren:Funktioniert nicht vollständig
Ich habe kürzlich festgestellt, dass einige Elemente nicht reagieren, wenn andere Elemente ausgewählt werden.Damit diese Elemente reagieren, muss ich vor dem Tippen+Halten-Ereignis tippen (übrigens sind beides Ereignisse, die sich gegenseitig ausschließen).
Update 2:
Nach dem Entfernen scheint alles gut zu funktionieren if
Anweisungen im CodeBehind.Aber die Bindung an eine Opazitätseigenschaft im Modell, damit es funktioniert, ist immer noch eine stinkende Lösung.
Schauen Sie sich hier die Lösung an: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/95f49050-368e-4ddb-962e-6be62e7e3d57/context-menus-in-listview-windows-phone-81?forum=wpdevelop
Ich bin mir nicht sicher, ob das genau Ihr Problem ist, aber es hört sich so an UserControl
empfängt das Hold-Ereignis nicht, da es von der absorbiert wird ListView
?Die obige Lösung beschreibt die Notwendigkeit, das zu verpacken DataTemplates
Grid
in einem ListViewItem
, und ein Hold-Ereignis daran anhängen.Vielleicht hilft das?