WPF перед записью

В моем приложении WPF у меня есть несколько текстовых полей с привязкой к данным. UpdateSourceTrigger для этих привязок – LostFocus . Объект сохраняется в меню «Файл». Проблема заключается в том, что можно ввести новое значение в TextBox, выбрать «Сохранить» в меню «Файл» и никогда не сохранять новое значение (одно из видимых в TextBox), потому что доступ к меню не удаляет фокус из TextBox , Как я могу это исправить? Есть ли способ заставить все элементы управления на странице привязать данные?

@palehorse: Хорошая точка. К сожалению, мне нужно использовать LostFocus в качестве моего UpdateSourceTrigger, чтобы поддерживать тип проверки, которую я хочу.

@dmo: Я подумал об этом. Однако это похоже на действительно неэлегантное решение для относительно простой проблемы. Кроме того, это требует, чтобы на странице всегда был какой-то элемент управления, который всегда виден для получения фокуса. Однако мое приложение имеет вкладку, поэтому такой контроль не может быть легко представлен.

@Nidonocu: тот факт, что использование меню не перемещало фокус с TextBox, тоже смутило меня. Это, однако, поведение, которое я вижу. Следующий простой пример демонстрирует мою проблему:

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

Предположим, у вас есть TextBox в окне и ToolBar с кнопкой Save в нем. Предположим, что свойство TextBox Text привязано к свойству в бизнес-объекте, а свойство UpdateSourceTrigger привязки имеет значение по умолчанию для LostFocus, что означает, что связанное значение возвращается к свойству бизнес-объекта, когда TextBox теряет фокус ввода. Кроме того, предположим, что кнопка «Сохранить» в ToolBar имеет свойство Command, заданное командой ApplicationCommands.Save.

В этой ситуации, если вы редактируете TextBox и щелкаете кнопкой «Сохранить» с помощью мыши, возникает проблема. При нажатии кнопки на панели инструментов TextBox не теряет фокус. Поскольку событие LostFocus в TextBox не срабатывает, привязка свойства Text не обновляет исходное свойство бизнес-объекта.

Очевидно, что вы не должны проверять и сохранять объект, если последнее редактируемое значение в пользовательском интерфейсе еще не было введено в объект. Это точная проблема, с которой Карл работал, написав код в своем окне, который вручную искал TextBox с фокусом и обновил источник привязки данных. Его решение работало нормально, но это заставило меня задуматься о универсальном решении, которое также было бы полезно вне этого конкретного сценария. Введите CommandGroup …

Взято из статьи CodeProject от Джоша Смита о CommandGroup

Я обнаружил, что удаление пунктов меню, зависящих от фокуса FocusScope меню, заставляет текстовое поле терять фокус правильно. Я бы не подумал, что это относится ко всем элементам меню, но, конечно, для сохранения или проверки действия.

  

