Xamarin.Forms ListView: Legen Sie die Hervorhebungsfarbe eines getippten Elements fest


74

Mit Xamarin.Forms , wie kann ich definieren das highlight / Hintergrundfarbe eines ausgewählten / angezapft Listview - Element?

(Meine Liste hat einen schwarzen Hintergrund und eine weiße Textfarbe, daher ist die Standard-Hervorhebungsfarbe unter iOS zu hell. Im Gegensatz dazu gibt es unter Android überhaupt keine Hervorhebung - bis zu einer subtilen horizontalen grauen Linie.)

Beispiel: (links: iOS, rechts: Android; während Sie "Barn2" drücken)

Antworten:


117

In Android bearbeiten Sie einfach Ihre Datei styles.xml unter Resources \ values ​​und fügen Folgendes hinzu:

<resources>
  <style name="MyTheme" parent="android:style/Theme.Material.Light.DarkActionBar">
   <item name="android:colorPressedHighlight">@color/ListViewSelected</item>
   <item name="android:colorLongPressedHighlight">@color/ListViewHighlighted</item>
   <item name="android:colorFocusedHighlight">@color/ListViewSelected</item>
   <item name="android:colorActivatedHighlight">@color/ListViewSelected</item>
   <item name="android:activatedBackgroundIndicator">@color/ListViewSelected</item>
  </style>
<color name="ListViewSelected">#96BCE3</color>
<color name="ListViewHighlighted">#E39696</color>
</resources>

5
Das Problem tritt nur unter Android auf, daher ist dies die beste / sauberste Lösung für mich
Ateik

1
Die einfachste Lösung auch für mich. Es ist so schade, dass Xamarin Forms keine anpassbare Eigenschaft dafür haben.
Marin Shalamanov

Funktioniert diese Lösung, wenn das Hauptprojekt auf pcl läuft?
Pxaml

1
Ich habe die Style.xml erstellt, da in meinem Android-Projekt keine vorhanden war, aber das funktioniert bei mir nicht. Muss ich noch etwas hinzufügen? Vielleicht ein besonderes Thema? Ich benutze Xamarin.Forms.
Denis Vitez

68

Es sieht so aus, als gäbe es tatsächlich eine plattformübergreifende Möglichkeit, die sowohl unter iOS als auch unter Android funktioniert (bei Windows nicht sicher). Es verwendet nur Bindung und erfordert keine benutzerdefinierten Renderer (was selten erscheint). Dies ist eine Mischung aus viel Googeln, also danke an alle, die ich mir vielleicht geliehen habe ...

Ich gehe von ViewCells aus, aber dies sollte auch für Text- oder Bildzellen funktionieren. Ich füge hier nur den relevanten Code über den typischen Text, das Bild usw. hinaus ein.

Gehen Sie auf Ihrer Seite folgendermaßen vor:

MyModel model1 = new MyModel();
MyModel model2 = new MyModel();

ListView list = new ListView
{
    ItemsSource = new List<MyModel> { model1, model2 };
    ItemTemplate = new DataTemplate( typeof(MyCell) )
};

Ihr benutzerdefiniertes Modell könnte ungefähr so ​​aussehen:

public class MyModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private Color _backgroundColor;

    public Color BackgroundColor 
    { 
        get { return _backgroundColor; } 
        set 
        { 
            _backgroundColor = value; 

            if ( PropertyChanged != null )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( "BackgroundColor" ) );
            }
        }
    }

    public void SetColors( bool isSelected )
    {
        if ( isSelected )
        {
            BackgroundColor = Color.FromRgb( 0.20, 0.20, 1.0 );
        }
        else
        {
            BackgroundColor = Color.FromRgb( 0.95, 0.95, 0.95 ); 
        }
    }
}

Dann benötigen Sie für Ihre ItemTemplate eine benutzerdefinierte Zellenklasse wie die folgende:

public class MyCell : ViewCell
{
    public MyCell() : base()
    {
        RelativeLayout layout = new RelativeLayout();
        layout.SetBinding( Layout.BackgroundColorProperty, new Binding( "BackgroundColor" ) );

        View = layout;
    }
}

