我尝试在UI中显示带有叠加按钮的图像堆栈。这些按钮应该是透明的,并且在顶部或底部应有一个图标。当“按钮”起作用时,图像堆栈不起作用(或更精确地说,是父网格。但是,我还不确定100%)。我希望得到与以下结果类似的结果:https://i.imgur.com/2F7n963.png(橙色= Up.png / Down.png;透明的绿色=上部CommandGrid /按钮;透明的蓝色=下部CommandGrid /按钮;黑色和红色,图像堆栈为红色是所选的“级别”)。
到目前为止,这是我的代码:
<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>
这是ItemsStack(只是可绑定的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;
}