2009-07-15 8 views
10

Tôi đã quan sát một số hành vi không mong muốn hoặc ít nhất là không hoàn toàn phù hợp với nhu cầu của tôi về hộp văn bản được liên kết với textproperties khi tôi không thể sử dụng UpdateTrigger = PropertyChanged cho ràng buộc của tôi. Có lẽ nó không phải là một vấn đề với hộp văn bản nhưng sẽ xảy ra với các biên tập viên khác là tốt.Làm thế nào để đạt được tập trung-thiết lập lại để cập nhật BindingSource của TextBox trước khi bất kỳ hành động

Trong ví dụ của tôi (mã nguồn được đính kèm), tôi có một TabControl WPF ràng buộc với một số bộ sưu tập. Trên mỗi tab, bạn có thể chỉnh sửa một mục từ bộ sưu tập, theo nhiều cách khác nhau mà bạn có thể kích hoạt hành động lưu, thao tác này sẽ lưu các chỉnh sửa cho một số mô hình. Các hộp văn bản được liên kết với các thuộc tính của mỗi mục được (theo mục đích) được lưu giữ mặc định cập nhật kích hoạt 'OnFocusLost'. Điều này là do có một số xác thực tốn kém diễn ra khi một giá trị mới được thiết lập.

Bây giờ tôi thấy có ít nhất hai cách để kích hoạt hành động lưu của tôi theo cách như vậy, hộp văn bản được tập trung cuối cùng không cập nhật giá trị bị ràng buộc. 1) Thay đổi mục tab bằng cách nhấp chuột vào tiêu đề của nó và sau đó nhấp vào một số nút lưu. (thay đổi trở lại tab trước đó cho thấy giá trị mới thậm chí bị mất) 2) Kích hoạt lệnh lưu thông qua KeyGesture.

Tôi thiết lập một ứng dụng mẫu thể hiện hành vi. Nhấp vào "Lưu tất cả" sẽ hiển thị tất cả các giá trị của mục, nút lưu khác chỉ hiển thị mục hiện tại.

Q: Điều gì sẽ là cách tốt nhất để đảm bảo rằng tất cả các liên kết tài nguyên của tất cả các hộp văn bản của tôi sẽ được cập nhật trước khi các đối tượng bị ràng buộc được đính kèm? Tốt hơn là nên có một cách duy nhất để nắm bắt tất cả các vật sở hữu, tôi không thích bắt từng sự kiện khác nhau, vì tôi sẽ lo lắng đã quên một số sự kiện. Quan sát sự kiện thay đổi lựa chọn của điều khiển tab chẳng hạn sẽ giải quyết vấn đề 1) nhưng không giải quyết vấn đề 2).

Bây giờ để ví dụ:

XAML đầu tiên:

<Window x:Class="TestOMat.TestWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:TestOMat="clr-namespace:TestOMat" 
Title="TestOMat" x:Name="wnd"> 
<Grid> 
    <Grid.Resources> 
     <DataTemplate x:Key="dtPerson" DataType="{x:Type TestOMat:Person}"> 
      <StackPanel Orientation="Vertical"> 
       <StackPanel.CommandBindings> 
        <CommandBinding Command="Close" Executed="CmdSaveExecuted"/> 
       </StackPanel.CommandBindings> 
       <TextBox Text="{Binding FirstName}"/> 
       <TextBox Text="{Binding LastName}"/> 
       <Button Command="ApplicationCommands.Stop" CommandParameter="{Binding}">Save</Button> 
      </StackPanel> 
     </DataTemplate> 
    </Grid.Resources> 
    <Grid.RowDefinitions> 
     <RowDefinition/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 
    <Grid.CommandBindings> 
     <CommandBinding Command="ApplicationCommands.Stop" Executed="CmdSaveAllExecuted"/> 
    </Grid.CommandBindings> 
    <TabControl ItemsSource="{Binding ElementName=wnd, Path=Persons}" ContentTemplate="{StaticResource dtPerson}" SelectionChanged="TabControl_SelectionChanged"/> 
    <Button Grid.Row="1" Command="ApplicationCommands.Stop">Save All</Button> 
</Grid></Window> 

