Я наблюдал неожиданное или, по крайней мере, не совсем точное соответствие моим потребностям текстовых полей, связанных с textproperties, когда я не могу использовать UpdateTrigger = PropertyChanged для своей привязки. Вероятно, это не проблема с текстовым полем, но будет происходить и с другими редакторами.
В моем примере (исходный код прилагается) у меня есть WPF TabControl, привязанный к некоторой коллекции.
На каждой вкладке вы можете редактировать элемент из коллекции, различными способами вызывая действие сохранения, которое должно сохранять изменения в некоторой модели.
Текстовые поля, привязанные к свойствам каждого элемента (специально), сохраняются для триггера обновления по умолчанию «OnFocusLost». Это связано с тем, что при установке нового значения выполняется дорогостоящая проверка.
Теперь я обнаружил, что есть по крайней мере два способа инициировать мое действие сохранения таким образом, чтобы последнее сфокусированное текстовое поле не обновляло связанное значение.
1) Изменение элемента вкладки с помощью щелчка мышью на его заголовке и затем нажатия некоторой кнопки сохранения.
(переход на предыдущую вкладку показывает, что новое значение даже потеряно)
2) Запуск команды сохранения через KeyGesture.
Я установил пример приложения, которое демонстрирует поведение. Нажатие на «Сохранить все» покажет все значения элемента, другая кнопка сохранения покажет только текущий элемент.
В: Как лучше всего убедиться, что все источники связывания всех моих текстовых полей будут обновлены до того, как связанные объекты будут завершены?
Желательно, чтобы был единый способ, позволяющий отловить все возможные варианты. Мне не нравится перехватывать каждое событие по-разному, так как я бы беспокоился о том, чтобы забыть о некоторых событиях.
Например, наблюдение за изменением выбора элемента управления вкладками решит проблему 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; }
}
}