2012-05-04 31 views
31

Chúng tôi muốn đặt SelectedItem của một ListBox theo chương trình và muốn mục đó sau đó có tiêu điểm sao cho các phím mũi tên hoạt động liên quan đến mục đã chọn đó. Có vẻ đơn giản là đủ.Làm thế nào để bạn lập trình tập trung vào SelectedItem trong một ListBox WPF đã có tiêu điểm?

Vấn đề tuy nhiên là nếu ListBox đã có tập trung bàn phím khi thiết SelectedItem lập trình, trong khi nó cập nhật đúng IsSelected tài sản trên ListBoxItem, nó không bộ bàn phím tập trung vào nó, và do đó, các phím mũi tên di chuyển tương đối so với mục được tập trung trước đó trong danh sách và không phải mục được chọn mới như mong đợi.

Điều này rất khó hiểu đối với người dùng vì nó làm cho lựa chọn xuất hiện để nhảy xung quanh khi sử dụng bàn phím khi nó quay trở lại vị trí trước khi lựa chọn có lập trình diễn ra.

Lưu ý: Như tôi đã nói, điều này chỉ xảy ra nếu bạn lập trình đặt thuộc tính SelectedItem trên ListBox đã có tiêu điểm bàn phím. Nếu nó không (hoặc nếu nó không nhưng bạn để lại, sau đó quay lại ngay), khi tiêu điểm bàn phím trở về ListBox, mục chính xác bây giờ sẽ có tiêu điểm bàn phím như mong đợi.

Dưới đây là một số mã mẫu cho thấy sự cố này. Để giới thiệu điều này, hãy chạy mã, sử dụng chuột để chọn 'Bảy' trong danh sách (do đó đặt trọng tâm vào số ListBox), sau đó nhấp vào nút 'Kiểm tra'. Cuối cùng, chạm vào phím 'Alt' trên bàn phím của bạn để hiển thị tiêu điểm rect. Bạn sẽ thấy nó vẫn còn thực sự trên 'Bảy' và nếu bạn sử dụng mũi tên lên và xuống, chúng liên quan đến hàng đó, không phải là 'Bốn' như người dùng mong đợi.

Lưu ý rằng tôi có Focusable được đặt thành false trên nút không làm mất hộp danh sách tiêu điểm khi nhấn. Nếu tôi không có điều này, các ListBox sẽ mất tập trung khi bạn nhấp vào nút, và do đó, khi tập trung quay trở lại ListBox, nó sẽ được vào đúng mục.

XAML file:

<Window x:Class="Test.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="525" Height="350" WindowStartupLocation="CenterScreen" 
    Title="MainWindow" x:Name="Root"> 

    <DockPanel> 

     <Button Content="Test" 
      DockPanel.Dock="Bottom" 
      HorizontalAlignment="Left" 
      Focusable="False" 
      Click="Button_Click" /> 

     <ListBox x:Name="MainListBox" /> 

    </DockPanel> 

</Window> 

Mã-đằng sau:

using System.Collections.ObjectModel; 
using System.Windows; 

namespace Test 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      MainListBox.ItemsSource = new string[]{ 
       "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight" 
      }; 

     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      MainListBox.SelectedItem = MainListBox.Items[3]; 
     } 

    } 

} 

Lưu ý: Một số người đã đề nghị sử dụng IsSynchronizedWithCurrentItem, nhưng tài sản đó đồng bộ hóa các SelectedItem của ListBox với Current tài sản của liên lượt xem. Nó không liên quan đến tập trung vì vấn đề này vẫn còn tồn tại.

của chúng tôi làm việc xung quanh là tạm thời đặt trọng tâm ở một nơi khác, sau đó đặt mục đã chọn, sau đó đặt trọng tâm trở lại ListBox nhưng điều này có tác dụng undesireable người trong chúng ta phải thực hiện của chúng tôi ViewModel nhận thức được ListBox chính nó, sau đó thực hiện logic tùy thuộc vào việc nó có tập trung, v.v. (nghĩa là bạn không muốn chỉ đơn giản nói 'Tập trung ở nơi khác rồi quay lại đây, nếu' ở đây 'không có tiêu điểm như bạn đã đánh cắp nó từ một nơi khác.) Thêm vào đó, bạn không thể đơn giản xử lý điều này thông qua các ràng buộc khai báo. Không cần phải nói điều này là xấu.

Sau đó, một lần nữa, tàu 'xấu', do đó, có điều đó.

Trả lời

47

Đó là một vài dòng mã. Nếu bạn không muốn nó ở phía sau mã, tôi chắc chắn nó có thể được đóng gói trong một hành vi đính kèm.

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    MainListBox.SelectedItem = MainListBox.Items[3]; 
    MainListBox.UpdateLayout(); // Pre-generates item containers 

    var listBoxItem = (ListBoxItem) MainListBox 
     .ItemContainerGenerator 
     .ContainerFromItem(MainListBox.SelectedItem); 

    listBoxItem.Focus(); 
} 
+2

