2009-07-15 29 views
10

我觀察到一些意想不到的或至少不,完美匹配 - 我 - 的需求勢必textproperties當我不能使用使用文本框的行爲的任何行動UpdateTrigger =的PropertyChanged之前更新的TextBox的BindingSource爲了我的約束。可能它不是文本框的問題,但也會出現在其他編輯器中。如何實現重點復位

在我的例子(附源代碼),我有一個WPF的TabControl綁定到一些集合。 在每個選項卡上,您都可以通過各種方式編輯集合中的項目,從而觸發保存操作,並將編輯保存到某個模型中。 綁定到每個項目屬性的文本框(有意)保持爲默認的更新觸發器'OnFocusLost'。這是因爲在設置新值時會發生一些昂貴的驗證。

現在我發現至少有兩種方法可以觸發我的拯救行動以這樣的方式,即最後的焦點文本框不更新綁定值。 1)通過鼠標點擊其標題,然後點擊一些保存按鈕來更改標籤項。 (更改回上一個標籤顯示新值甚至丟失) 2)通過KeyGesture觸發保存命令。

我設置了一個演示行爲的示例應用程序。點擊「全部保存」將顯示所有項目值,另一個保存按鈕僅顯示當前項目。

問:什麼是確保被comitted綁定的對象之前,我所有的文本框的所有bindingsources將被更新的最佳方式? 最好應該有一種方法捕捉所有可能性,我不喜歡以不同方式捕捉每個事件,因爲我擔心已經忘記了一些事件。 觀察選項卡控件的選擇更改事件,例如將解決問題1),但不解決問題2)。

我們的例子:

XAML第一:

<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> 

而且相應的類

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

爲了方便起見,我使用了Stop-Command,s.t.對於大多數用戶按[Esc]應觸發該操作。 – 2009-07-15 20:00:31

回答

3

也許這不是很好回答自己的問題,但我想這個答案是更適合比其他的問題,因此值得寫。這當然也是因爲我沒有足夠清楚地描述問題。

最後,就像一個概念的快速證明一樣,我這樣解決它: 當我切換選項卡時,LostFocus事件永遠不會觸發TextBox。因此,綁定不會更新,並且輸入的值會丟失,因爲切回會使綁定從其源中刷新。 但什麼是燒製是PreviewLostFocus-事件,因此我在這個微小的功能鉤住,即手動觸發更新綁定源:

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)); 
    } 
} 

輸出根據與PreviewLostFocus,引發LostFocus事件鏈(兩者都來自文本框)和的SelectionChanged(從TabControl的)看起來就像這樣:

預覽失去焦點:文本框的值Smith123456 /數據值約翰Smith123則newFocus將System.Windows.Controls.TabItem標題:彼得·米勒內容:彼得·米勒 預覽Lost Focus更新強制:TextBox值Smith123456 /數據值John Smith12 3456 NewFocus將System.Windows.Controls.TabItem標題:Peter Miller內容:Peter Miller 選擇已從System.Object []更改爲System.Object [] Preview Lost Focus:TextBox值Miller/Data value Peter Miller NewFocus will be System.Windows.Controls.TextBox:彼得 預覽失去焦點更新強制:文本框的值米勒/數據值彼得·米勒則newFocus將System.Windows.Controls.TextBox:彼得 失去其價值焦點米勒

我們看到LostFocus只發生在最後,但不是在改變TabItem之前。不過我認爲這很奇怪,可能是WPF或標準控件模板中的一個錯誤。 非常感謝大家的建議,對不起,我無法真正簽署他們的答案,因爲他們沒有解決tab-change上條目的丟失問題。

+0

回答你自己的問題肯定是可以的。 – 2009-09-17 10:10:11

0

也許設置綁定的UpdateSourceTrigger屬性:

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

我不確定這是你要找的。

+0

我很抱歉,事實並非如此。它可能會工作,但如果我使用明確的更新,我將需要在代碼隱藏中手動更新所有我的綁定,例如,保存之前。 因此,如果有多個可能的動作,每個動作必須首先更新所有綁定,這是我想避免的。 (原因是:我想根據MV-VM模式將綁定對象從視圖中分離出來,所以Save-Command根本不應該「知道」該視圖,並且不訪問綁定到它的屬性) Anyway ,謝謝你的回答,我已經放棄收到任何反饋。 – 2009-09-14 11:32:29

1

你可以寫一個風格定位的所有文本框,在其中您會對在GotFocus或GotKeyboardFocus活動的EventSetter和互補引發LostFocus事件。在與GotFocus事件關聯的處理程序中,您可以將「canSave」布爾變量設置爲false,在LostFocus處理程序中,您將設置回true。你所要做的就是在保存前檢查你的變量是否允許你。如果沒有,您可以通知用戶,或者簡單地將焦點從文本框切換到其他內容。這樣,當前編輯的文本框的綁定更新觸發器將在其焦點丟失時正確觸發。

+0

不幸的是,當切換選定的選項卡時,舊選項卡中的TextBox將永遠不會獲得LostFocus-Event,這是WPF中的錯誤,我會說。這就是爲什麼UpdateTrigger(被LostFocus)永遠不會自動觸發的原因。所以你的解決方案在這種情況下不會幫助我。 仍然+1導致我檢查所有焦點相關的事件,我想出了一個quick'n'dirty方法來做到這一點。 – 2009-09-17 09:52:22

相關問題