Führen Sie dann in Ihrem ItemSelected-Ereignishandler die folgenden Schritte aus. Beachten Sie, dass 'selected' eine Instanz von MyModel ist, mit der das aktuell ausgewählte Element verfolgt wird. Ich zeige hier nur Hintergrundfarben, aber ich verwende diese Technik auch, um die Text- und Detailtextfarben umgekehrt hervorzuheben.

private void ItemSelected( object sender, ItemTappedEventArgs args )
{
    // Deselect previous
    if ( selected != null )
    {
        selected.SetColors( false );
    }

    // Select new
    selected = (list.SelectedItem as MyModel);
    selected.SetColors( true );
}

1
Das funktioniert - zumindest ein bisschen. Ich stelle fest, dass dies bei Auswahl eines Elements funktioniert, aber wenn ich dann ein anderes auswähle, kehrt das erste zum "alten" Hintergrund zurück - es sei denn, ich scrolle, um es auszublenden, und scrolle dann erneut, um es anzuzeigen. In diesem Fall ist das Beim Neuzeichnen wird die 'neue' Farbe verwendet.
Nicht geschnitten

2
omg, danke! Ich habe selbst daran gedacht, aber als ich es versuchte, hat es nicht funktioniert. Ich bin nicht sicher, was ich falsch gemacht habe, aber das Kopieren Ihres Codes hat funktioniert.
SpaceMonkey

5
es funktioniert NICHT, du
zeichnest

5
@ Greag.Deay - ListView.SeparatorVisibility = SeparatorVisibility.None; ListView.SeparatorColor= Color.Transparent;sollte das von Ihnen erwähnte Problem lösen.
Rohit Vipin Mathews

Diese Methode hat in meinem UWP-Projekt zusammen mit Android und iOS einwandfrei funktioniert. Super!
Scuzzlebutt

33

iOS

Lösung:

Innerhalb eines Benutzerdefinierten können ViewCellRendererSie die festlegen SelectedBackgroundView. Erstellen Sie einfach eine neue UIViewmit einer Hintergrundfarbe Ihrer Wahl und schon sind Sie fertig.

public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
    var cell =  base.GetCell(item, reusableCell, tv);

    cell.SelectedBackgroundView = new UIView {
        BackgroundColor = UIColor.DarkGray,
    };

    return cell;
}

Ergebnis:

Hinweis:

Bei Xamarin.Forms scheint es wichtig zu sein, eine neue zu erstellen, UIViewanstatt nur die Hintergrundfarbe der aktuellen Farbe festzulegen.


Android

Lösung:

Die Lösung, die ich auf Android gefunden habe, ist etwas komplizierter:

  1. Erstellen Sie eine neue Zeichnung ViewCellBackground.xmlim Ordner Resources> drawable:

    <?xml version="1.0" encoding="UTF-8" ?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true" >
            <shape android:shape="rectangle">
                <solid android:color="#333333" />
            </shape>
        </item>
        <item>
            <shape android:shape="rectangle">
                <solid android:color="#000000" />
            </shape>
        </item>
    </selector>
    

    Es definiert feste Formen mit unterschiedlichen Farben für den Standardstatus und den "gedrückten" Status eines UI-Elements.

  2. Verwenden Sie eine geerbte Klasse für ViewIhre ViewCell, z.

    public class TouchableStackLayout: StackLayout
    {
    }
    
  3. Implementieren Sie einen benutzerdefinierten Renderer für diese Klasse, indem Sie die Hintergrundressource festlegen:

    public class ElementRenderer: VisualElementRenderer<Xamarin.Forms.View>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
        {
            SetBackgroundResource(Resource.Drawable.ViewCellBackground);
    
            base.OnElementChanged(e);
        }
    }
    

Ergebnis:


Wenn das Festlegen der Hintergrundfarbe für das vorhandene UIView nicht funktioniert, rufen Sie das SetNeedsLayout der Zelle (oder wie auch immer diese Methode aufgerufen wurde) auf, nachdem Sie die Hintergrundfarbe festgelegt haben. UITableView versucht, kein Layout durchzuführen, wann immer dies möglich ist
Sten Petrov

@StenPetrov: Nein, SetNeedsDisplayoder `` `SetNeedsLayout``` funktioniert nicht. Aber es spielt keine Rolle, da das Zuweisen new UIView {...}eine ziemlich kurze Problemumgehung ist.
Falko

