保存之前WPF数据绑定

时间:2020-03-05 18:51:46  来源:igfitidea点击:

在WPF应用程序中,我有许多数据绑定的TextBox。这些绑定的" UpdateSourceTrigger"是" LostFocus"。使用"文件"菜单保存该对象。我的问题是可以在TextBox中输入新值,从File菜单中选择Save,并且永远不会保留新值(在TextBox中可见),因为访问菜单不会从TextBox中移出焦点。我怎样才能解决这个问题?有什么方法可以强制页面中的所有控件进行数据绑定吗?

@palehorse:好点。不幸的是,为了支持我想要的验证类型,我需要使用LostFocus作为UpdateSourceTrigger。

@dmo:我已经想到了。但是,对于一个相对简单的问题而言,这似乎是一个非常微不足道的解决方案。此外,它要求页面上有一些控件,该控件始终可见,以接收焦点。我的应用程序是选项卡式的,因此,没有这样的控件很容易出现。

@Nidonocu:使用菜单不会将焦点从TextBox移开的事实也使我感到困惑。但是,这就是我所看到的行为。下面的简单示例演示了我的问题:

<Window x:Class="WpfApplication2.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ObjectDataProvider x:Key="MyItemProvider" />
    </Window.Resources>
    <DockPanel LastChildFill="True">
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Save" Click="MenuItem_Click" />
            </MenuItem>
        </Menu>
        <StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
            <Label Content="Enter some text and then File > Save:" />
            <TextBox Text="{Binding ValueA}" />
            <TextBox Text="{Binding ValueB}" />
        </StackPanel>
    </DockPanel>
</Window>
using System;
using System.Text;
using System.Windows;
using System.Windows.Data;

namespace WpfApplication2
{
    public partial class Window1 : Window
    {
        public MyItem Item
        {
            get { return (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance as MyItem; }
            set { (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance = value; }
        }

        public Window1()
        {
            InitializeComponent();
            Item = new MyItem();
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(string.Format("At the time of saving, the values in the TextBoxes are:\n'{0}'\nand\n'{1}'", Item.ValueA, Item.ValueB));
        }
    }

    public class MyItem
    {
        public string ValueA { get; set; }
        public string ValueB { get; set; }
    }
}

解决方案

回答

我们是否尝试过将UpdateSourceTrigger设置为PropertyChanged?另外,我们可以调用UpdateSOurce()方法,但这似乎有点过头了,并且违反了TwoWay数据绑定的目的。

回答

我们可以在保存之前将焦点设置在其他位置吗?

我们可以通过在UI元素上调用focus()来实现此目的。

我们可以专注于调用"保存"的任何元素。如果触发器是LostFocus,则必须将焦点移到某个位置。 Save的优点是它不会被修改,并且对用户有意义。

回答

在研究此问题的答案时,我有点困惑我们所看到的行为正在发生,肯定是单击"文件"菜单的行为,或者我们应该取消文本框的焦点并将其设置为菜单吗?

回答

Suppose you have a TextBox in a window, and a ToolBar with a Save button in it. Assume the TextBox’s Text property is bound to a property on a business object, and the binding’s UpdateSourceTrigger property is set to the default value of LostFocus, meaning that the bound value is pushed back to the business object property when the TextBox loses input focus. Also, assume that the ToolBar’s Save button has its Command property set to ApplicationCommands.Save command. 
  
  In that situation, if you edit the TextBox and click the Save button with the mouse, there is a problem. When clicking on a Button in a ToolBar, the TextBox does not lose focus. Since the TextBox’s LostFocus event does not fire, the Text property binding does not update the source property of the business object. 
  
  Obviously you should not validate and save an object if the most recently edited value in the UI has not yet been pushed into the object. This is the exact problem Karl had worked around, by writing code in his window that manually looked for a TextBox with focus and updated the source of the data binding. His solution worked fine, but it got me thinking about a generic solution that would also be useful outside of this particular scenario. Enter CommandGroup…

摘自Josh Smiths CodeProject上有关CommandGroup的文章

回答

这是一个丑陋的hack,但也应该可以

TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox != null)
{
    focusedTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

此代码检查TextBox是否具有焦点...如果找到1,则更新绑定源!

回答

最简单的方法是将焦点设置在某个位置。
我们可以立即将焦点重新设置,但是在任何地方设置焦点都会在任何类型的控件上触发LostFocus-Event并使其更新其内容:

IInputElement x = System.Windows.Input.Keyboard.FocusedElement;
DummyField.Focus();
x.Focus();

另一种方法是获取焦点元素,从焦点元素获取绑定元素,然后手动触发更新。 TextBox和ComboBox的示例(我们需要添加需要支持的任何控件类型):

TextBox t = Keyboard.FocusedElement as TextBox;
if ((t != null) && (t.GetBindingExpression(TextBox.TextProperty) != null))
  t.GetBindingExpression(TextBox.TextProperty).UpdateSource();

ComboBox c = Keyboard.FocusedElement as ComboBox;
if ((c != null) && (c.GetBindingExpression(ComboBox.TextProperty) != null))
  c.GetBindingExpression(ComboBox.TextProperty).UpdateSource();

回答

我发现从菜单的FocusScope中删除范围相关的菜单项会导致文本框正确失去焦点。我不认为这适用于Menu中的所有项目,但肯定可以进行保存或者验证操作。

<Menu FocusManager.IsFocusScope="False" >