ContainerFromItem sẽ trả về Null nếu không có vùng chứa nào chưa được tạo cho nó, đó là trường hợp của một danh sách ảo hóa và mục đó không có màn hình. Thêm vào đó nếu bạn cố gắng thiết lập giá trị từ một ràng buộc nó bị phá vỡ khi bạn không có quyền truy cập vào ListBox, cũng không phải là bạn. (Tiếp theo bên dưới ...) – MarqueIV

+0

Ngay cả hành vi đính kèm cũng đặt ra các vấn đề vì bạn không muốn điều khiển nhắm mắt tập trung vào ListBoxItem trừ khi a) điều khiển đã có tiêu điểm (dễ kiểm tra) và b) thay đổi đến Nếu không, bạn có thể vô tình đánh cắp lấy nét từ một điều khiển khác (trường hợp a) hoặc làm lộn xộn tiêu điểm trong điều khiển hiện tại (trường hợp b) khi ở chế độ đa lựa chọn và bỏ chọn một hàng, mà thông thường nên giữ lại tiêu điểm bàn phím, nhưng thay vào đó sẽ được đặt thành SelectedItem mới, nếu có, gây ra các hành vi kỳ lạ. – MarqueIV

+0

Trên thực tế, suy nghĩ thứ hai, tôi nghĩ rằng tôi có công việc xung quanh cho trường hợp 'B' ở trên. Bạn tạo hành vi đính kèm như bạn đã nói, nhưng bạn không đặt nó trực tiếp trong XAML. Thay vào đó bạn tạo một thuộc tính trên ViewModel của bạn và ràng buộc hành vi đó. Bằng cách đó bạn không cần phải biết (hoặc quan tâm) ai đang lắng nghe. Sau đó, ngay trước khi bạn đặt mục đã chọn từ mã phía sau, bạn bật hành vi, chọn mục, sau đó tắt lại hành vi. Địa chỉ này 'B' ở trên. (Bạn vẫn cần 'A' tất nhiên.) Bình chọn của bạn là câu trả lời kể từ khi, mặc dù không hoàn thành, đã dẫn tôi xuống con đường này. – MarqueIV

2

Có thể có hành vi được đính kèm?Một cái gì đó như

public static DependencyProperty FocusWhenSelectedProperty = DependencyProperty.RegisterAttached(
      "FocusWhenSelected", 
      typeof(bool), 
      typeof(FocusWhenSelectedBehavior), 
      new PropertyMetadata(false, new PropertyChangedCallback(OnFocusWhenSelectedChanged))); 

private static void OnFocusWhenSelectedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
    { 
     var i = (ListBoxItem)obj; 
     if ((bool)args.NewValue) 
      i.Selected += i_Selected; 
     else 
      i.Selected -= i_Selected; 
    } 

static void i_Selected(object sender, RoutedEventArgs e) 
{ 
    ((ListBoxItem)sender).Focus(); 
} 

và trong XAML

 <Style TargetType="ListBoxItem"> 
      <Setter Property="local:FocusWhenSelectedBehavior.FocusWhenSelected" Value="True"/> 
     </Style> 
+1

Đây là nơi tôi đang trên đường ban đầu, nhưng vấn đề là những mặt hàng don chưa tồn tại nếu bạn có một danh sách ảo hóa, do đó hành vi đính kèm chưa được kết nối để trả lời. Bạn vẫn phải bằng cách nào đó đầu tiên làm phương pháp ScrollIntoView. Thứ hai, nếu bạn nghiêm chỉnh trả lời IsSelected, mọi thứ có thể đi một chút điên rồ khi ở chế độ đa lựa chọn, nhưng sau đó một lần nữa, có thể được coi là hành vi mong muốn. Ảo hóa (được bật theo mặc định) là kẻ giết người thực sự. – MarqueIV

0