Und wie ändere ich die Schriftfarbe?
Edgar

Unter Android wird die ausgewählte Farbe für mich nicht beibehalten. Ich habe versucht , mit Android: state_selected ..: [
masterwok

19

Um die Farbe der ausgewählten zu ändern ViewCell, gibt es einen einfachen Vorgang ohne Verwendung eines benutzerdefinierten Renderers. Machen Sie ein TappedEreignis von Ihrem ViewCellwie unten

<ListView.ItemTemplate>
    <DataTemplate>
        <ViewCell Tapped="ViewCell_Tapped">            
        <Label Text="{Binding StudentName}" TextColor="Black" />
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>

Implementieren Sie das Ereignis in Ihrer ContentPage- oder CS-Datei

private void ViewCell_Tapped(object sender, System.EventArgs e)
{
    if(lastCell!=null)
    lastCell.View.BackgroundColor = Color.Transparent;
    var viewCell = (ViewCell)sender;
    if (viewCell.View != null)
    {
        viewCell.View.BackgroundColor = Color.Red;
        lastCell = viewCell;
    }
} 

Deklarieren Sie lastCellan der Spitze Ihres ContentPagewie dieseViewCell lastCell;


1
Beste Lösung! Vielen Dank!
a.tarasevich

2
Diese Lösung sollte maximale Stimmen haben und muss oben sein, da dies die einfachste ist.
user3559462

2
Die beste Lösung, am einfachsten und am schönsten. Sollte oben sein
Tornike Gomareli

1
Dies funktioniert nur, wenn Sie einen Eintrag aus dem Menü auswählen. Es funktioniert nicht, wenn das Menü zum ersten Mal angezeigt wird.
Luis Lema

14

Nur für Android

Fügen Sie Ihr benutzerdefiniertes Thema hinzu

<item name="android:colorActivatedHighlight">@android:color/transparent</item>

13

Ich habe einen ähnlichen Prozess, vollständig plattformübergreifend, aber ich verfolge den Auswahlstatus selbst und habe dies in XAML getan.

 <ListView x:Name="ListView" ItemsSource="{Binding ListSource}" RowHeight="50">
        <ListView.ItemTemplate>
          <DataTemplate>
            <ViewCell>
              <ViewCell.View>
                <ContentView Padding="10" BackgroundColor="{Binding BackgroundColor}">
                  <Label Text="{Binding Name}" HorizontalOptions="Center" TextColor="White" />
                </ContentView>
              </ViewCell.View>
            </ViewCell>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>

Dann im ItemTapped-Ereignis

 ListView.ItemTapped += async (s, e) =>
            {
                var list = ListSource;

                var listItem = list.First(c => c.Id == ((ListItem)e.Item).Id);

                listItem.Selected = !listItem.Selected;

                SelectListSource = list;

                ListView.SelectedItem = null;

            };

Wie Sie sehen können, habe ich ListView.SelectedItem auf null gesetzt, um alle plattformspezifischen Auswahlstile zu entfernen, die ins Spiel kommen.

In meinem Modell habe ich

        private Boolean _selected;

        public Boolean Selected
        {
            get
            {
                return _selected;
            }
            set
            {
                _selected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("BackgroundColor"));
            }
        }                 

        public Color BackgroundColor
        {
            get
            {
                if (Selected)
                    return Color.Black;
                else
                    return Color.Blue
            }
        }

Gilt dies auch für kontextbezogene Aktionsmenüs? Wie lange auf Android klicken und unter iOS nach links wischen?
mr5

Ich kann list nicht aufrufen. First (...) .. Es heißt, IEnumerable enthält keine Definition für First ...
Prince

@Prince - Sie müssen mit System.Linq hinzufügen; oben.
Adam

Wenn Sie System.Linq verwenden, hat ein IEnumerable mit Sicherheit die Erweiterung .First (). Wenn Sie dieses Problem nicht beheben können, stellen Sie in StackOverflow eine weitere Frage, um das jeweilige Problem zu beheben.
Adam

Da das ausgewählte Element geändert wird, wird das ItemSelectedEreignis erneut ausgelöst. In meinem Fall ist der weitere Code in eine Ausnahme geraten.
Morse

11

