介绍
我有一种情况,我希望能够将项目添加到列表中,并在列表中移动项目,这似乎是使用a的最简单方法ListBox
。我立刻想到了如何以通用的方式做到这一点,然后,也许,可以使用行为来做到这一点。这似乎是一个非常有用的想法。我决定以一种简单的方式为我正在开发的应用程序做这件事,但我想我会创建一个演示项目来探索这个想法。这是结果。
概观
该行为实际上有四个独立的部分,可以在一个类中执行不同的功能:
添加项目
将所选项目向上移动一个位置
将所选项目向下移动一个位置
删除所选项目。
每个函数的代码结构非常相似,只有一些细节不同。
将要检查的代码是Move Up函数的代码。
首先是以下定义DependencyProperty
:
隐藏 复制代码
public static readonly DependencyProperty MoveItemUpProperty = DependencyProperty.RegisterAttached("MoveItemUp", typeof(Selector), typeof(ListHelperBehavior), new PropertyMetadata(null, OnMoveItemUpChanged));public static Selector GetMoveItemUp(UIElement uiElement) { return (Selector)uiElement.GetValue(MoveItemUpProperty); }public static void SetMoveItemUp(UIElement uiElement, Selector value) { uiElement.SetValue(MoveItemUpProperty, value); }
这用于为包含列表的Selector
(或ListBox
)控件提供绑定。它用于Button
执行动作,在这种情况下是将所选项目向上移动一个位置。对于这个动作的代码需要有机会获得ItemsSource
和SelectedIndex
的Selector
控制,首先要真正能够做到移动,第二知道要移动的项目。
对于所有操作,此代码几乎相同,只是Add Item不需要监视SelectionChanged
事件Selector
,并且Button
永远不会禁用。
当此DependencyProperty
更改时,将OnMoveUpItemChanged
执行事件处理程序。此事件处理程序在DependencyProperty
RegisterAttached方法的FrameworkMetadata参数中指定。
隐藏 复制代码
private static void OnMoveItemUpChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (e.OldValue is Selector Selector1) { Selector1.SelectionChanged -= SetMoveItemUpButtonIsEnabled; } if (e.NewValue is Selector Selector) { var Button = CheckForButtonBase(d); Button.Click -= MoveItemUpEvent; Button.Click += MoveItemUpEvent; Selector.SetValue(MoveUpButton, Button); Selector.SelectionChanged += SetMoveItemUpButtonIsEnabled; SetMoveItemUpButtonIsEnabled(Selector, null); } }
此代码将事件处理程序附加到Button
Click事件和Selector
SelectionChanged
事件。为了确保Button
在订阅事件之前没有双重订阅Click事件,并且删除SelectionChanged
旧事件的事件处理程序Selector
(如果存在)。此外,Button
它保存在附件DependencyProperty
中,Selector
以便可以找到它以供SelectionChanged
事件处理程序使用。最后,Button
通过使用SelectionChanged
事件处理程序调整IsEnabled值。
为的保存代码Button
在Selector
被下面的私人DependencyProperty
从而使Button
被启用和禁用,可以发现:
隐藏 复制代码
private static readonly DependencyProperty MoveUpButton = DependencyProperty.RegisterAttached("MoveUpButton", typeof(ButtonBase), typeof(ListHelperBehavior), new PropertyMetadata(null));
Add Item代码不需要监视SelectionChanged事件,因为Button
从不禁用它。
的Click事件Button
的下移功能如下:
隐藏 复制代码
private static void MoveItemUpEvent(object sender, RoutedEventArgs e) { Debug.Assert(sender is ButtonBase); var Button = (ButtonBase)sender; var Selector = GetMoveItemUp(Button); var IList = CheckForIList(Selector); var itemNumber = Selector.SelectedIndex; var item = IList[itemNumber]; IList.RemoveAt(itemNumber); var type = IList.GetType().GetGenericArguments().Single(); var castInstance = Convert.ChangeType(item, type); IList.Insert(itemNumber - 1, castInstance); if (itemNumber == 1) Button.IsEnabled = false; Selector.SelectedIndex = itemNumber - 1; }
sender参数必须强制转换为ButtonBase类型,然后用于获取Selector
作为ButtonBase中附加属性保存的控件的值。然后使用它来获取IList
绑定到Selector
ItemsSource
DependencyProperty
的SelectedItem
值和值Selector
。IList
然后复制所选项目,转换为正确的类型(使用Type类的Reflection GetGenericArgument方法获取类型,然后使用Convert.ChangeType方法将其强制转换),然后从IList
(RemoveAt方法)中删除IList
)。然后使用该Selector
Insert
方法插入删除的项目。
接下来检查是否现在是第一个项目,禁用Button
它是否为,并且Selector
SelectedIndex
设置为仍然指向同一个项目。
该移码几乎是相同的,则删除要简单得多,因为它没有保存已删除的项目,然后将其放回IList
。
最后,有适当的代码启用或禁用Button
取决于是否存在SelectedItem
,SelectedItem
是第一个(用于上移)或最后一个项目IList
(用于下移)。这是SelectedItem
在Selector
触发事件时调用的事件处理程序:
隐藏 复制代码
private static void SetMoveItemUpButtonIsEnabled(object sender, RoutedEventArgs e) { <code> Debug.Assert(sender is Selector); var Selector = (Selector)sender; var IList = CheckForIList(Selector); var itemNumber = Selector.SelectedIndex; var Button = (ButtonBase) Selector.GetValue(MoveUpButton); Button.IsEnabled = (itemNumber >= 1 && itemNumber < IList.Count); }</code>
对于这种需要IList
绑定到ItemsSource
的SelectedIndex
,并需要得到Button
保存为一个附加属性在此功能Selector
。对于Remove函数,只需要知道if SelectedIndex
是否等于-1,这样简单得多。
使用行为
要使用此行为,只需要一个从Selector
控件派生的列表控件,Name
为此控件关联一个值,并Button
为每个应该实现的函数定义一个网站源码。在每一个Button
XAML只包括ListHelperBahavior
与DependencyProperty
它有关联Binding
的Selector
:
隐藏 收缩 复制代码
<Grid Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ListBox Name="TheList" ItemsSource="{Binding List}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="30"/> <ColumnDefinition Width="200"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding ItemNumber}"/> <TextBlock Grid.Column="1" Text="{Binding TimeCreated}"/> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <StackPanel Grid.Row="2" Margin="-5 5" Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="Add" Width="70" Margin="5" local:ListHelperBehavior.AddToList="{Binding ElementName=TheList}"/> <Button Content="Remove" Width="70" Margin="5" local:ListHelperBehavior.RemoveFromList="{Binding ElementName=TheList}"/> <Button Content="Move Up" Width="70" Margin="5" local:ListHelperBehavior.MoveItemUp="{Binding ElementName=TheList}"/> <Button Content="Move Down" Width="70" Margin="5" local:ListHelperBehavior.MoveItemDown="{Binding ElementName=TheList}"/> </StackPanel>
问题
此行为存在一些限制,其中一些可以使用其他代码进行处理。
其中一个问题是行为预期绑定到该类型Selector
的类型的IList
,这意味着这两个List
和ObservableCollection
可使用,但Array
Type
不能。这可以编码,但需要Array
每次重新创建。
另一个限制是Add只有Type
在它IList
是一个类时才有效,并且有一个默认的构造函数。
当然另一个限制是它只处理从控件派生的Selector
控件。
结论
这是一个非常好的小行为,因为它允许更改列表的顺序,并通过仅将行为添加Button
到实现该功能的每个项目来添加或删除项目。在ViewModel中无需任何操作即可提供此功能。
作者:九天飞翼
共同学习,写下你的评论
评论加载中...
作者其他优质文章