Почему не получается удалять подряд элементы из середины Carousel?
В Carousel отображается коллекция объектов, которые мне нужно добавлять/удалять, если удалять элементы с конца или начала, то удается удалить больше одного элемента подряд, но если удалять из середины, то получается удалить только 1 элемент, как я понял, Carousel обновляется только если я сдвину выбранный элемент, как можно это исправить? Пробовал Carousel.UpdateLayout() но не сработало:
Page.xaml:
<controls:Carousel x:Name="ClipboardsCarousel"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0,0,0,35"
ItemRotationX="0" ItemRotationY="35" ItemRotationZ="-5"
ItemsSource="{Binding Clipboards}"
InvertPositive="True"
ItemDepth="250"
ItemMargin="25"
Orientation="Horizontal"
SelectedIndex="0">
<controls:Carousel.EasingFunction>
<CubicEase EasingMode="EaseInOut" />
</controls:Carousel.EasingFunction>
<controls:Carousel.ItemTemplate>
<DataTemplate x:DataType="local:ClipboardModel">
<StackPanel Background="#1F1F1F"
BorderThickness="1.8"
BorderBrush="{StaticResource SystemAccentColor}"
CornerRadius="8"
Padding="15"
Width="250"
Height="350">
<TextBlock Text="{Binding Title}"
TextWrapping="Wrap"
TextTrimming="CharacterEllipsis"
TextAlignment="Center"
FontSize="18"
Margin="0,2,0,16"
/>
<TextBlock Text="{Binding Content}"
MaxHeight="270"
TextWrapping="Wrap"
TextTrimming="CharacterEllipsis"
Foreground="{StaticResource ShadedTextColor}"
/>
</StackPanel>
</DataTemplate>
</controls:Carousel.ItemTemplate>
</controls:Carousel>
Page.xaml.cs:
private void ConfirmDeleteButton_Click(object sender, RoutedEventArgs e)
{
int currentIndex = ClipboardsCarousel.SelectedIndex;
viewModel.DeleteClipboard(((ClipboardModel)ClipboardsCarousel.SelectedItem).Title);
if (ClipboardsCarousel.SelectedIndex == viewModel.Clipboards.Count)
{
ClipboardsCarousel.SelectedIndex -= 1;
}
else
{
ClipboardsCarousel.SelectedIndex = currentIndex;
}
ClipboardsCarousel.UpdateLayout();
}
ViewModel:
public ObservableCollection<ClipboardModel> Clipboards { get; set; } =
new ObservableCollection<ClipboardModel>
{
};
public void DeleteClipboard(string title)
{
ClipboardsDatabase.DeleteClipboard(title);
RefreshClipboards();
}
Ответы (2 шт):
Вы обновляете данные, но пытаетесь работать с контролом до того как он подтянет изменения, ведь UI поток все еще занят обработкой вашего кода.
Получается, что хронологически вы сначала игретесь с индексом выделенного элемента, а потом только контрол обновляет данные, в результате чего индекс выделенного элемента слетает.
Можно воткнуть вот такой некрасивый костыль, показываю для наглядности проблемы.
private async void ConfirmDeleteButton_Click(object sender, RoutedEventArgs e)
{
int currentIndex = ClipboardsCarousel.SelectedIndex;
viewModel.DeleteClipboard(((ClipboardModel)ClipboardsCarousel.SelectedItem).Title);
await Task.Delay(1);
if (currentIndex == viewModel.Clipboards.Count)
{
currentIndex--;
}
ClipboardsCarousel.SelectedIndex = currentIndex;
}
Ну или добавлять обработчик события, вызываемый при смене выделенного элемента и обрабатывать поведение там.
А вообще надо использовать привязки данных x:Bind или Binding, тогда можно было бы через SelectedItem этим всем управлять без костылей. А чтобы не мучаться с клик-обработчиками, используйте команды ICommand, тогда код, обрабатывающий клики будет тоже во вьюмодели.
Получится что-то вроде такого во вьюмодели
public ClipboardModel SelectedClipboard { get; set; } // +INPC реализация
public ICommand DeleteCommand { get; } = new RelayCommand(parameter =>
{
var index = Clipboards.IndexOf(SelectedClipboard);
Clipboards.Remove(SelectedClipboard);
if (index == Clipboards.Count);
index--;
SelectedClipboard = Clipboards[index];
}, parameter => SelectedClipboard != null);
<Button Content="Удалить" Command="{Binding DeleteCommand}"/>
<controls:Carousel SelectedItem="{Binding SelectedClipboard}" .../>
А SelectedIndex="0" надо вообще убрать, при наличии привязки SelectedItem оно само заработает так как вам надо.
Кстати, привыкайте все-таки использовать x:Bind, оно бодрее работает и в некоторых случаях удобнее использовать.
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
=> (_execute, _canExecute) = (execute, canExecute);
public bool CanExecute(object parameter)
=> _canExecute == null || _canExecute(parameter);
public void Execute(object parameter)
=> _execute(parameter);
}
Не знаю насколько это правильно, но мне помогло добавление этого ифа в конец:
private void ConfirmDeleteButton_Click(object sender, RoutedEventArgs e)
{
...
if (viewModel.Clipboards.ToList().Count != 0)
{
ClipboardsCarousel.SelectedItem = viewModel.Clipboards[currentIndex];
}
}