Ich hatte das gleiche Problem und habe es auch gelöst, indem ich einen benutzerdefinierten Renderer für iOS erstellt habe, wie Falko vorschlägt. Ich habe jedoch die Stiländerung für Android vermieden und einen Weg gefunden, einen benutzerdefinierten Renderer auch für Android zu verwenden.

Es ist irgendwie irre, wie das ausgewählte Flag für die Android-Ansichtszelle immer falsch ist, deshalb musste ich eine neue private Eigenschaft erstellen, um es zu verfolgen. Abgesehen davon denke ich, dass dies einem angemesseneren Muster folgt, wenn Sie benutzerdefinierte Renderer für beide Plattformen verwenden möchten. In meinem Fall habe ich dies für TextCell getan, aber ich glaube, dass dies für andere CellViews genauso gilt.

Xamarin-Formen

using Xamarin.Forms;

public class CustomTextCell : TextCell
    {
        /// <summary>
        /// The SelectedBackgroundColor property.
        /// </summary>
        public static readonly BindableProperty SelectedBackgroundColorProperty =
            BindableProperty.Create("SelectedBackgroundColor", typeof(Color), typeof(CustomTextCell), Color.Default);

        /// <summary>
        /// Gets or sets the SelectedBackgroundColor.
        /// </summary>
        public Color SelectedBackgroundColor
        {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set { SetValue(SelectedBackgroundColorProperty, value); }
        }
    }

iOS

public class CustomTextCellRenderer : TextCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var cell = base.GetCell(item, reusableCell, tv);
            var view = item as CustomTextCell;
            cell.SelectedBackgroundView = new UIView
            {
                BackgroundColor = view.SelectedBackgroundColor.ToUIColor(),
            };

            return cell;
        }
    }

Android

public class CustomTextCellRenderer : TextCellRenderer
{
    private Android.Views.View cellCore;
    private Drawable unselectedBackground;
    private bool selected;

    protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
    {
        cellCore = base.GetCellCore(item, convertView, parent, context);

        // Save original background to rollback to it when not selected,
        // We assume that no cells will be selected on creation.
        selected = false;
        unselectedBackground = cellCore.Background;

        return cellCore;
    }

    protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        base.OnCellPropertyChanged(sender, args);

        if (args.PropertyName == "IsSelected")
        {
            // I had to create a property to track the selection because cellCore.Selected is always false.
            // Toggle selection
            selected = !selected;

            if (selected)
            {
                var customTextCell = sender as CustomTextCell;
                cellCore.SetBackgroundColor(customTextCell.SelectedBackgroundColor.ToAndroid());
            }
            else
            {
                cellCore.SetBackground(unselectedBackground);
            }
        }
    }
}

... dann müssen Sie auf der .xaml-Seite einen XMLNS-Verweis zurück zur neuen CustomViewCell hinzufügen ...

xmlns:customuicontrols="clr-namespace:MyMobileApp.CustomUIControls"

Vergessen Sie nicht, die neue benutzerdefinierte Steuerung in Ihrer XAML tatsächlich zu verwenden.


Vielen Dank, das hat mir Stunden gespart! Ich kann bestätigen, dass es für ViewCells funktioniert (was ich brauchte)
wislon

2
Hat jemand dies zum Laufen gebracht, während er einen Standardwert für das SelectedItem von ListView festgelegt hat?
Filipe Silva

Dies funktioniert hervorragend für ListViews, die die RetainElement-Caching-Strategie verwenden. Für diejenigen, die die RecycleElement- oder RecycleElementAndDataTemplate-Strategie verwenden, ändert sich die IsSelected-Eigenschaft jedoch nie. Es werden jedoch Änderungen an den Eigenschaften 'BindingContext' und 'Index' angezeigt. Ich versuche immer noch herauszufinden, ob ich diese stattdessen irgendwie verwenden kann. So nah! :)
Wislon

1
@Extragorey, Sie vermissen wahrscheinlich den ExportRenderer-Aufruf: [Assembly: ExportRenderer (typeof (ExtendedViewCell), typeof (ExtendedViewCellRenderer))]
thomasgalliker

1
Ich habe diese Technik verwendet, aber ich muss sagen, dass es, wie @FilipeSilva sagte, nicht funktioniert, wenn das ausgewählte Element im Ansichtsmodell initialisiert wird. Arbeiten Sie nur mit Benutzerinteraktion. Ich habe versucht, herumzuarbeiten, aber keine Lösung gefunden.
Nk54

