Горизонтальный динамический список с кнопками вставки

#c# #wpf

Вопрос:

Я хотел бы создать горизонтальный динамический список: Список с кнопками вставки

Кнопка видна, когда мышь находится между двумя элементами.

           <ListBox
                MinHeight="32"
            dd:DragDrop.IsDragSource="True"
            dd:DragDrop.IsDropTarget="True"
            SelectionMode="Extended"

                HorizontalAlignment="Stretch">

                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal" />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>

                <ListBoxItem>
                    <TextBlock><Run Text="C1" /></TextBlock>
                </ListBoxItem>

                <ListBoxItem>
                    <TextBlock><Run Text="C2" /></TextBlock>
                </ListBoxItem>

                <ListBoxItem>
                    <TextBlock><Run Text="C3" /></TextBlock>
                </ListBoxItem>

                <ListBoxItem>
                    <TextBlock><Run Text="C4" /></TextBlock>
                </ListBoxItem>
            </ListBox>
        </ListBox>
 

Есть какие-нибудь предложения, пожалуйста?
Спасибо

Редактировать

 private void myElement_MouseEnter(object sender, MouseEventArgs e)
        {
            //change button visibility
            if (sender is Grid item)
            {
                if (item.DataContext is DataModel data)
                {
                    System.Diagnostics.Debug.WriteLine("myElement_MouseEnter: "   data.TextValue);
                }

                var border1 = (Border)item.FindName("HitTestBorder1");
                var border2 = (Border)item.FindName("HitTestBorder2");

                if (border1 is Border)
                {
                    var margin = border1.Margin;
                    margin.Left = -item.ActualWidth;
                    border1.Margin = margin;
                }

                if (border2 is Border)
                {
                    var margin = border2.Margin;
                    margin.Left = item.ActualWidth;
                    border2.Margin = margin;
                }
            }
        }
 

Проблема в том, что ширина сетки изменяется после mouse_enter… Итак, я не получаю «эффект наложения».

Комментарии:

1. вы можете подумать, что это не так уж тривиально. Вам нужны следующие концепции: Привязка источника элементов списка к модели представления, Таблички данных для различных визуальных элементов (элементов/Кнопок добавления) с помощью и Выбор элементов

2. Я нашел решение @dba

Ответ №1:

Вот решение.

Я создал холст. Он содержит 2 границы с кнопкой внутри, а панель ZPanel установлена очень высоко (=1000), чтобы быть сверху всего.

Я изменяю положение этих границ в событиях MouseEnter и MouseLeave элемента ListBoxItem (см. Таблицу элементов).

XAML

 <Border Padding="10">
        <Canvas x:Name="supergrid" MouseLeave="supergrid_MouseLeave">

            <Border x:Name="HitTestBorder1" 
                                Panel.ZIndex="1000"
                                Background="Transparent" 
                                BorderBrush="Transparent"
                                HorizontalAlignment="Center" 
                                VerticalAlignment="Center" 
                                BorderThickness="0,0,0,0" >

                <Button x:Name="button1"
                                    Visibility="Hidden"  
                                    Content=" "
                                    Height="18" Width="18" FontSize="6" Background="Red" 
                                    Click="button1_Click"
                                    />
            </Border>

            <Border x:Name="HitTestBorder2" 
                                Panel.ZIndex="1000"
                                Background="Transparent" 
                                BorderBrush="Transparent"
                                HorizontalAlignment="Center" 
                                VerticalAlignment="Center" 
                                BorderThickness="1,1,1,1" >
                <Button x:Name="button2"
                                    Visibility="Hidden"  
                                    Content=" "
                                    Height="18" Width="18" FontSize="6" Background="Green" 
                                     Click="button2_Click"
                                    />
            </Border>

            <ListBox x:Name="HorizontalListBox" 
                 ItemsSource="{Binding DataModels}"
                 Margin="0,0,0,0" 
                 HorizontalContentAlignment="Stretch" 
                 VerticalContentAlignment="Stretch" 
                 Background="Yellow" MouseLeave="HorizontalListBox_MouseLeave">

                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel Orientation="Horizontal"  />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>

                <ListBox.ItemContainerStyle>
                    <Style TargetType="ListBoxItem">
                        <!--<Setter Property="MinWidth" Value="60" />
                    <Setter Property="MinHeight" Value="40" />-->
                        <Setter Property="Background" Value="Blue" />
                        <Setter Property="Margin" Value="0" />
                        <Setter Property="Padding" Value="0" />
                    </Style>
                </ListBox.ItemContainerStyle>

                <ListBox.ItemTemplate>
                    <DataTemplate>

                        <Grid x:Name="myElement" 
                          HorizontalAlignment="Stretch" 
                          VerticalAlignment="Stretch" 
                          MouseEnter="myElement_MouseEnter" 
                          MouseLeave="myElement_MouseLeave"
                          Background="White">

                            <TextBlock x:Name="myText" 
                                   Margin="10"
                                  Text="{Binding TextValue}" 
                                   HorizontalAlignment="Center" 
                                   VerticalAlignment="Center" 
                                   TextAlignment="Center" />
                        </Grid>


                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Canvas>
    </Border>
 

