程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> [WPF]如何正確地用代碼設置ListBox的當前選中項

[WPF]如何正確地用代碼設置ListBox的當前選中項

編輯:關於.NET

有人可能會說這有什麼好寫的。不就是一行代碼就能搞定的嗎?而且為什麼需要用代碼設置SelectedItem呢?用戶所點的Item不就自動是SelectedItem嗎?在這裡將要討論我們的,就是ListBox自己沒有能自己把SelectedItem設置正確的情況。本來想當作一個WPF Bug清單的一篇文章的,但是又感覺也許就是有這樣變態的需求呢。

我們用一個非常簡單的代碼的XAML就可以重現這個問題。

Demo Window

1<Window x:Class="SelectListBoxItem.DemoWindow"
2  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4  xmlns:s="clr-namespace:System;assembly=mscorlib"  
5  Title="ListBox Selection Problem"
6  SizeToContent="Height"
7  Width="300">
8  <ListBox>
9    <ListBox.ItemTemplate>
10      <DataTemplate>
11        <TextBox Text="{Binding .}" Width="100"/>
12      </DataTemplate>
13    </ListBox.ItemTemplate>
14    <s:String>a</s:String>
15    <s:String>b</s:String>
16    <s:String>c</s:String>
17  </ListBox>
18</Window>

運行的效果如下。

圖1. TextBox得到焦點

其實這就是個問題,一個ListBoxItem已經被MouseDown了,可是沒有被選中。MouseDown已經被TextBox吃了。結果有可能出現下面的狀況。

圖2. 焦點與選中項不一致

這個問題在WPF裡的其它控件也有,在智者千慮的【WPF】如何讓TreeView實現右鍵選中的功能裡就描述了TreeView上的相似問題。感覺很惡心。

一開始使用的是PreviewMouseDown解決,在MouseDown的時候,通過DataContext也好,通過FindAncestor也好,反正是在獲得焦點的同時選擇上了。

但是隨著項目的進行,這種方法造成DataBinding的Validation出現了問題。Validation應該是在LostFocus是對DataContext進行驗證;但是使用PreviewMouseDown更改選中項,這個LostFocus就是在別的項被選中之後發現,結果就是用一個無關的數據在新的DataContext上進行驗證。

這個問題又普遍存在於項目各個DataBinding中,分別修改肯定是不行的。只能是不用PreviewMouseDown。用GotFocus,用它的Item的GotFocus來設置選中項。

為了在現有系統中方便應用,使用了AttachedProperty來實現這個功能。代碼如下:

ListBoxService

1using System.Diagnostics;
2using System.Windows;
3using System.Windows.Controls;
4
5namespace SelectListBoxItem
6{
7  /**//// <summary>
8  ///
9  /// </summary>
10  public class ListBoxService
11  {
12    AutoSelect Property#region AutoSelect Property
13
14    public static readonly DependencyProperty AutoSelectProperty = DependencyProperty.RegisterAttached("AutoSelect", typeof(bool), typeof(ListBoxService), new PropertyMetadata(OnAutoSelectPropertyChanged));
15
16    public static bool GetAutoSelect(DependencyObject element)
17    {
18      if (element == null)
19        return false;
20
21      return (bool)element.GetValue(AutoSelectProperty);
22    }
23
24    public static void SetAutoSelect(DependencyObject element, bool value)
25    {
26      if (element == null)
27        return;
28
29      element.SetValue(AutoSelectProperty, value);
30    }
31
32    #endregion
33
34    private static void OnAutoSelectPropertyChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
35    {
36      if (!(element is UIElement))
37        return;
38
39      if ((bool)e.NewValue)
40        (element as UIElement).GotFocus += new RoutedEventHandler(OnElementGotFocus);
41      else
42        (element as UIElement).GotFocus -= new RoutedEventHandler(OnElementGotFocus);
43    }
44
45    private static void OnElementGotFocus(object sender, RoutedEventArgs e)
46    {
47      Debug.Assert(e.OriginalSource is DependencyObject);
48
49      ListBoxItem item = (e.OriginalSource as DependencyObject).FindAncestor<ListBoxItem>();
50      if (item != null)
51        item.IsSelected = true;
52      else
53        Debug.WriteLine(string.Format("Cannot find ListBoxItem from {0}", sender));
54    }
55  }
56}
57

其中FindAncestor是自己定義的一個方法,因為單純地使用VisualTreeHelper是不足以在所有情況下找到Parent的。代碼可參見源代碼。

寫好了怎麼用呢?我們說了,要以對現有代碼最小的變動實現這個功能。可能有人已經想到了,用Style,那個Window的代碼根本不用動。只要在App.xaml裡加上一個Resource就OK了。代碼如下,簡單吧。

App Resource

1<Application x:Class="SelectListBoxItem.App"
2  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4  xmlns:l="clr-namespace:SelectListBoxItem"
5  StartupUri="DemoWindow.xaml">
6  <Application.Resources>
7    <Style TargetType="{x:Type ListBox}">
8      <Setter Property="l:ListBoxService.AutoSelect" Value="True"/>
9    </Style>
10  </Application.Resources>
11</Application>
12

到此,ListBox的行為算是正常些了。正常的運行截圖就不發了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved