WPF自定义TrackPanel(ListBox)点击选中及拖动选中小切换问题
视频剪辑时间线轨道模块开发问题
背景
我正在开发视频剪辑软件的时间线(轨道)模块,为方便管理ItemsSource和SelectedItem(s),自定义了TrackPanel : ListBox控件并定义样式。
第一阶段:Thumb拦截点击事件问题
最初实现时无法点击选中剪辑片段,原因是点击事件被Thumb拦截,相关代码如下:
C#逻辑代码
public TrackPanel() { AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnThumbDragDelta), true); } private void OnThumbDragDelta(object sender, DragDeltaEventArgs e) { if (e.OriginalSource is Thumb thumb && ContainerFromElement(thumb) is ListBoxItem item) { if (thumb.Name is "thumbTrimHandleLeft" or "thumbTrimHandleRight") { return; } if (item.RenderTransform is not TranslateTransform transform) { transform = new TranslateTransform(); item.RenderTransform = transform; } transform.X += e.HorizontalChange; transform.Y += e.VerticalChange; } }
XAML样式代码
<Style TargetType="{x:Type c:TrackPanel}"> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <Thumb> <Thumb.Template> <ControlTemplate TargetType="{x:Type Thumb}"> <c:ClipControl /> </ControlTemplate> </Thumb.Template> </Thumb> </DataTemplate> </Setter.Value> </Setter> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <Canvas HorizontalAlignment="Stretch" IsItemsHost="True" /> </ItemsPanelTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBox}"> <ItemsPresenter /> </ControlTemplate> </Setter.Value> </Setter> </Style>
此前我曾用TrackPanel : Canvas实现,逻辑更简单,但需手动实现ItemsSource和SelectedItem(s),因此未采用该方案。
第二阶段:原生鼠标事件拖动导致选中项异常
改用原生鼠标事件实现拖动后,点击选中问题解决,但出现选中项自动切换的异常:

修改后的XAML样式代码
<Style TargetType="{x:Type c:TrackPanel}"> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="ListBoxItem"> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="Panel.ZIndex" Value="0" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Panel.ZIndex" Value="999" /> </Trigger> </Style.Triggers> </Style> </Setter.Value> </Setter> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate DataType="{x:Type m:Clip}"> <c:ClipControl IsSelected="{Binding IsSelected}" /> </DataTemplate> </Setter.Value> </Setter> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <Canvas HorizontalAlignment="Stretch" IsItemsHost="True" /> </ItemsPanelTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBox}"> <ItemsPresenter /> </ControlTemplate> </Setter.Value> </Setter> </Style>
修改后的C#逻辑代码
public TrackPanel() { AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnClipMouseLeftButtonDown), true); AddHandler(MouseMoveEvent, new MouseEventHandler(OnClipMouseMove), true); AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnClipMouseLeftButtonUp), true); } #region Drag Handling private UIElement? _draggedElement; private Point _dragStartPoint; private bool _isDragging = false; private void OnClipMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.OriginalSource is DependencyObject source) { if (ContainerFromElement(source) is UIElement item) { _dragStartPoint = e.GetPosition(this); _isDragging = true; _draggedElement = item; CaptureMouse(); } } } private void OnClipMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (_isDragging) { _isDragging = false; _draggedElement = null; ReleaseMouseCapture(); } } private void OnClipMouseMove(object sender, MouseEventArgs e) { if (_isDragging && _draggedElement != null && e.LeftButton == MouseButtonState.Pressed) { var current = e.GetPosition(this); var delta = current - _dragStartPoint; double left = Canvas.GetLeft(_draggedElement); if (System.Double.IsNaN(left)) { left = 0; } double top = Canvas.GetTop(_draggedElement); if (System.Double.IsNaN(top)) { top = 0; } Canvas.SetLeft(_draggedElement, left + delta.X); Canvas.SetTop(_draggedElement, top + delta.Y); _dragStartPoint = current; } } #endregion Drag Handling
内容的提问来源于stack exchange,提问作者CodingNinja
相关产品推荐
相关产品推荐

