我几乎得到这个从一个小恼人的事情工作除了...
由于列表框选择鼠标情况下,如果你开始用鼠标向下拖动时选择的最后一个项目拖动它工作正常,但如果你选择所有项目拖动,然后再点击选择开始拖动,你点击一个被勾选,拖拽后留下的。
的最佳方式有什么想法来解决这个问题?
<DockPanel LastChildFill="True">
<ListBox ItemsSource="{Binding SourceItems}"
SelectionMode="Multiple"
PreviewMouseLeftButtonDown="HandleLeftButtonDown"
PreviewMouseLeftButtonUp="HandleLeftButtonUp"
PreviewMouseMove="HandleMouseMove"
MultiSelectListboxDragDrop:ListBoxExtension.SelectedItemsSource="{Binding SelectedItems}"/>
<ListBox ItemsSource="{Binding DestinationItems}"
AllowDrop="True"
Drop="DropOnToDestination"/>
<DockPanel>
...
public partial class Window1
{
private bool clickedOnSourceItem;
public Window1()
{
InitializeComponent();
DataContext = new WindowViewModel();
}
private void DropOnToDestination(object sender, DragEventArgs e)
{
var viewModel = (WindowViewModel)
e.Data.GetData(typeof(WindowViewModel));
viewModel.CopySelectedItems();
}
private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var sourceElement = (FrameworkElement)sender;
var hitItem = sourceElement.InputHitTest(e.GetPosition(sourceElement))
as FrameworkElement;
if(hitItem != null)
{
clickedOnSourceItem = true;
}
}
private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
{
clickedOnSourceItem = false;
}
private void HandleMouseMove(object sender, MouseEventArgs e)
{
if(clickedOnSourceItem)
{
var sourceItems = (FrameworkElement)sender;
var viewModel = (WindowViewModel)DataContext;
DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
clickedOnSourceItem = false;
}
}
}
所以......已成为一个滚草徽章骄傲的主人,我得回到这个尝试和寻找办法解决它。 ;-)
我不知道我喜欢的解决方案,所以我还是很愿意接受任何更好的方法。
基本上,我落得这样做是记住ListBoxItem的最后点击&然后确保被拖拽前添加到选定的项目。 这也意味着在看多远开始拖动之前移动鼠标 - 因为点击所选项目不选它有时可能会导致它越来越再次选择,如果鼠标反弹开始有点拖动操作。
最后,我加了一些热点追踪到ListBox项目,因此,如果你上的鼠标按下所选项目它会得到未选中的,但你仍然得到一些反馈,以表明它会被包含在拖动操作。
private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var source = (FrameworkElement)sender;
var hitItem = source.InputHitTest(e.GetPosition(source)) as FrameworkElement;
hitListBoxItem = hitItem.FindVisualParent<ListBoxItem>();
origPos = e.GetPosition(null);
}
private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
{
hitListBoxItem = null;
}
private void HandleMouseMove(object sender, MouseEventArgs e)
{
if (ShouldStartDrag(e))
{
hitListBoxItem.IsSelected = true;
var sourceItems = (FrameworkElement)sender;
var viewModel = (WindowViewModel)DataContext;
DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
hitListBoxItem = null;
}
}
private bool ShouldStartDrag(MouseEventArgs e)
{
if (hitListBoxItem == null)
return false;
var curPos = e.GetPosition(null);
return
Math.Abs(curPos.Y-origPos.Y) > SystemParameters.MinimumVerticalDragDistance ||
Math.Abs(curPos.X-origPos.X) > SystemParameters.MinimumHorizontalDragDistance;
}
XAML的变化包括热追踪......
<Style TargetType="ListBoxItem">
<Setter Property="Margin" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<Border Background="{TemplateBinding Background}" />
<Border Background="#BEFFFFFF" Margin="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition /><RowDefinition />
</Grid.RowDefinitions>
<Border Margin="1" Grid.Row="0" Background="#57FFFFFF" />
</Grid>
</Border>
<ContentPresenter Margin="8,5" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="PowderBlue" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="#5FB0E0E6" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我发现一个很简单的方法,使Windows资源管理器一样拖/有选择的多个项目时掉落的行为。 该解决方案取代了常用的ListBox
一点点导出垫片,它取代了ListBoxItem
具有更智能的版本。 这样一来,我们就可以在合适的水平封装点击状态和调入的保护选择机械ListBox
。 下面是相关类。 对于一个完整的示例,请参阅我在github回购 。
public class ListBoxEx : ListBox
{
protected override DependencyObject GetContainerForItemOverride()
{
return new ListBoxItemEx();
}
class ListBoxItemEx : ListBoxItem
{
private bool _deferSelection = false;
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (e.ClickCount == 1 && IsSelected)
{
// the user may start a drag by clicking into selected items
// delay destroying the selection to the Up event
_deferSelection = true;
}
else
{
base.OnMouseLeftButtonDown(e);
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (_deferSelection)
{
try
{
base.OnMouseLeftButtonDown(e);
}
finally
{
_deferSelection = false;
}
}
base.OnMouseLeftButtonUp(e);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
// abort deferred Down
_deferSelection = false;
base.OnMouseLeave(e);
}
}
}
一个办法是不要让列表框或ListView删除选定的项目,直到的MouseLeftButtonUp被触发。 示例代码:
List<object> removedItems = new List<object>();
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
ListBox box = sender as ListBox;
if (removedItems.Contains(e.RemovedItems[0]) == false)
{
foreach (object item in e.RemovedItems)
{
box.SelectedItems.Add(item);
removedItems.Add(item);
}
}
}
}
private void ListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (removedItems.Count > 0)
{
ListBox box = sender as ListBox;
foreach (object item in removedItems)
{
box.SelectedItems.Remove(item);
}
removedItems.Clear();
}
}
我很惊讶,ListBox和Windows资源管理器之间的行为差异还没有被照顾后整个.NET框架的3个大更新4年。
我跑这个问题早在Silverlight中3.我结束了覆盖按下鼠标和鼠标了事件处理程序完全模拟Windows资源管理器的行为。
我没有源代码的任何以上,但逻辑应该是:
当按下鼠标
- 如果没有选择目标项,清除现有的选择
- 如果Ctrl键是向下,目标项目添加到选择
- 如果Shift键是向下
- 如果有以前选定的项目,添加目标项目和以往项目选择之间的所有项目
- 否则唯一的目标项目添加到选择
- 如果按Ctrl键被按下,如果目标项目只选择取消选择
当鼠标向上(同一项目)
- 如果目标项目被选择
- 如果Ctrl键是向下,从列表中删除项目
- 如果Shift键是向下
- 如果有以前选定的项目,删除目标项目,并从以前的选择项之间的所有项
- 否则只从列表中删除目标项目
不过这确实应该是微软的工作,以更新的行为是操作系统一致,更加直观。 我,如果任何机构想为它投票提交它作为微软的一个错误: http://connect.microsoft.com/VisualStudio/feedback/details/809192/