C#

 public partial class MainWindow : Window
    {
        private DataModel currentDataModel;

        public ObservableCollection<DataModel> DataModels { get; }

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
            this.DataModels = new ObservableCollection<DataModel>();
            this.DataModels.Add(new DataModel("Item1"));
            this.DataModels.Add(new DataModel("Item2"));
            this.DataModels.Add(new DataModel("SuperMegaHyperLong"));
            this.DataModels.Add(new DataModel("Item3"));
            this.DataModels.Add(new DataModel("Item4"));
            this.DataModels.Add(new DataModel("123"));
            this.DataModels.Add(new DataModel("Item5"));
        }

        private void myElement_MouseEnter(object sender, MouseEventArgs e)
        {
            updateOverlay((Grid)sender);
        }

        private void updateOverlay(Grid lbi)
        { 

            if (lbi is Grid item) //the grid of listboxitem
            {
                if (item.DataContext is DataModel data)
                {
                    Debug.WriteLine("myElement_MouseEnter: "   data.TextValue);
                }

                var myText = (TextBlock)item.FindName("myText");

                if (myText is TextBlock)
                {
                    Point relativePoint = myText.TransformToAncestor(supergrid)
                              .Transform(new Point(0, 0));

                    Debug.WriteLine("relativePoint: "   relativePoint.ToString());

                    //update left button position
                    double w = button1.ActualWidth;
                    double h = button1.ActualHeight;
                    double x = relativePoint.X - w / 2.0 - myText.Margin.Left;
                    double y = relativePoint.Y;
                    updateMargin(HitTestBorder1,
                        x,
                        x   w,
                        y   h,
                        y);

                    //update right button position
                    w = button2.ActualWidth;
                    h = button2.ActualHeight;
                    x = relativePoint.X - w / 2.0 - myText.Margin.Left;
                    x  = item.ActualWidth;
                    y = relativePoint.Y;
                    updateMargin(HitTestBorder2,
                        x,
                        x   w,
                        y   h,
                        y);

                    //show the button
                    button1.Visibility = button2.Visibility = Visibility.Visible;


                    //the current item
                    if (myText.DataContext is DataModel dm)
                    {
                        this.currentDataModel = dm;
                    }
                }
            }
        }

        private void updateMargin(Border border, double left, double right, double bottom, double top)
        {
            //border = HitTestBorder2;
            var margin = border.Margin;
            margin.Left = left;
            margin.Right = right;
            margin.Top = top;
            margin.Bottom = bottom;
            border.Margin = margin;

            Debug.WriteLine("updateMargin Left: "   left.ToString()   "Right: "   right.ToString());
        }

        private void myElement_MouseLeave(object sender, MouseEventArgs e)
        {
            //change button visibility
            if (sender is Grid item)
            {
                if (item.DataContext is DataModel data)
                {
                    System.Diagnostics.Debug.WriteLine("myElement_MouseLeave: "   data.TextValue);
                }

                //button1.Visibility = button2.Visibility = Visibility.Hidden;
            }
        }

        private void HorizontalListBox_MouseLeave(object sender, MouseEventArgs e)
        {           
            Debug.WriteLine("HorizontalListBox_MouseLeave");
            //button1.Visibility = button2.Visibility = Visibility.Hidden;         
        }
        
        private void supergrid_MouseLeave(object sender, MouseEventArgs e)
        {
            Debug.WriteLine("supergrid_MouseLeave: ");

            button1.Visibility = button2.Visibility = Visibility.Hidden;
            // => :D
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("LEFT INSERT");
             
            int idx = this.DataModels.IndexOf(currentDataModel);
            
            DataModel newDataModel = new DataModel($"Item{this.DataModels.Count}");
            this.DataModels.Insert(idx, newDataModel);


            button1.Visibility = button2.Visibility = Visibility.Hidden;
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("RIGHT INSERT");
             
            int idx = this.DataModels.IndexOf(currentDataModel);
            this.DataModels.Insert(idx 1, new DataModel($"Item{this.DataModels.Count}"));

            button1.Visibility = button2.Visibility = Visibility.Hidden;
        }
    }

    public class DataModel
    {
        public string TextValue { get; set; }

        public DataModel(string textValue)
        {
            this.TextValue = textValue;
        }
    }
 

Как вы можете видеть, есть место для улучшений (например, выберите новый элемент списка). Отключите кнопки при перетаскивании, анимации, задержке и т. Д.

Комментарии:

1. Я открыт для критики. Если у вас есть лучшее решение, не стесняйтесь. Вы можете предложить свое решение, и я изменю свой принятый ответ (@BionicCode)