7

Hier ist die rein plattformübergreifende und ordentliche Art:

1) Definieren Sie eine Triggeraktion

namespace CustomTriggers {
   public class DeselectListViewItemAction:TriggerAction<ListView> {
       protected override void Invoke(ListView sender) {
                sender.SelectedItem = null;
       }
   }
}

2) Wenden Sie die obige Klasseninstanz wie folgt als EventTrigger-Aktion in XAML an

 <ListView x:Name="YourListView" ItemsSource="{Binding ViewModelItems}">
    <ListView.Triggers>
        <EventTrigger Event="ItemSelected">
            <customTriggers:DeselectListViewItemAction></customTriggers:DeselectListViewItemAction>
        </EventTrigger>
    </ListView.Triggers>
</ListView>

Vergiss nicht hinzuzufügen xmlns:customTriggers="clr-namespace:CustomTriggers;assembly=ProjectAssembly"

Hinweis: Da sich keines Ihrer Elemente im ausgewählten Modus befindet, wird das Auswahlstil auf keiner der Plattformen angewendet.


Wie hilft dies, "die Hervorhebungs- / Hintergrundfarbe eines getippten Elements festzulegen"? Sieht so aus, als hätten Sie Farbänderungen unterdrückt .
ToolmakerSteve

1
Die Hintergrundfarbe wird dadurch in keiner Weise verändert. Aber hilfreich, wenn der Benutzer die Artikelauswahl unterdrücken möchte. Dies ist normalerweise in Szenarien hilfreich, in denen der Benutzer die Detailansicht starten möchte, wenn auf ein Element im ListViewgetippt wird, das ausgewählte Element jedoch unterdrückt werden soll.
Zafar

2

Ich habe und verwende eine ähnliche Lösung wie @ adam-pedley. Keine benutzerdefinierten Renderer, in xaml binde ich die ViewCell-Eigenschaft im Hintergrund

                <ListView x:Name="placesListView" Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding PlacesCollection}" SelectedItem="{Binding PlaceItemSelected}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid BackgroundColor="{Binding IsSelected,Converter={StaticResource boolToColor}}">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="auto"/>
                                    <RowDefinition Height="auto"/>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>

                                <Label Grid.Row="1" Grid.ColumnSpan="2" Text="{Binding DisplayName}" Style="{StaticResource blubeLabelBlackItalic}" FontSize="Default" HorizontalOptions="Start" />
                                <Label Grid.Row="2" Grid.ColumnSpan="2" Text="{Binding DisplayDetail}"  Style="{StaticResource blubeLabelGrayItalic}" FontSize="Small" HorizontalOptions="Start"/>
                                <!--
                                <Label Grid.RowSpan="2" Grid.ColumnSpan="2" Text="{Binding KmDistance}"  Style="{StaticResource blubeLabelGrayItalic}" FontSize="Default" HorizontalOptions="End" VerticalOptions="Center"/>
                                -->
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>                    
            </ListView>

Im Code (MVVM) speichere ich das zuletzt von einem boolToColor Converter ausgewählte Element und aktualisiere die Hintergrundfarbe

    public class BoolToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? Color.Yellow : Color.White;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (Color)value == Color.Yellow ? true : false;
        }
    }

    PlaceItem LastItemSelected;

    PlaceItem placeItemSelected;
    public PlaceItem PlaceItemSelected
    {
        get
        {
            return placeItemSelected;
        }

        set
        {
            if (LastItemSelected != null)
                LastItemSelected.IsSelected = false;

            placeItemSelected = value;
            if (placeItemSelected != null)
            {
                placeItemSelected.IsSelected = true;
                LastItemSelected = placeItemSelected;
            }
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PlaceItemSelected)));
        }
    }

Mein Beispiel wird durch eine Listenansicht von Orten extrahiert, die sich in einer Xamarin Forms Maps befinden (dieselbe Inhaltsseite). Ich hoffe, diese Lösung ist für jemanden nützlich


1

Um die Farbe des hervorgehobenen Elements festzulegen, müssen Sie die Farbe cell.SelectionStylein iOS festlegen .