Và các lớp tương ứng

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows; 
using System.Windows.Controls; 
namespace TestOMat 
{ 
    /// <summary> 
    /// Interaction logic for TestOMat.xaml 
    /// </summary> 
    public partial class TestWindow : Window 
    { 
    public TestWindow() 
    { 
     InitializeComponent(); 
    } 

private List<Person> persons = new List<Person> 
       { 
       new Person {FirstName = "John", LastName = "Smith"}, 
       new Person {FirstName = "Peter", LastName = "Miller"} 
       }; 

public List<Person> Persons 
{ 
    get { return persons; } 
    set { persons = value; } 
} 

private void CmdSaveExecuted(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) 
{ 
    Person p = e.Parameter as Person; 
    if (p != null) 
    { 
    MessageBox.Show(string.Format("FirstName={0}, LastName={1}", p.FirstName, p.LastName)); 
    e.Handled = true; 
    } 
} 

private void CmdSaveAllExecuted(object sender, System.Windows.Input.ExecutedRoutedEventArgs e) 
{ 
    MessageBox.Show(String.Join(Environment.NewLine, Persons.Select(p=>string.Format("FirstName={0}, LastName={1}", p.FirstName, p.LastName)).ToArray())); 
    e.Handled = true; 
} 

private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    Console.WriteLine(String.Format("Selection changed from {0} to {1}", e.RemovedItems, e.AddedItems)); 
    // Doing anything here only avoids loss on selected-tab-change 
} 
    } 
    public class Person 
    { 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    } 
} 
+0

Để thuận tiện, tôi đã sử dụng Stop-Command, s.t. đối với hầu hết người dùng, nhấn [Esc] sẽ kích hoạt hành động. –

Trả lời

3

Có lẽ không tốt khi trả lời các câu hỏi của riêng mình, nhưng tôi nghĩ câu trả lời này phù hợp hơn với câu hỏi so với các câu hỏi khác và do đó đáng để viết. Chắc chắn đây cũng là vì tôi không mô tả vấn đề đủ rõ ràng.

Cuối cùng, cũng giống như một bằng chứng nhanh chóng về khái niệm, tôi đã làm việc xung quanh nó như thế này: Sự kiện LostFocus không bao giờ được kích hoạt trên TextBox, khi tôi chuyển tab. Do đó, liên kết không cập nhật và giá trị đã nhập bị mất, bởi vì việc chuyển đổi trở lại làm cho việc làm mới liên kết từ nguồn của nó. Nhưng những gì là bắn là PreviewLostFocus-tổ chức sự kiện, vì thế tôi nối chức năng nhỏ này, điều đó bằng tay gây nên các bản cập nhật cho nguồn ràng buộc:

private void BeforeFocusLost(object sender, KeyboardFocusChangedEventArgs e) 
{ 
    if (sender is TextBox) { 
    var tb = (TextBox)sender; 

    var bnd = BindingOperations.GetBindingExpression(tb, TextBox.TextProperty); 

    if (bnd != null) { 
     Console.WriteLine(String.Format("Preview Lost Focus: TextBox value {0}/Data value {1} NewFocus will be {2}", tb.Text, bnd.DataItem, e.NewFocus)); 
     bnd.UpdateSource(); 
    } 
    Console.WriteLine(String.Format("Preview Lost Focus Update forced: TextBox value {0}/Data value {1} NewFocus will be {2}", tb.Text, bnd.DataItem, e.NewFocus)); 
    } 
} 

Sản lượng theo chuỗi sự kiện với PreviewLostFocus, LostFocus (cả hai từ TextBox) và SelectionChanged (từ TabControl) sẽ trông như thế này:

Preview Lost Focus: giá trị TextBox giá trị Smith123456/dữ liệu John Smith123 NewFocus sẽ System.Windows.Controls.TabItem Tiêu đề: Peter Miller Nội dung: Peter Miller Bản cập nhật mất tập trung xem trước bị buộc: Giá trị TextBox Smith123456/Giá trị dữ liệu John Smith12 3456 NewFocus sẽ là System.Windows.Controls.TabItem Tiêu đề: Peter Miller Nội dung: Peter Miller Lựa chọn thay đổi từ System.Object [] thành System.Object [] Xem trước Mất tiêu điểm: Giá trị TextBox Miller/Dữ liệu giá trị Peter Miller NewFocus sẽ là System.Windows.Controls.TextBox: Peter Preview Lost Focus cập nhật buộc: giá trị TextBox giá trị Miller/dữ liệu Peter Miller NewFocus sẽ System.Windows.Controls.TextBox: Peter Lost Focus có giá trị Miller

