2009-07-15 27 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

相关问题