In diesem Beispiel wird die Farbe des abgegriffenen Elements auf transparent gesetzt.

Wenn Sie möchten, können Sie es mit anderen Farben von ändern UITableViewCellSelectionStyle. Dies muss im Plattformprojekt von iOS geschrieben werden, indem in Ihrem Forms-Projekt ein neuer benutzerdefinierter ListView-Renderer erstellt wird.

public class CustomListViewRenderer : ListViewRenderer
    {
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (Control == null)
            {
                return;
            }

            if (e.PropertyName == "ItemsSource")
            {
                foreach (var cell in Control.VisibleCells)
                {
                    cell.SelectionStyle = UITableViewCellSelectionStyle.None;
                }
            }
        }
    }

Für Android können Sie diesen Stil in Ihre values ​​/ styles.xml einfügen

<style name="ListViewStyle.Light" parent="android:style/Widget.ListView">
    <item name="android:listSelector">@android:color/transparent</item>
    <item name="android:cacheColorHint">@android:color/transparent</item>
  </style>

1
Wie beantwortet das die Frage? Sie stellen überhaupt keine Farbe ein. Obwohl die ursprüngliche Hervorhebungsfarbe unter iOS zu hell ist, wollte ich die Hervorhebung nicht vollständig ausblenden.
Falko

@Falko - Es setzt das Farbbeispiel. In dem gezeigten Beispiel habe ich auf transparent gesetzt, aber Sie können jede Farbe Ihrer Wahl einstellen.
Sawan Kumar Bundelkhandi

Ich musste diesen benutzerdefinierten iOS-Renderer zusätzlich zu Barry Sohls Lösung verwenden, um zu verhindern, dass meine Vorlagen-Listenansicht die Hintergrundfarbe aller Steuerelemente in der Vorlage in die gebundene Hintergrundfarbe ändert. Allerdings habe ich ändern müssen , e.PropertyName == "ItemsSource"um e.PropertyName == "SelectedItem"es zu bekommen richtig zu arbeiten.
Scuzzlebutt

Wie Falko erwähnte, gibt dies unter iOS nicht genügend Kontrolle über die Farbe . UITableViewCellSelectionStyleerwähnt nur zwei eingebaute Farben, blau und grau.
ToolmakerSteve

1

Diese Lösung funktioniert einwandfrei. Wenn Sie jedoch die Caching-Strategie von ListView vom Standardwert abweichen, funktioniert sie nicht mehr. Es funktioniert, wenn Sie Ihre ListView wie folgt neu erstellen: listView = new ListView() { ... }; Wenn Sie dies tun, funktioniert es nicht (der Hintergrund bleibt für das ausgewählte Element grau): listView = new ListView(cachingStrategy:ListViewCachingStrategy.RecycleElement) { ... };

Im Folgenden finden Sie eine Lösung, die auch mit einer nicht standardmäßigen cachingStrategy funktioniert. Ich ziehe dies anderen Lösungen vor, z. B. Code in der OnItemSelected-Methode in Verbindung mit einer Bindung aus dem ViewModel für die Hintergrundfarbe.

Dank an @Lang_tu_bi_dien, der die Idee hier gepostet hat: Listenansicht Ausgewählte Artikel Hintergrundfarbe

Der endgültige Code sieht dann so aus:

Xamarin.Forms-Code:

namespace MyProject
{
    public class ListView2 : ListView
    {
        public ListView2(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
        {
        }
    }
}

XAML auf Ihrer Seite:

    <ListView2 x:Name="myListView" ListViewCachingStrategy="RecycleElement" ItemsSource="{Binding ListSource}" RowHeight="50">
        <ListView.ItemTemplate>
          <DataTemplate>
            <ViewCell>
              <ViewCell.View>
                  <Label Text="{Binding Name}" HorizontalOptions="Center" TextColor="White" />
                </ContentView>
              </ViewCell.View>
            </ViewCell>
          </DataTemplate>
        </ListView.ItemTemplate>
    </ListView2>

iOS-spezifischer Renderer:

[assembly: ExportRenderer(typeof(ListView2), typeof(ListView2Renderer))]
namespace MyProject.iOS
{
    public partial class ListView2Renderer : ListViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged(e);
            if (Control != null && e != null)
            {
                //oldDelegate = (UITableViewSource)Control.Delegate;
                Control.Delegate = new ListView2Delegate(e.NewElement);
            }
        }
    }


    class ListView2Delegate : UITableViewDelegate
    {
        private ListView _listView;

        internal ListView2Delegate(ListView listView)
        {
            _listView = listView;
        }

        public override void WillDisplay(UITableView tableView, UITableViewCell cell, Foundation.NSIndexPath indexPath)
        {
            cell.SelectedBackgroundView = new UIView()
            {
                BackgroundColor = Color.Red.ToUIColor()
            };
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _listView = null;
            }
            base.Dispose(disposing);
        }
    }
}

