[英]How can I animate a TextBlock when a SelectionChanged() event of a ListBox is called?
[英]How can I fire SelectionChanged event on MouseUp
我有一个 ListView,我想改变行为,所以SelectionChanged
事件会在 MouseUp 上触发,而不是在 MouseDown 上触发。 我想改变行为的原因是,当我想拖动对象(在 MouseMove 上)时,当我在要移动的对象上单击(MouseDown)时,选择会改变。
我找到了那段代码,但这仅适用于简单的选择,我想允许选择多个项目。
public static class SelectorBehavior
{
#region bool ShouldSelectItemOnMouseUp
public static readonly DependencyProperty ShouldSelectItemOnMouseUpProperty =
DependencyProperty.RegisterAttached(
"ShouldSelectItemOnMouseUp", typeof(bool), typeof(SelectorBehavior),
new PropertyMetadata(default(bool), HandleShouldSelectItemOnMouseUpChange));
public static void SetShouldSelectItemOnMouseUp(DependencyObject element, bool value)
{
element.SetValue(ShouldSelectItemOnMouseUpProperty, value);
}
public static bool GetShouldSelectItemOnMouseUp(DependencyObject element)
{
return (bool)element.GetValue(ShouldSelectItemOnMouseUpProperty);
}
private static void HandleShouldSelectItemOnMouseUpChange(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Selector selector)
{
selector.PreviewMouseDown -= HandleSelectPreviewMouseDown;
selector.MouseUp -= HandleSelectMouseUp;
if (Equals(e.NewValue, true))
{
selector.PreviewMouseDown += HandleSelectPreviewMouseDown;
selector.MouseUp += HandleSelectMouseUp;
}
}
}
private static void HandleSelectMouseUp(object sender, MouseButtonEventArgs e)
{
var selector = (Selector)sender;
if (e.ChangedButton == MouseButton.Left && e.OriginalSource is Visual source)
{
var container = selector.ContainerFromElement(source);
if (container != null)
{
var index = selector.ItemContainerGenerator.IndexFromContainer(container);
if (index >= 0)
{
selector.SelectedIndex = index;
}
}
}
}
private static void HandleSelectPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = e.ChangedButton == MouseButton.Left;
}
#endregion
}
这是我的 Xaml:
<ListView x:Name="ListViewContract" SelectedItem="{Binding SelectedContract}" ItemsSource="{Binding ListContractsView}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" MouseDoubleClick="ListViewContract_DoubleClick" GridViewColumnHeader.Click ="GridViewHeaderClicked" SelectionChanged="ListViewContract_SelectionChanged" Visibility="{Binding Grid1Visible, Converter={StaticResource BoolToVisConverter}}"
MouseMove="ListViewContract_MouseMove" local:SelectorBehavior.ShouldSelectItemOnMouseUp="True">
<ListView.View>
<GridView AllowsColumnReorder="true" x:Name="GridViewContract">
<GridViewColumn DisplayMemberBinding="{Binding ID}" Header="ID" Width="{Binding WidthIDs, Mode=TwoWay}"/>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="{x:Static p:Resources.Name}" Width="{Binding WidthColumnContractName, Mode=TwoWay}"/>
<GridViewColumn DisplayMemberBinding="{Binding Code}" Header="{x:Static p:Resources.Number}" Width="{Binding WidthColumnContractCode, Mode=TwoWay}"/>
<GridViewColumn DisplayMemberBinding="{Binding Comm}" Header="{x:Static p:Resources.Commentary}" Width="{Binding WidthColumnContractComm, Mode=TwoWay}"/>
<GridViewColumn DisplayMemberBinding="{Binding Revision}" Header="{x:Static p:Resources.Revision}" Width="{Binding WidthColumnContractRev, Mode=TwoWay}"/>
<GridViewColumn Header="{x:Static p:Resources.Advancement}" Width="{Binding WidthColumnContractAdv, Mode=TwoWay}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<Rectangle Name="AFF_Track" Height="12" Stroke="black" StrokeThickness="1" Fill="{Binding RectangleProgression}" Tag="{Binding ID}" MouseMove="mouseOverProgressionContractAss">
<Rectangle.ToolTip>
<ContentControl Template="{StaticResource ToolTipOperations}"/>
</Rectangle.ToolTip>
</Rectangle>
<Rectangle Name="AFF_Track2" Height="12" Stroke="black" StrokeThickness="1" Fill="{Binding RectangleProgressionWeight}" Tag="{Binding ID}" MouseMove="mouseOverProgressionContractAss">
<Rectangle.ToolTip>
<ContentControl Template="{StaticResource ToolTipOperations}"/>
</Rectangle.ToolTip>
</Rectangle>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="{x:Static p:Resources.Advancement}" Width="{Binding WidthColumnContractAdvRep, Mode=TwoWay}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<Rectangle Name="AFF_TrackRep" Height="12" Stroke="black" StrokeThickness="1" Fill="{Binding RectangleProgressionRep}" Tag="{Binding ID}" MouseMove="mouseOverProgressionContractRep">
<Rectangle.ToolTip>
<ContentControl Template="{StaticResource ToolTipOperations}"/>
</Rectangle.ToolTip>
</Rectangle>
<Rectangle Name="AFF_Track2Rep" Height="12" Stroke="black" StrokeThickness="1" Fill="{Binding RectangleProgressionRepWeight}" Tag="{Binding ID}" MouseMove="mouseOverProgressionContractRep">
<Rectangle.ToolTip>
<ContentControl Template="{StaticResource ToolTipOperations}"/>
</Rectangle.ToolTip>
</Rectangle>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding Manager.CompleteName}" Header="{x:Static p:Resources.ProjectManager}" Width="{Binding WidthColumnContractManager, Mode=TwoWay}"/>
<GridViewColumn DisplayMemberBinding="{Binding Client.Name}" Header="{x:Static p:Resources.Customer}" Width="{Binding WidthColumnContractCustomer, Mode=TwoWay}"/>
<GridViewColumn DisplayMemberBinding="{Binding Source}" Header="{x:Static p:Resources.Source}" Width="{Binding WidthColumnContractSource, Mode=TwoWay}"/>
</GridView>
</ListView.View>
</ListView>
您可以修改您的行为以分别处理扩展MultiSelector
(如DataGrid
)和ListBox
的控件。 ListBox
(因此也是ListView
)不是MultiSelector
,但在将ListBox.SelectionMode
属性设置为不同于SelectionMode.Single
(这是默认设置)时支持多选。
对于每个其他简单的选择器,您都必须手动跟踪所选项目。 您还必须拦截Selector.Unselected
事件,以防止 Selector 在多选模式下取消选择所选项目 - Selector
仅支持单个项目选择。
以下示例显示了在附加的Selector
不是ListBox
或MultiSelector
的情况下如何跟踪所选项目。 出于这个原因,该行为公开了一个公共的只读SelectedItems
依赖属性。
该示例还显示了如何观察按下的键盘键以过滤多选用户操作。 此示例将 Shift或Ctrl键过滤为触发多选行为的手势:按下CTRL时随机多选和按下Shift键时进行范围选择。 否则, Selector
将像往常一样运行(释放鼠标左键时单选)。
选择器服务.cs
public class SelectorService : DependencyObject
{
#region IsSelectItemOnMouseUpEnabled attached property
public static readonly DependencyProperty IsSelectItemOnMouseUpEnabledProperty = DependencyProperty.RegisterAttached(
"IsSelectItemOnMouseUpEnabled",
typeof(bool),
typeof(SelectorService),
new PropertyMetadata(default(bool), OnIsSelectItemOnMouseUpEnabledChanged));
public static void SetIsSelectItemOnMouseUpEnabled(DependencyObject attachedElement, bool value) => attachedElement.SetValue(IsSelectItemOnMouseUpEnabledProperty, value);
public static bool GetIsSelectItemOnMouseUpEnabled(DependencyObject attachedElement) => (bool)attachedElement.GetValue(IsSelectItemOnMouseUpEnabledProperty);
#endregion IsSelectItemOnMouseUpEnabled attached property
#region SelectedItems attached property
public static IList GetSelectedItems(DependencyObject attachedElement) => (IList)attachedElement.GetValue(SelectedItemsProperty);
public static void SetSelectedItems(DependencyObject attachedElement, IList value) => attachedElement.SetValue(SelectedItemsPropertyKey, value);
private static readonly DependencyPropertyKey SelectedItemsPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
"SelectedItems",
typeof(IList),
typeof(SelectorService),
new PropertyMetadata(default));
public static readonly DependencyProperty SelectedItemsProperty = SelectedItemsPropertyKey.DependencyProperty;
#endregion SelectedItems attached property
#region SelectionMode attached property (private)
private static SelectionMode GetOriginalSelectionModeBackup(DependencyObject attachedElement) => (SelectionMode)attachedElement.GetValue(OriginalSelectionModeBackupProperty);
private static void SetOriginalSelectionModeBackup(DependencyObject attachedElement, SelectionMode value) => attachedElement.SetValue(OriginalSelectionModeBackupProperty, value);
private static readonly DependencyProperty OriginalSelectionModeBackupProperty = DependencyProperty.RegisterAttached(
"OriginalSelectionModeBackup",
typeof(SelectionMode),
typeof(SelectorService),
new PropertyMetadata(default));
#endregion SelectionMode attached property
private static bool IsRandomMultiSelectEngaged
=> Keyboard.Modifiers is ModifierKeys.Control;
private static bool IsRangeMultiSelectEngaged
=> Keyboard.Modifiers is ModifierKeys.Shift;
private static Dictionary<Selector, bool> IsMultiSelectAppliedMap { get; } = new Dictionary<Selector, bool>();
private static int SelectedRangeStartIndex { get; set; } = -1;
private static int SelectedRangeEndIndex { get; set; } = -1;
private static void OnIsSelectItemOnMouseUpEnabledChanged(DependencyObject attachedElement, DependencyPropertyChangedEventArgs e)
{
if (attachedElement is not Selector selector)
{
return;
}
if ((bool)e.NewValue)
{
WeakEventManager<FrameworkElement, MouseButtonEventArgs>.AddHandler(selector, nameof(selector.PreviewMouseLeftButtonDown), OnPreviewLeftMouseButtonDown);
WeakEventManager<FrameworkElement, MouseButtonEventArgs>.AddHandler(selector, nameof(selector.PreviewMouseLeftButtonUp), OnPreviewLeftMouseButtonUp);
if (selector.IsLoaded)
{
InitializeAttachedElement(selector);
}
else
{
selector.Loaded += OnSelectorLoaded;
}
}
else
{
WeakEventManager<FrameworkElement, MouseButtonEventArgs>.RemoveHandler(selector, nameof(selector.PreviewMouseLeftButtonDown), OnPreviewLeftMouseButtonDown);
WeakEventManager<FrameworkElement, MouseButtonEventArgs>.RemoveHandler(selector, nameof(selector.PreviewMouseLeftButtonUp), OnPreviewLeftMouseButtonUp);
SetSelectedItems(selector, null);
IsMultiSelectAppliedMap.Remove(selector);
selector.Loaded -= OnSelectorLoaded;
if (selector is ListBox listBox)
{
listBox.SelectionMode = GetOriginalSelectionModeBackup(listBox);
}
}
}
private static void OnSelectorLoaded(object sender, RoutedEventArgs e)
{
var selector = sender as Selector;
selector.Loaded -= OnSelectorLoaded;
InitializeAttachedElement(selector);
}
private static void InitializeAttachedElement(Selector selector)
{
IList selectedItems = new List<object>();
if (selector is ListBox listBox)
{
ValidateListBoxSelectionMode(listBox);
selectedItems = listBox.SelectedItems;
}
else if (selector is MultiSelector multiSelector)
{
selectedItems = multiSelector.SelectedItems;
}
else if (selector.SelectedItem is not null)
{
selectedItems.Add(selector.SelectedItem);
}
SetSelectedItems(selector, selectedItems);
IsMultiSelectAppliedMap.Add(selector, false);
}
private static void OnUnselected(object? sender, RoutedEventArgs e)
{
var itemContainer = sender as DependencyObject;
Selector.SetIsSelected(itemContainer, true);
Selector.RemoveUnselectedHandler(itemContainer, OnUnselected);
}
private static void OnPreviewLeftMouseButtonUp(object sender, MouseButtonEventArgs e)
{
var selector = sender as Selector;
DependencyObject itemContainerToSelect = ItemsControl.ContainerFromElement(selector, e.OriginalSource as DependencyObject);
DependencyObject currentSelectedItemContainer = selector.ItemContainerGenerator.ContainerFromItem(selector.SelectedItem);
if (itemContainerToSelect is null)
{
return;
}
if (IsRandomMultiSelectEngaged)
{
MultiSelectItems(selector, itemContainerToSelect, currentSelectedItemContainer);
}
else if (IsRangeMultiSelectEngaged)
{
MultiSelectRangeOfItems(selector, itemContainerToSelect, currentSelectedItemContainer);
}
else
{
SingleSelectItem(selector, itemContainerToSelect, currentSelectedItemContainer);
}
IsMultiSelectAppliedMap[selector] = IsRandomMultiSelectEngaged || IsRangeMultiSelectEngaged;
}
private static void MultiSelectRangeOfItems(Selector selector, DependencyObject itemContainerToSelect, DependencyObject currentSelectedItemContainer)
{
int clickedContainerIndex = selector.ItemContainerGenerator.IndexFromContainer(itemContainerToSelect);
// In case there is not any preselected item. Otherwis SingleSlectItem() has already set the SelectedRangeStartIndex property.
if (SelectedRangeStartIndex == -1)
{
SelectedRangeStartIndex = clickedContainerIndex;
DependencyObject itemContainer = selector.ItemContainerGenerator.ContainerFromIndex(SelectedRangeStartIndex);
MultiSelectItems(selector, itemContainer, currentSelectedItemContainer);
return;
}
// Complete the range selection
else if (SelectedRangeEndIndex == -1)
{
bool isSelectionRangeFromTopToBotton = clickedContainerIndex > SelectedRangeStartIndex;
if (isSelectionRangeFromTopToBotton)
{
SelectedRangeEndIndex = clickedContainerIndex;
}
else
{
// Selection is from bottom to top, so we need to swap start and end index
// as they are used to initialize the for-loop.
SelectedRangeEndIndex = SelectedRangeStartIndex;
SelectedRangeStartIndex = clickedContainerIndex;
}
for (int itemIndex = SelectedRangeStartIndex; itemIndex <= SelectedRangeEndIndex; itemIndex++)
{
DependencyObject itemContainer = selector.ItemContainerGenerator.ContainerFromIndex(itemIndex);
bool isContainerUnselected = !Selector.GetIsSelected(itemContainer);
if (isContainerUnselected)
{
MultiSelectItems(selector, itemContainer, currentSelectedItemContainer);
}
}
// Only remember start index to append sequential ranges (more clicks while Shift key is pressed)
// and invalidate the end index.
SelectedRangeEndIndex = -1;
}
}
private static void MultiSelectItems(Selector? selector, DependencyObject itemContainerToSelect, DependencyObject currentSelectedItemContainer)
{
bool oldIsSelectedValue = Selector.GetIsSelected(itemContainerToSelect);
// Toggle the current state
bool newIsSelectedValue = oldIsSelectedValue ^= true;
if (selector is ListBox listBox)
{
// In case the mode was overriden externally, force it back
// but store the changed value to allow roll back when the behavior gets disabled.
ValidateListBoxSelectionMode(listBox);
}
// If the current Selector instance does not support native multi select
// we need to prevent the Selector from unselecting previous selected items.
// By setting unselected items back to selected we can enforce a visual multi select feedback.
if (selector is not MultiSelector and not ListBox)
{
if (newIsSelectedValue && currentSelectedItemContainer is not null)
{
Selector.AddUnselectedHandler(currentSelectedItemContainer, OnUnselected);
}
}
Selector.SetIsSelected(itemContainerToSelect, newIsSelectedValue);
(itemContainerToSelect as UIElement)?.Focus();
if (selector is not MultiSelector and not ListBox)
{
object item = selector.ItemContainerGenerator.ItemFromContainer(itemContainerToSelect);
IList selectedItems = GetSelectedItems(selector);
if (newIsSelectedValue)
{
selectedItems.Add(item);
}
else
{
selectedItems.Remove(item);
}
}
}
private static void SingleSelectItem(Selector? selector, DependencyObject itemContainerToSelect, DependencyObject currentSelectedItemContainer)
{
bool isPreviousSelectMultiSelect = IsMultiSelectAppliedMap[selector];
if (!isPreviousSelectMultiSelect)
{
// Unselect the currently selected
if (currentSelectedItemContainer is not null)
{
Selector.SetIsSelected(currentSelectedItemContainer, false);
}
}
// If the Selector has multiple selected items and an item was clicked without the modifier key pressed,
// then we need to switch back to single selection mode and only select the currently clicked item.
else
{
// Invalidate tracked multi select range
SelectedRangeStartIndex = -1;
SelectedRangeEndIndex = -1;
if (selector is ListBox listBox)
{
ValidateListBoxSelectionMode(listBox);
listBox.UnselectAll();
}
else if (selector is MultiSelector multiSelector)
{
multiSelector.UnselectAll();
}
else
{
IList selectedItems = GetSelectedItems(selector);
foreach (object item in selectedItems)
{
DependencyObject itemContainer = selector.ItemContainerGenerator.ContainerFromItem(item);
Selector.SetIsSelected(itemContainer, false);
}
selectedItems.Clear();
}
}
// Execute single selection
Selector.SetIsSelected(itemContainerToSelect, true);
(itemContainerToSelect as UIElement)?.Focus();
if (selector is not MultiSelector and not ListBox)
{
IList selectedItems = GetSelectedItems(selector);
selectedItems.Clear();
selectedItems.Add(selector.SelectedItem);
}
// Track index in case the next click enabled select range (press Shift while click)
int clickedContainerIndex = selector.ItemContainerGenerator.IndexFromContainer(itemContainerToSelect);
SelectedRangeStartIndex = clickedContainerIndex;
return;
}
private static void ValidateListBoxSelectionMode(ListBox listBox)
{
if (listBox.SelectionMode is not SelectionMode.Extended)
{
// In case the mode was overriden externally, force it back
// but store the changed value to allow roll back when the behavior gets disabled.
SetOriginalSelectionModeBackup(listBox, listBox.SelectionMode);
listBox.SelectionMode = SelectionMode.Extended;
}
}
private static void OnPreviewLeftMouseButtonDown(object sender, MouseButtonEventArgs e)
=> e.Handled = true;
}
除了@BionicCode 的代码:代码不负责DoubleClick
。 我可以添加该代码来解决它:(BionicCode,如果您可以为未来的访问者编辑答案,谢谢)。
- 在类中添加一个静态变量:
public static DateTime lastClickDateTime = DateTime.Now;
然后在SingleSelectItem
函数的最开始:
TimeSpan interval = DateTime.Now - lastClickDateTime;
if(interval.TotalMilliseconds<500)
{
if(selector is ListBox listBox)
{
MouseButtonEventArgs doubleClickEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, (int)DateTime.Now.Ticks, MouseButton.Left);
doubleClickEvent.RoutedEvent = Control.MouseDoubleClickEvent;
doubleClickEvent.Source = selector;
selector.RaiseEvent(doubleClickEvent);
return;
}
}
lastClickDateTime = DateTime.Now;
再次感谢你的帮助 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.