2016-07-25 20 views
0

Verzeihung der Cross-Posting im Xamarin-Forum, aber niemand antwortet mir dort.Benutzerdefinierte Steuerparameter verbindlich Bestellung

Vor einiger Zeit war ich auf der Suche nach einem Repeater-ähnlichen Steuerelement in XF, und ich bekomme endlich diese http://www.qimata.com/?p=7671, wirklich sehr einfach. Ich habe dann das übliche "Warum füge das nicht hinzu, warum füge ich das nicht hinzu" und so habe ich andere Eigenschaften und Vorlagen hinzugefügt. Nun, die Steuerung funktioniert jetzt sehr gut, aber ich habe ein Problem (abgesehen davon glaube ich nicht, dass dies der beste Weg ist, um mit diesem Szenario umzugehen, wenn Sie einen Ratschlag haben, teilen Sie uns bitte Ihre Gedanken).

Die gesamte Logik befindet sich im ItemsChanged-Ereignis, das ausgelöst wird, wenn die ItemSource-Eigenschaft gebunden ist. Nun, wenn ich die Eigenschaft nicht für die letzte schreibe, wenn das Ereignis ausgelöst wird, müssen die anderen noch ausgewertet werden. Zum Beispiel diese

<local:RepeaterView ShowSeparator="false" ItemsSource="{Binding itemsource}"> 

ist nicht das Gleiche von

<local:RepeaterView ItemsSource="{Binding itemsource}" ShowSeparator="false"> 

Nur im ersten Fall Eigenschaft ShowSeparator den erwarteten Wert hat, weil Itemschanged ausgelöst wird, bevor Parameterinitialisierung. Jetzt ist es nicht akzeptabel, sich über die Reihenfolge der Parameter zu informieren, und wie kann ich das auf eine anständigere Weise handhaben?

public class RepeaterView : StackLayout 
    { 
     public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate)); 
     public static readonly BindableProperty HeaderTemplateProperty = BindableProperty.Create(nameof(HeaderTemplate), typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate)); 
     public static readonly BindableProperty EmptyTextTemplateProperty = BindableProperty.Create(nameof(EmptyTextTemplate), typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate)); 
     public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(ICollection), typeof(RepeaterView), new List<object>(), BindingMode.OneWay, null, propertyChanged: (bindable, oldValue, newValue) => { ItemsChanged(bindable, (ICollection)oldValue, (ICollection)newValue); }); 

     public static readonly BindableProperty EmptyTextProperty = BindableProperty.Create(nameof(EmptyText), typeof(string), typeof(RepeaterView), string.Empty); 

     public static readonly BindableProperty SelectedItemCommandProperty = BindableProperty.Create(nameof(SelectedItemCommand), typeof(ICommand), typeof(RepeaterView), default(ICommand)); 

     public ICollection ItemsSource 
     { 
      get { return (ICollection)GetValue(ItemsSourceProperty); } 
      set { SetValue(ItemsSourceProperty, value); } 
     } 

     public DataTemplate ItemTemplate 
     { 
      get { return (DataTemplate)GetValue(ItemTemplateProperty); } 
      set { SetValue(ItemTemplateProperty, value); } 
     } 

     public DataTemplate HeaderTemplate 
     { 
      get { return (DataTemplate)GetValue(HeaderTemplateProperty); } 
      set { SetValue(HeaderTemplateProperty, value); } 
     } 

     public DataTemplate EmptyTextTemplate 
     { 
      get { return (DataTemplate)GetValue(EmptyTextTemplateProperty); } 
      set { SetValue(EmptyTextTemplateProperty, value); } 
     } 

     public string EmptyText 
     { 
      get { return (string)GetValue(EmptyTextProperty); } 
      set { SetValue(EmptyTextProperty, value); } 
     } 

     public ICommand SelectedItemCommand 
     { 
      get { return (ICommand)GetValue(SelectedItemCommandProperty); } 
      set { SetValue(SelectedItemCommandProperty, value); } 
     } 

     public bool ShowSeparator { get; set; } = true; 

     private static void ItemsChanged(BindableObject bindable, ICollection oldValue, ICollection newValue) 
     { 
      var repeater = (RepeaterView)bindable; 

      repeater.Children.Clear(); 

      var headerTemplate = repeater.HeaderTemplate; 

      if (headerTemplate != null) 
      { 
       var content = headerTemplate.CreateContent(); 
       if (!(content is View) && !(content is ViewCell)) 
       { 
        //throws exception 
       } 

       var view = (content is View) ? content as View : ((ViewCell)content).View; 

       repeater.Children.Add(view); 
       repeater.Children.Add(new Divider()); 
      } 

      if (newValue.Count == 0 && (repeater.EmptyTextTemplate != null || !string.IsNullOrEmpty(repeater.EmptyText))) 
      { 
       if (repeater.EmptyTextTemplate == null) 
        repeater.Children.Add(new Label { Text = repeater.EmptyText }); 
       else 
       { 
        var content = repeater.EmptyTextTemplate.CreateContent(); 
        if (!(content is View) && !(content is ViewCell)) 
        { 
         //throws exception 
        } 

        var view = (content is View) ? content as View : ((ViewCell)content).View; 

        repeater.Children.Add(view); 
       } 

       return; 
      } 

      var dataTemplate = repeater.ItemTemplate; 

      foreach (object item in newValue) 
      { 
       var content = dataTemplate.CreateContent(); 
       if (!(content is View) && !(content is ViewCell)) 
       { 
        //throws exception 
       } 

       var view = (content is View) ? content as View : ((ViewCell)content).View; 

       if (repeater.SelectedItemCommand != null) 
       { 
        var tapGestureRecognizer = new TapGestureRecognizer(); 

        tapGestureRecognizer.Tapped += (sender, e) => { repeater.SelectedItemCommand?.Execute(item); }; 

        view.GestureRecognizers.Add(tapGestureRecognizer); 
       } 

       view.BindingContext = item; 

       repeater.Children.Add(view); 

       if (repeater.ShowSeparator) 
        repeater.Children.Add(new Divider { Margin = new Thickness(5, 0) }); 
      } 
     } 
    } 
} 

Antwort

0

Die beste Strategie wäre hier, um sicherzustellen, dass die Elemente nur zuerst berechnet werden, wenn sie wirklich (wie in LayoutChildren) angefordert werden.

So in OnItemSourceChanged, legen Sie nur die ItemSource, aber nichts weiter zu tun, wenn Initialisierung wurde bereits durchgeführt.

Sollte aussehen etwas wie diese (pseude-Code):

private static void ItemsChanged(...) 
{ 
    var repeater = (Repeaterview)bindable; 

    repeater.ItemsSource = value; 

    if(repeater.IsInitialized) UpdateItems(); 
} 


private override void LayoutChildren() 
{ 
    IsInitialized = true; 

    UpdateItems(); 
} 

Dies ist die grundlegende Strategie ist. Ich werde auf die richtigen Methoden/Overrides aktualisieren, sobald ich die Zeit dazu finde. Fühlen Sie sich frei, diese Antwort zu aktualisieren, wenn Sie zufällig vor mir herausfinden.

+0

Hallo, danke für die Antwort. Ich habe Ihre vorgeschlagene Lösung versucht, aber das überschriebene Ereignis 'protected override void LayoutChildren (double x, doppelte y, doppelte Breite, doppelte Höhe)' ** nie ** feuert, was mache ich falsch? – DottorPagliaccius

Verwandte Themen