Hinweis: Aufgrund der Tatsache, dass Sie den Standarddelegierten ersetzen, können einige Probleme auftreten. Weitere Informationen hierzu finden Sie unter Festlegen des Steuerdelegaten im benutzerdefinierten Renderer führt zu Funktionsverlust . In meinem Projekt funktioniert alles so, wie es sollte, wenn ich das mache:

  • Verwenden Sie die normale ListView zusammen mit dem ListItemViewCellRenderer-Code, der in den früheren Beiträgen zu diesem Thread für ListViews angegeben wurde, die die Standard-Caching-Strategie ListViewCachingStrategy.RetainElement verwenden.

  • Verwenden Sie diese ListView2 zusammen für ListViews, die eine nicht standardmäßige Caching-Strategie verwenden, z. B. ListViewCachingStrategy.RecycleElement oder ListViewCachingStrategy.RecycleElementAndDataTemplate.

Ich reiche auch eine Funktionsanfrage bei Xamarin ein. Bitte stimmen Sie dieser zu, wenn Sie der Meinung sind, dass dies zur Standard-ListView hinzugefügt werden sollte: ListView benötigt dringend eine SelectedItemBackgroundColor-Eigenschaft


1

Fand diese schöne Option mit Effekten hier .

iOS:

[assembly: ResolutionGroupName("MyEffects")]
[assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
namespace Effects.iOS.Effects
{
    public class ListViewHighlightEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            var listView = (UIKit.UITableView)Control;

            listView.AllowsSelection = false;
        }

        protected override void OnDetached()
        {
        }
    }
}

Android:

[assembly: ResolutionGroupName("MyEffects")]
[assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
namespace Effects.Droid.Effects
{
    public class ListViewHighlightEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            var listView = (Android.Widget.ListView)Control;

            listView.ChoiceMode = ChoiceMode.None;
        }

        protected override void OnDetached()
        {
        }
    }
}

Formen:

ListView_Demo.Effects.Add(Effect.Resolve($"MyEffects.ListViewHighlightEffect"));

3
Ich denke, dies beantwortet die Frage nicht, da Sie die Farbe des getippten Elements nicht einstellen. Sie vermeiden nur die Auswahl insgesamt.
Falko

Dies beantwortet nicht nur nicht die Frage, sondern macht es auch unmöglich, eine Zeile auszuwählen!
ToolmakerSteve

Fairer Punkt, muss die Frage falsch verstanden haben, als ich hier nach einer Möglichkeit gesucht habe, die ausgewählte Zeilenfarbe zu entfernen. Ich schaffe es immer noch, eine Zeile durch Tippen auf Gesten auszuwählen, wenn ich mich richtig erinnere.
Paul Charlton

Versuchte es: Kann keine Zeile mehr auswählen, wenn dieser Effekt aktiv ist. Rollback ändert sich ...
Thomasgalliker

1

Der einfachste Weg, dies auf Android zu erreichen, besteht darin, Ihrem benutzerdefinierten Stil den folgenden Code hinzuzufügen:

@android: Farbe / transparent


-1

Die vorherigen Antworten schlagen entweder benutzerdefinierte Renderer vor oder erfordern, dass Sie das ausgewählte Element entweder in Ihren Datenobjekten oder auf andere Weise verfolgen. Dies ist nicht wirklich erforderlich, es gibt eine Möglichkeit, auf ListViewplattformunabhängige Weise mit dem Funktionieren des zu verknüpfen . Dies kann dann verwendet werden, um das ausgewählte Element nach Bedarf zu ändern. Je nach ausgewähltem Status können Farben geändert, verschiedene Teile der Zelle angezeigt oder ausgeblendet werden.