Chúng ta thấy rằng LostFocus chỉ xảy ra ở cuối, nhưng không phải trước khi thay đổi TabItem. Tuy nhiên tôi nghĩ rằng điều này là lạ, có thể là một lỗi trong WPF hoặc trong các mẫu kiểm soát tiêu chuẩn. Cảm ơn tất cả các đề xuất của bạn, xin lỗi tôi không thể thực sự ký họ là câu trả lời, vì họ không giải quyết được việc mất các mục nhập trên tab-thay đổi.

+0

Đó là chắc chắn OK để trả lời câu hỏi của riêng bạn. –

+1

Câu trả lời này giúp tôi đi đúng hướng cho vấn đề cụ thể của tôi; Tôi đang theo dõi mức tăng/giảm tiêu điểm bàn phím trên các điều khiển chỉnh sửa văn bản trong cấu trúc phần tử định hướng/dữ liệu. Khi bối cảnh bị ràng buộc thay đổi (làm mất hiệu lực cấu trúc), hộp văn bản có tiêu điểm có thể được xóa mà không cần bất kỳ thông báo mất tập trung nào. Tôi đã thử câu trả lời được gợi ý (bằng cách gắn kết sự kiện PreviewKeyboardFocusLoss trên điều khiển) nhưng nó thậm chí không có cơ hội để bắn. Tôi định cư trên hooking các Unloaded sự kiện thay vì như điều này gây nên sự thay đổi cấu trúc cho tôi một cơ hội để dọn dẹp. Cảm ơn. :) – Gaspode

0

Có lẽ thiết lập UpdateSourceTrigger tài sản của các ràng buộc:

<TextBox Text="{Binding FirstName, UpdateSourceTrigger=Explicit}"/> 

Tôi không chắc chắn đây là những gì bạn đang tìm kiếm.

+0

Tôi xin lỗi, nó không phải là. Nó có thể hoạt động, nhưng nếu tôi sử dụng cập nhật rõ ràng, tôi sẽ cần phải cập nhật tất cả các ràng buộc của mình theo cách thủ công trong mã sau, ví dụ: trước khi lưu. Vì vậy, nếu có nhiều hơn một hành động có thể, mỗi hành động phải quan tâm đến việc cập nhật tất cả các ràng buộc đầu tiên, đó là những gì tôi muốn tránh. (Lý do cho điều này: Tôi muốn tách rời đối tượng bị ràng buộc khỏi khung nhìn theo mẫu MV-VM, vì vậy lệnh Save-Command không nên "biết" chế độ xem và không truy cập vào các thuộc tính của nó) Dù sao , cảm ơn câu trả lời của bạn, tôi đã từ bỏ việc nhận bất kỳ phản hồi nào. –

1

Bạn có thể viết kiểu nhắm mục tiêu tất cả các hộp văn bản, trong đó bạn sẽ có EventSetter trên các sự kiện GotFocus hoặc GotKeyboardFocus và các sự kiện LostFocus bổ sung. Trong trình xử lý được kết hợp với các sự kiện GotFocus, bạn sẽ đặt biến boolean "canSave" thành false, trong trình xử lý LostFocus bạn sẽ đặt trở lại thành true. Tất cả những gì bạn phải làm sau đó là kiểm tra trước khi lưu nếu biến của bạn cũng cho phép bạn. Nếu không, bạn có thể thông báo cho người dùng hoặc chỉ cần chuyển tiêu điểm từ hộp văn bản sang hộp văn bản khác. Bằng cách đó, kích hoạt cập nhật của liên kết cho hộp văn bản hiện đang được chỉnh sửa sẽ kích hoạt một cách thích hợp khi tiêu điểm của nó bị mất.

+0

Thật không may, khi chuyển đổi tab đã chọn, các TextBox trong tab cũ sẽ không bao giờ có được sự kiện LostFocus-Sự kiện, mà là một lỗi trong WPF tôi sẽ nói. Đó là lý do tại sao UpdateTrigger (là LostFocus) không bao giờ được kích hoạt tự động. Vì vậy, giải pháp của bạn sẽ không giúp tôi trong trường hợp này. Vẫn +1 để hướng dẫn tôi kiểm tra tất cả các sự kiện liên quan đến tiêu điểm, tôi đã tìm ra cách nhanh chóng để thực hiện. –