我有一種狀況,我但願可以將項目添加到列表中,並在列表中移動項目,這彷佛是使用a的最簡單方法ListBox
。我馬上想到了如何以通用的方式作到這一點,而後,也許,可使用行爲來作到這一點。這彷佛是一個很是有用的想法。我決定以一種簡單的方式爲我正在開發的應用程序作這件事,但我想我會建立一個演示項目來探索這個想法。這是結果。git
該行爲實際上有四個獨立的部分,能夠在一個類中執行不一樣的功能:ide
每一個函數的代碼結構很是類似,只有一些細節不一樣。函數
將要檢查的代碼是Move Up函數的代碼。網站
首先是如下定義DependencyProperty
:ui
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
永遠不會禁用。spa
當此DependencyProperty
更改時,將OnMoveUpItemChanged
執行事件處理程序。此事件處理程序在DependencyProperty
RegisterAttached方法的FrameworkMetadata參數中指定。code
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值。blog
爲的保存代碼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"