Сетка не расширяется, как я ожидал - PullRequest
0 голосов
/ 03 мая 2019

Я пытаюсь показать стопку изображений с наложенными кнопками в пользовательском интерфейсе.Эти кнопки должны быть прозрачными и иметь значок сверху или снизу.Пока работают «кнопки», стек изображений не работает (или, если быть более точным, родительская сетка. Однако я еще не уверен на 100%).Я ожидаю, что результат, похожий на этот: https://i.imgur.com/2F7n963.png (оранжевый = Up.png / Down.png; прозрачный зеленый = верхняя CommandGrid / Button; прозрачный синий = нижняя CommandGrid / Button; черный и красный стека изображений в то время каккрасный - выбранный «уровень»).

На данный момент это мой код:

<Grid HorizontalOptions="End" VerticalOptions="StartAndExpand" Margin="10">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <controls:ItemsStack Grid.Row="0" Grid.RowSpan="2" ItemsSource="{Binding Levels}" Margin="0,15" Spacing="-8">
        <controls:ItemsStack.ItemTemplate>
            <DataTemplate>
                <controls:TintedImage Source="{Binding Source}" Foreground="{Binding Selected, Converter={StaticResource MapLevelColorConverter}}" />
            </DataTemplate>
        </controls:ItemsStack.ItemTemplate>
    </controls:ItemsStack>

    <controls:CommandGrid Grid.Row="0" Command="{Binding NavigateLevelCommand}" CommandParameter="{Binding LevelUpKey}" MinimumHeightRequest="32" Margin="0" Padding="0">
        <controls:TintedImage Source="Up.png" Foreground="{DynamicResource AccentColor}" VerticalOptions="Start" HorizontalOptions="Center" />
    </controls:CommandGrid>

    <controls:CommandGrid Grid.Row="1" Command="{Binding NavigateLevelCommand}" CommandParameter="{Binding LevelDownKey}" MinimumHeightRequest="32" Margin="0" Padding="0">
        <controls:TintedImage Source="Down.png" Foreground="{DynamicResource AccentColor}" VerticalOptions="End" HorizontalOptions="Center" />
    </controls:CommandGrid>
</Grid>

Это стека предметов (просто привязка StackLayout):

public class ItemsStack : StackLayout
{
    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(ItemsStack),
            default(IEnumerable<object>), BindingMode.TwoWay, null, ItemsSourceChanged);

    public static readonly BindableProperty ItemTemplateProperty =
        BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(ItemsStack),
            default(DataTemplate), BindingMode.TwoWay);

    public event EventHandler<SelectedItemChangedEventArgs> SelectedItemChanged;

    public IEnumerable ItemsSource
    {
        get => (IEnumerable)GetValue(ItemsSourceProperty);
        set => SetValue(ItemsSourceProperty, value);
    }

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

    private static void ItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var itemsLayout = (ItemsStack)bindable;
        itemsLayout.SetItems();
    }

    protected readonly ICommand ItemSelectedCommand;

    protected virtual void SetItems()
    {
        Children.Clear();

        if (ItemsSource == null)
        {
            ObservableSource = null;
            return;
        }

        var t = ItemsSource.GetType();
        if (t.IsConstructedGenericType && t.GetGenericTypeDefinition() == typeof(ObservableCollection<>))
        {
            var o = Activator.CreateInstance(typeof(ObservableReadOnlyCollection<>).MakeGenericType(t.GenericTypeArguments), ItemsSource);
            ObservableSource = (IObservableReadOnlyCollection<object>)o;
        }
        else
        {
            foreach (var item in ItemsSource)
                Children.Add(GetItemView(item));
        }
    }

    protected virtual View GetItemView(object item)
    {
        DataTemplate template;
        if (ItemTemplate is DataTemplateSelector selector)
            template = selector.SelectTemplate(item, this);
        else
            template = ItemTemplate;

        var content = template.CreateContent();

        var view = content as View;
        if (view == null)
            return null;

        view.BindingContext = item;

        var gesture = new TapGestureRecognizer
            {
                Command = ItemSelectedCommand,
                CommandParameter = item
            };

        return view;
    }

    IObservableReadOnlyCollection<object> _observableSource;
    protected IObservableReadOnlyCollection<object> ObservableSource
    {
        get => _observableSource;
        set
        {
            if (_observableSource != null)
            {
                _observableSource.CollectionChanged -= CollectionChanged;
            }
            _observableSource = value;

            if (_observableSource != null)
            {
                _observableSource.CollectionChanged += CollectionChanged;
            }
        }
    }

    private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                {
                    var index = e.NewStartingIndex;
                    foreach (var item in e.NewItems)
                        Children.Insert(index++, GetItemView(item));
                }
                break;
            case NotifyCollectionChangedAction.Move:
                {
                    var item = ObservableSource[e.OldStartingIndex];
                    Children.RemoveAt(e.OldStartingIndex);
                    Children.Insert(e.NewStartingIndex, GetItemView(item));
                }
                break;
            case NotifyCollectionChangedAction.Remove:
                {
                    Children.RemoveAt(e.OldStartingIndex);
                }
                break;
            case NotifyCollectionChangedAction.Replace:
                {
                    Children.RemoveAt(e.OldStartingIndex);
                    Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex]));
                }
                break;
            case NotifyCollectionChangedAction.Reset:
                Children.Clear();
                foreach (var item in ItemsSource)
                    Children.Add(GetItemView(item));
                break;
        }
    }
}

И это CommandGrid (в основном«кнопка» с пользовательским содержимым и прозрачностью):

public class CommandGrid : Grid
{
    public static readonly BindableProperty CommandProperty =
        BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(CommandGrid),
            default(ICommand), propertyChanged: OnCommandPropertyChanged);

    public static readonly BindableProperty CommandParameterProperty =
        BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(CommandGrid),
            default(object), propertyChanged: OnCommandPropertyChanged);

    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }

    private static void OnCommandPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        if (bindable is CommandGrid grid)
        {
            grid.GestureRecognizers.Clear();
            grid.GestureRecognizers.Add(new TapGestureRecognizer { Command = grid.Command, CommandParameter = grid.CommandParameter });
        }
    }
}

Я сейчас пытаюсь исправить это целый день и больше не знаю, как можно это исправить ...

Редактировать: Вот как это выглядит в настоящее время (далеко не так, как должно выглядеть): https://i.imgur.com/KVmcjwe.png

Редактировать 2: На данный момент, чтобы иметьВ первую очередь, для решения этой проблемы я использовал код.Но я, безусловно, должен провести дополнительное исследование по этому вопросу и, возможно, взглянуть на него после перерыва (или, если кто-то здесь знает, как это может работать или что здесь не так).Вот как я «решил» это сейчас:

private async void SetLevelSize()
{
    await Task.Delay(200);

    var requiredHeight = 64d;
    if (ViewModel.ShowMapLevelIcon)
    {
        LevelStack.Margin = ViewModel.ShowMapLevelButtons ? new Thickness(0, 15) : new Thickness(0);
        requiredHeight = LevelStack.Children.Sum(c => c.Height + c.Margin.Top + c.Margin.Bottom) + LevelStack.Spacing * (LevelStack.Children.Count - 1) + LevelStack.Margin.Top + LevelStack.Margin.Bottom;
        requiredHeight = Math.Max(requiredHeight, 64);
    }

    LevelGrid.HeightRequest = requiredHeight;
}
...