Предполагая, что в последовательности вкладок имеется более одного элемента управления, следующее решение выглядит полным и общим (просто вырезать-вставить) …

 Control currentControl = System.Windows.Input.Keyboard.FocusedElement as Control; if (currentControl != null) { // Force focus away from the current control to update its binding source. currentControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); currentControl.Focus(); } 

Это UGLY взлом, но он также должен работать

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

Этот код проверяет, имеет ли TextBox фокус … Если 1 найден … обновите источник привязки!

Простым решением является обновление кода Xaml, как показано ниже.

    

Вы пытались настроить UpdateSourceTrigger на PropertyChanged? В качестве альтернативы вы можете вызвать метод UpdateSOurce (), но это кажется немного переборщиком и побеждает цель привязки данных TwoWay.

Я столкнулся с этой проблемой, и лучшим решением, которое я нашел, было изменение фокусируемого значения кнопки (или любого другого компонента, такого как MenuItem), на true:

  

Причина, по которой она работает, заключается в том, что она заставляет кнопку сфокусироваться, прежде чем она вызывает команду, и поэтому делает TextBox или любой другой UIElement таким образом, чтобы потерять фокус и увеличить потерянное фокусное событие, которое вызывает привязку, которую нужно изменить.

Если вы используете ограниченную команду (как я указывал в моем примере), отличное решение Джона Смита не очень хорошо впишется, поскольку вы не можете привязать StaticExtension к ограниченному свойству (или DP).

Не могли бы вы настроить фокус в другом месте перед сохранением?

Вы можете сделать это, вызвав focus () для элемента пользовательского интерфейса.

Вы можете сосредоточиться на любом элементе, который вызывает «сохранение». Если ваш триггер – LostFocus, вам нужно куда-то перемещать фокус. Сохранить имеет то преимущество, что оно не изменяется и имеет смысл для пользователя.

Изучая это, чтобы ответить на него, я немного смущен тем, что поведение, которое вы видите, происходит, конечно, действие щелчка по меню «Файл» или что вам нужно расфокусировать текстовое поле и установить его в меню?

Самый простой способ – установить фокус где-нибудь .
Вы можете сразу установить фокус, но настройка фокуса в любом месте вызовет событие LostFocus на любом типе управления и сделает его обновлением:

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

Что Вы думаете об этом? Я считаю, что я понял способ сделать его более универсальным, используя reflection. Мне действительно не понравилась идея сохранить список, как некоторые из других примеров.

 var currentControl = System.Windows.Input.Keyboard.FocusedElement; if (currentControl != null) { Type type = currentControl.GetType(); if (type.GetMethod("MoveFocus") != null && type.GetMethod("Focus") != null) { try { type.GetMethod("MoveFocus").Invoke(currentControl, new object[] { new TraversalRequest(FocusNavigationDirection.Next) }); type.GetMethod("Focus").Invoke(currentControl, null); } catch (Exception ex) { throw new Exception("Unable to handle unknown type: " + type.Name, ex); } } } 

См. Какие-либо проблемы с этим?

Поскольку я заметил, что этот вопрос по-прежнему является болью в заднице, чтобы решить очень общий путь, я пробовал различные решения.

В конце концов, тот, который сработал для меня: когда есть необходимость в том, что изменения пользовательского интерфейса должны быть проверены и обновлены до его источников (проверьте изменения при закрытии windows, выполняя операции сохранения, …), я вызываю функцию проверки, которая выполняет различные вещи: – убедитесь, что сфокусированный элемент (например, текстовое поле, combobox, …) теряет фокус, который инициирует поведение по умолчанию по умолчанию – проверяет любые элементы управления в дереве DependencyObject, который присваивается функции проверки – установите фокус на оригинальный сфокусированный элемент

Сама функция возвращает true, если все в порядке (валидация успешна) -> ваше исходное действие (закрытие с дополнительным запросом подтверждения, сохранение, …) может продолжаться. В противном случае функция вернет false, и ваше действие не будет продолжено, потому что есть ошибки проверки на одном или нескольких элементах (с помощью общего шаблона ErrorTemplate для элементов).

Код (функция проверки основана на статье « Обнаружение ошибок проверки WPF» ):

 public static class Validator { private static Dictionary> gdicCachedDependencyProperties = new Dictionary>(); public static Boolean IsValid(DependencyObject Parent) { // Move focus and reset it to update bindings which or otherwise not processed until losefocus IInputElement lfocusedElement = Keyboard.FocusedElement; if (lfocusedElement != null && lfocusedElement is UIElement) { // Move to previous AND to next InputElement (if your next InputElement is a menu, focus will not be lost -> therefor move in both directions) (lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous)); (lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); Keyboard.ClearFocus(); } if (Parent as UIElement == null || (Parent as UIElement).Visibility != Visibility.Visible) return true; // Validate all the bindings on the parent Boolean lblnIsValid = true; foreach (DependencyProperty aDependencyProperty in GetAllDependencyProperties(Parent)) { if (BindingOperations.IsDataBound(Parent, aDependencyProperty)) { // Get the binding expression base. This way all kinds of bindings (MultiBinding, PropertyBinding, ...) can be updated BindingExpressionBase lbindingExpressionBase = BindingOperations.GetBindingExpressionBase(Parent, aDependencyProperty); if (lbindingExpressionBase != null) { lbindingExpressionBase.ValidateWithoutUpdate(); if (lbindingExpressionBase.HasError) lblnIsValid = false; } } } if (Parent is Visual || Parent is Visual3D) { // Fetch the visual children (in case of templated content, the LogicalTreeHelper will return no childs) Int32 lintVisualChildCount = VisualTreeHelper.GetChildrenCount(Parent); for (Int32 lintVisualChildIndex = 0; lintVisualChildIndex < lintVisualChildCount; lintVisualChildIndex++) if (!IsValid(VisualTreeHelper.GetChild(Parent, lintVisualChildIndex))) lblnIsValid = false; } if (lfocusedElement != null) lfocusedElement.Focus(); return lblnIsValid; } public static List GetAllDependencyProperties(DependencyObject DependencyObject) { Type ltype = DependencyObject.GetType(); if (gdicCachedDependencyProperties.ContainsKey(ltype.FullName)) return gdicCachedDependencyProperties[ltype.FullName]; List llstDependencyProperties = new List(); List llstFieldInfos = ltype.GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static).Where(Field => Field.FieldType == typeof(DependencyProperty)).ToList(); foreach (FieldInfo aFieldInfo in llstFieldInfos) llstDependencyProperties.Add(aFieldInfo.GetValue(null) as DependencyProperty); gdicCachedDependencyProperties.Add(ltype.FullName, llstDependencyProperties); return llstDependencyProperties; } } 

Я использую BindingGroup.

XAML:

     ...  

C #

 private void RibbonWindow_Closing(object sender, CancelEventArgs e) { e.Cancel = !NeedSave(); } bool NeedSave() { BindingGroup.CommitEdit(); // Insert your business code to check modifications. // return true; if Saved/DontSave/NotChanged // return false; if Cancel } 

Он должен работать.

  • Image UriSource и привязка данных
  • Почему привязка данных WPF к исключению ласточки?
  • Заполнить Combobox из базы данных
  • Нажатие свойств GUI только для чтения обратно в ViewModel
  • Принуждение WPF TextBox больше не работает в .NET 4.0
  • Связывающие свойства в коде
  • Есть ли способ определить, где объявляется / создается привязка WPF?
  • ElementName привязка из MenuItem в ContextMenu
  • WPF привязка данных к интерфейсу, а не к фактическому объекту - возможность литья?
  • Связывание в WPF с элементом массива, заданным свойством
  • Что означает «{Binding Path =.}» В привязке WPF?
  • Давайте будем гением компьютера.