Fügen wir eine IsSelectedEigenschaft zu unserer hinzu ViewCell. Es ist nicht erforderlich, es dem Datenobjekt hinzuzufügen. In der Listenansicht wird die Zelle ausgewählt, nicht die gebundenen Daten.

public partial class SelectableCell : ViewCell {

  public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(SelectableCell), false, propertyChanged: OnIsSelectedPropertyChanged);
  public bool IsSelected {
    get => (bool)GetValue(IsSelectedProperty);
    set => SetValue(IsSelectedProperty, value);
  }

  // You can omit this if you only want to use IsSelected via binding in XAML
  private static void OnIsSelectedPropertyChanged(BindableObject bindable, object oldValue, object newValue) {
    var cell = ((SelectableCell)bindable);
    // change color, visibility, whatever depending on (bool)newValue
  }

  // ...
}

Um die fehlende Verbindung zwischen den Zellen und der Auswahl in der Listenansicht herzustellen, benötigen wir einen Konverter (die ursprüngliche Idee stammt aus dem Xamarin-Forum ):

public class IsSelectedConverter : IValueConverter {
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
    value != null && value == ((ViewCell)parameter).View.BindingContext;

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

Wir verbinden die beiden mit diesem Konverter:

<ListView x:Name="ListViewName">
  <ListView.ItemTemplate>
    <DataTemplate>
      <local:SelectableCell x:Name="ListViewCell"
        IsSelected="{Binding SelectedItem, Source={x:Reference ListViewName}, Converter={StaticResource IsSelectedConverter}, ConverterParameter={x:Reference ListViewCell}}" />
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

Diese relativ komplexe Bindung dient dazu, zu überprüfen, welches tatsächliche Element aktuell ausgewählt ist. Es vergleicht die SelectedItemEigenschaft der Listenansicht mit der BindingContextder Ansicht in der Zelle. Dieser Bindungskontext ist das Datenobjekt, an das wir tatsächlich binden. Mit anderen Worten, es wird geprüft, ob das Datenobjekt, auf das gezeigt SelectedItemwird, tatsächlich das Datenobjekt in der Zelle ist. Wenn sie gleich sind, haben wir die ausgewählte Zelle. Wir binden dies an die IsSelectedEigenschaft, die dann in XAML oder Code dahinter verwendet werden kann, um festzustellen, ob sich die Ansichtszelle im ausgewählten Zustand befindet.

Es gibt nur eine Einschränkung: Wenn Sie ein standardmäßig ausgewähltes Element festlegen möchten, wenn Ihre Seite angezeigt wird, müssen Sie ein bisschen clever sein. Leider hat Xamarin Forms keine Seite angezeigt Ereignis, wir haben nur Erscheinen und dies ist zu früh, um die Standardeinstellung festzulegen: Die Bindung wird dann nicht ausgeführt. Verwenden Sie also eine kleine Verzögerung:

protected override async void OnAppearing() {
  base.OnAppearing();

  Device.BeginInvokeOnMainThread(async () => {
    await Task.Delay(100);
    ListViewName.SelectedItem = ...;
  });
}

Verwenden Sie solchen Code niemals in Produktionssoftware. Es funktioniert möglicherweise auf Ihrem Computer, aber wenn Sie "etwas Verzögerung verwenden, um ein wenig zu warten", ist dies ein Indikator für Probleme. Was ist 100ms ist nicht genug auf einem sehr langsamen Nokia 3 Android-Handy?
Thomasgalliker

Wenn Sie einen direkten Verweis zwischen dem Namen der ListView und der ItemTemplate haben, können Sie die DataTemplate niemals in eine separate Datei extrahieren. Wenn die DataTemplate größer wird, erhalten Sie riesige xaml-Dateien (wie wir es oft getan haben).
Thomasgalliker

Ich habe Xamarin vor mehr als einem Jahr für Flutter verlassen und nie zurückgeschaut. Zum Zeitpunkt der Erstellung dieser Antwort war dies wahrscheinlich der einzige Weg, dies zu erreichen. Wenn sich die Dinge seitdem geändert haben und Sie jetzt eine bessere Lösung kennen, können Sie sie bearbeiten oder noch besser eine neue Antwort geben.
Gábor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.