2016-07-25 9 views
0

匿名、xamarinフォーラムでのクロスポストは誰も私に答えません。カスタムコントロールのパラメータバインドの順序

私はXFでリピーターのようなコントロールを探していましたが、最終的にはhttp://www.qimata.com/?p=7671という非常に単純なものです。私はいつも「なぜこれを追加しないのですか、それをなぜ追加しないのですか」を始めました。それで他のプロパティとテンプレートを追加しました。今、コントロールは非常にうまく動作しますが、私は問題があります(この点を除けば、このシナリオを処理する最良の方法ではないと思っています。

すべてのロジックはItemChangedイベントにあり、ItemSourceプロパティがバインドされたときに発生します。今、最後のプロパティを書き込まない場合、イベントが発生したときにもう一方のプロパティがまだ評価されていません。例えば、この

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

は最初のケースプロパティSh​​owSeparatorに

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

の同じではない火災パラメータ初期化前ItemsChangedので、期待値を有しています。さて、パラメータの順序を気にすることは受け入れられません。どうすればこれをもっとうまく扱うことができますか?

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) }); 
      } 
     } 
    } 
} 

答えて

0

最善の戦略は、ここで彼らが本当に(LayoutChildrenのように)要求された後のアイテムにのみ、最初に計算されていることを確認することです。

したがって、OnItemSourceChangedでは、ItemSourceのみを設定しますが、初期化が完了していない限り、それ以上は実行しません。

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

    repeater.ItemsSource = value; 

    if(repeater.IsInitialized) UpdateItems(); 
} 


private override void LayoutChildren() 
{ 
    IsInitialized = true; 

    UpdateItems(); 
} 

これが基本戦略です:

は多少この(pseudeコード)のようになります。私はそうする時間が見つかると、正しいメソッド/オーバーライドに更新します。あなたが私の前で見つけた場合は、この回答を更新してください。

+0

こんにちは、ありがとうございました。私はあなたの提案された解決策を試しましたが、オーバーライドされたイベント 'protected override void LayoutChildren(double x、double y、double width、double height)' ** never ** fire、 – DottorPagliaccius