介紹
我有一種情況,我希望能夠將項目添加到列表中,並在列表中移動項目,這似乎是使用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="