Trong XAML của bạn, bạn cố gắng này và đã không làm việc?

<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=YourCollectionView}" SelectedItem="{Binding SelectedItem}"></ListBox> 

SelectedItem tài sản:

private YourObject _SelectedItem; 
    public YourObject SelectedItem 
    { 
     get 
     { 
      return _SelectedItem; 
     } 
     set 
     { 
      if (_SelectedItem == value) 
       return; 

      _SelectedItem = value; 

      OnPropertyChanged("SelectedItem"); 
     } 
    } 

Bây giờ trong mã của bạn, bạn có thể làm:

SelectedItem = theItemYouWant; 

Đối với tôi cách tiếp cận này làm việc luôn.

+0

Tôi sẽ gọi cho bạn về điều đó. Bạn đã thử chưa? Tải hộp danh sách của bạn với 100 mục. Nhấp vào mục thứ 50 bằng chuột. Sau đó, thêm một nút trên biểu mẫu của bạn lập trình 'SelectedItem' thành mục thứ 10. Lựa chọn không thay đổi, nhưng a) mục đã chọn không cuộn vào dạng xem vì b) nó không được tập trung. Bạn có thể thấy điều đó bằng cách nhấn vào phím 'Alt' và bạn sẽ vẫn thấy mục bạn nhấp vẫn có tiêu điểm bàn phím. – MarqueIV

+0

Nhân tiện ... chỉ để chắc chắn, bạn phải chọn vật lý trước tiên bằng cách sử dụng chuột hoặc bàn phím. – MarqueIV

+0

@MarquelV Tôi có thể bối rối về những gì bạn muốn. Tôi nghĩ bạn muốn thiết lập chương trình hàng đã chọn. Hàng được chọn là hàng được tập trung. Bạn phải sử dụng ScrollIntoView để di chuyển đến đó. Và có, cách tiếp cận của tôi không hoạt động cho nhiều lựa chọn. – Dummy01

0

đầu tiên) Bạn phải tìm các mục được chọn trong listbox với ListBox.Items.IndexOf().
Thứ hai) Bây giờ, hãy thêm các mục với ListBox.SelectedItems.Add().

Đây là mã của tôi:

DataRow[] drWidgetItem = dtItemPrice.Select(widgetItemsFilter); 
lbxWidgetItem.SelectedItems.Clear(); foreach(DataRow drvItem in 
drWidgetItem) 
lbxWidgetItem.SelectedItems.Add(lbxWidgetItem.Items[dtItemPrice.Rows.IndexOf(drvItem)]); 

Nếu bạn muốn chọn một mục trong ListBox bạn có thể sử dụng theo cách này:
ListBox.SelectedItem = (ListBoxItem của bạn);

Nếu bạn muốn chọn một số mục trong ListBox bạn phải sử dụng theo cách này:
ListBox.SelectedItems.Add (ListBoxItem của bạn);

+0

Tôi không chắc chắn 100%, nhưng tôi nghĩ bạn có thể đã hiểu sai câu hỏi của tôi. Lựa chọn không phải là vấn đề. Tập trung là. Mã của bạn không chọn các mục, nhưng nó không đặt tiêu điểm, đó là những gì tôi đang cố gắng giải quyết. – MarqueIV

+0

Xin lỗi! Vì vậy, tôi nghĩ rằng nếu bạn tập trung kiểm soát của bạn sau đó mục của bạn phải tập trung. (xin lỗi vì tiếng Anh xấu của tôi) –

+0

Nhưng nếu điều khiển đã có tiêu điểm, bạn không thể thiết lập lại nó. Bạn sẽ phải tập trung cái gì khác trước. – MarqueIV

0

Bạn chỉ cần sử dụng ListBox.SelectedItem và sau đó sử dụng ListBox.ScrollIntoView (listBox.SelectedItem)

Ví dụ mã:

 private void textBox2_TextChanged(object sender, TextChangedEventArgs e) 
    { 

     var comparision = StringComparison.InvariantCultureIgnoreCase; 
     string myString = textBox2.Text; 
     List<dynamic> index = listBox.Items.SourceCollection.OfType<dynamic>().Where(x=>x.Nombre.StartsWith(myString,comparision)).ToList(); 
     if (index.Count > 0) { 
     listBox.SelectedItem= index.First(); 


      listBox.ScrollIntoView(listBox.SelectedItem); 


     } 

    } 
Các vấn đề liên quan