Установите фокус на текстовое поле в WPF из модели представления (C#)
у меня есть TextBox
и Button
на мой взгляд.
теперь я проверяю условие при нажатии кнопки, и если условие оказывается ложным, отображая сообщение пользователю, а затем я должен установить курсор на TextBox
управление.
if (companyref == null)
{
var cs = new Lipper.Nelson.AdminClient.Main.Views.ContactPanels.CompanyAssociation();
MessageBox.Show("Company does not exist.", "Error", MessageBoxButton.OK,
MessageBoxImage.Exclamation);
cs.txtCompanyID.Focusable = true;
System.Windows.Input.Keyboard.Focus(cs.txtCompanyID);
}
приведенный выше код находится в ViewModel.
The CompanyAssociation
- имя вида.
но курсор не устанавливается в TextBox
.
xaml-это:
<igEditors:XamTextEditor Name="txtCompanyID"
KeyDown="xamTextEditorAllowOnlyNumeric_KeyDown"
ValueChanged="txtCompanyID_ValueChanged"
Text="{Binding Company.CompanyId,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Width="{Binding ActualWidth, ElementName=border}"
Grid.Column="1" Grid.Row="0"
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
Margin="0,5,0,0"
IsEnabled="{Binding Path=IsEditable}"/>
<Button Template="{StaticResource buttonTemp1}"
Command="{Binding ContactCommand}"
CommandParameter="searchCompany"
Content="Search"
Width="80"
Grid.Row="0" Grid.Column="2"
VerticalAlignment="Top"
Margin="0"
HorizontalAlignment="Left"
IsEnabled="{Binding Path=IsEditable}"/>
19 ответов:
мне интересно, что такое " cs.txtCompanyID" в вашем примере? Это элемент управления TextBox? Если да, то вы на неправильном пути. Вообще говоря, это не очень хорошая идея, чтобы иметь какую-либо ссылку на пользовательский интерфейс в вашем ViewModel. Вы можете спросить: "почему?"но это еще один вопрос, чтобы разместить на Stackoverflow:).
лучший способ отследить проблемы с фокусом... отладка исходного кода .Net. Без шуток. Это сэкономило мне много времени много раз. Чтобы включить отладку исходного кода .net см. Шон Bruke это блог.
наконец, общий подход, который я использую для установки фокуса из ViewModel, - это прикрепленные свойства. Я написал очень простое вложенное свойство, которое можно установить на любой UIElement. И он может быть привязан к свойству ViewModel "IsFocused", например. Вот это:
public static class FocusExtension { public static bool GetIsFocused(DependencyObject obj) { return (bool) obj.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject obj, bool value) { obj.SetValue(IsFocusedProperty, value); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached( "IsFocused", typeof (bool), typeof (FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged)); private static void OnIsFocusedPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { var uie = (UIElement) d; if ((bool) e.NewValue) { uie.Focus(); // Don't care about false values. } } }
теперь в вашем представлении (в XAML) вы можете привязать это свойство к вашему ViewModel:
<TextBox local:FocusExtension.IsFocused="{Binding IsUserNameFocused}" />
надеюсь, что это помогает :). Если это не относится к ответу #2.
Ура.
Я знаю, что на этот вопрос уже ответили тысячу раз, но я внес некоторые изменения в Вклад Анваки, которые, я думаю, помогут другим, у которых были подобные проблемы, которые у меня были.
во-первых, я изменил выше прикрепленное свойство следующим образом:
public static class FocusExtension { public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusExtension), new FrameworkPropertyMetadata(IsFocusedChanged){BindsTwoWayByDefault = true}); public static bool? GetIsFocused(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool?)element.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject element, bool? value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsFocusedProperty, value); } private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)d; if (e.OldValue == null) { fe.GotFocus += FrameworkElement_GotFocus; fe.LostFocus += FrameworkElement_LostFocus; } if (!fe.IsVisible) { fe.IsVisibleChanged += new DependencyPropertyChangedEventHandler(fe_IsVisibleChanged); } if ((bool)e.NewValue) { fe.Focus(); } } private static void fe_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)sender; if (fe.IsVisible && (bool)((FrameworkElement)sender).GetValue(IsFocusedProperty)) { fe.IsVisibleChanged -= fe_IsVisibleChanged; fe.Focus(); } } private static void FrameworkElement_GotFocus(object sender, RoutedEventArgs e) { ((FrameworkElement)sender).SetValue(IsFocusedProperty, true); } private static void FrameworkElement_LostFocus(object sender, RoutedEventArgs e) { ((FrameworkElement)sender).SetValue(IsFocusedProperty, false); } }
моей причиной для добавления ссылок на видимость были вкладки. По-видимому, если вы использовали прикрепленное свойство на любой другой вкладке за пределами первоначально видимой вкладки, прикрепленное свойство не работало до тех пор, пока вы вручную сфокусировал управление.
<TextBox Text="{Binding Description}" FocusExtension.IsFocused="{Binding IsFocused}"/>
Если есть лучший способ справиться с проблемой видимости, пожалуйста, дайте мне знать.
Примечание: спасибо Apfelkuacha за предложение положить BindsTwoWayByDefault в DependencyProperty. Я сделал это давно в своем собственном коде, но никогда не обновлялся эта должность. Mode=TwoWay больше не требуется в коде WPF из-за этого изменения.
Я думаю, что лучший способ-сохранить принцип MVVM чистым, поэтому в основном вы должны использовать класс Messenger, поставляемый с MVVM Light, и вот как его использовать:
в вашем viewmodel (exampleViewModel.cs): напишите следующее
Messenger.Default.Send<string>("focus", "DoFocus");
теперь на ваш взгляд.cs (не XAML представление.код XAML.КС) написать следующее В конструкторе
public MyView() { InitializeComponent(); Messenger.Default.Register<string>(this, "DoFocus", doFocus); } public void doFocus(string msg) { if (msg == "focus") this.txtcode.Focus(); }
этот метод owrks просто отлично и с меньшим количеством кода и поддержанием стандартов MVVM
ни один из них не работал для меня точно, но для пользы других, это то, что я закончил писать на основе некоторых из кода, уже предоставленного здесь.
использование будет следующим:
<TextBox ... h:FocusBehavior.IsFocused="True"/>
и реализация будет выглядеть следующим образом:
/// <summary> /// Behavior allowing to put focus on element from the view model in a MVVM implementation. /// </summary> public static class FocusBehavior { #region Dependency Properties /// <summary> /// <c>IsFocused</c> dependency property. /// </summary> public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusBehavior), new FrameworkPropertyMetadata(IsFocusedChanged)); /// <summary> /// Gets the <c>IsFocused</c> property value. /// </summary> /// <param name="element">The element.</param> /// <returns>Value of the <c>IsFocused</c> property or <c>null</c> if not set.</returns> public static bool? GetIsFocused(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool?)element.GetValue(IsFocusedProperty); } /// <summary> /// Sets the <c>IsFocused</c> property value. /// </summary> /// <param name="element">The element.</param> /// <param name="value">The value.</param> public static void SetIsFocused(DependencyObject element, bool? value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsFocusedProperty, value); } #endregion Dependency Properties #region Event Handlers /// <summary> /// Determines whether the value of the dependency property <c>IsFocused</c> has change. /// </summary> /// <param name="d">The dependency object.</param> /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Ensure it is a FrameworkElement instance. var fe = d as FrameworkElement; if (fe != null && e.OldValue == null && e.NewValue != null && (bool)e.NewValue) { // Attach to the Loaded event to set the focus there. If we do it here it will // be overridden by the view rendering the framework element. fe.Loaded += FrameworkElementLoaded; } } /// <summary> /// Sets the focus when the framework element is loaded and ready to receive input. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param> private static void FrameworkElementLoaded(object sender, RoutedEventArgs e) { // Ensure it is a FrameworkElement instance. var fe = sender as FrameworkElement; if (fe != null) { // Remove the event handler registration. fe.Loaded -= FrameworkElementLoaded; // Set the focus to the given framework element. fe.Focus(); // Determine if it is a text box like element. var tb = fe as TextBoxBase; if (tb != null) { // Select all text to be ready for replacement. tb.SelectAll(); } } } #endregion Event Handlers }
Это старый поток, но, похоже, нет ответа с кодом, который решает проблемы с принятым ответом Anavanka: он не работает, если вы устанавливаете свойство в viewmodel в false или если вы устанавливаете свое свойство в true, пользователь вручную нажимает на что-то еще, а затем вы снова устанавливаете его в true. Я также не мог заставить решение Замотика надежно работать в этих случаях.
собирая некоторые из приведенных выше обсуждений, я получаю код ниже, который делает решить эти вопросы я думаю:
public static class FocusExtension { public static bool GetIsFocused(DependencyObject obj) { return (bool)obj.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject obj, bool value) { obj.SetValue(IsFocusedProperty, value); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached( "IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, null, OnCoerceValue)); private static object OnCoerceValue(DependencyObject d, object baseValue) { if ((bool)baseValue) ((UIElement)d).Focus(); else if (((UIElement) d).IsFocused) Keyboard.ClearFocus(); return ((bool)baseValue); } }
сказав это, это все еще сложно для чего-то, что можно сделать в одной строке в codebehind, и CoerceValue на самом деле не предназначен для использования таким образом, поэтому, возможно, codebehind-это путь.
в моем случае FocusExtension не работал, пока я не изменил метод OnIsFocusedPropertyChanged. Исходный работал только в debug, когда точка останова остановила процесс. Во время выполнения процесс выполняется слишком быстро, и ничего не происходит. С этой небольшой модификацией и помощью нашей задачи друга, это работает нормально в обоих сценариях.
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var uie = (UIElement)d; if ((bool)e.NewValue) { var action = new Action(() => uie.Dispatcher.BeginInvoke((Action)(() => uie.Focus()))); Task.Factory.StartNew(action); } }
anvakas блестящий код для настольных приложений Windows. Если вы похожи на меня и нужно такое же решение для приложений Магазина Windows, этот код может пригодиться:
public static class FocusExtension { public static bool GetIsFocused(DependencyObject obj) { return (bool)obj.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject obj, bool value) { obj.SetValue(IsFocusedProperty, value); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached( "IsFocused", typeof(bool), typeof(FocusExtension), new PropertyMetadata(false, OnIsFocusedPropertyChanged)); private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { var uie = d as Windows.UI.Xaml.Controls.Control; if( uie != null ) { uie.Focus(FocusState.Programmatic); } } } }
проблема в том, что как только IsUserNameFocused установлен в true, он никогда не будет ложным. Это решает его путем обработки GotFocus и LostFocus для FrameworkElement.
У меня возникли проблемы с форматированием исходного кода, так что вот ссылке
для тех, кто пытается использовать решение Anvaka выше, у меня возникли проблемы с привязкой, работающей только в первый раз, так как lostfocus не обновит свойство до false. Вы можете вручную установить свойство false, а затем true каждый раз, но лучшим решением может быть сделать что-то вроде этого в вашем свойстве:
bool _isFocused = false; public bool IsFocused { get { return _isFocused ; } set { _isFocused = false; _isFocused = value; base.OnPropertyChanged("IsFocused "); } }
таким образом, вам нужно только установить его в true, и он получит фокус.
Я использую WPF / Caliburn Micro a обнаружил, что "dfaivre" сделал общее и работоспособное решение здесь: http://caliburnmicro.codeplex.com/discussions/222892
нашел важнорешение проблемы IsVisible очень полезно. Это не полностью решило мою проблему, но некоторый дополнительный код, следующий за тем же шаблоном для шаблона IsEnabled, сделал это.
к методу IsFocusedChanged я добавил:
if (!fe.IsEnabled) { fe.IsEnabledChanged += fe_IsEnabledChanged; }
и вот обработчик:
private static void fe_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)sender; if (fe.IsEnabled && (bool)((FrameworkElement)sender).GetValue(IsFocusedProperty)) { fe.IsEnabledChanged -= fe_IsEnabledChanged; fe.Focus(); } }
Для Silverlight:
using System.Windows; using System.Windows.Controls; using System.Windows.Interactivity; namespace MyProject.Behaviors { public class FocusBehavior : Behavior<Control> { protected override void OnAttached() { this.AssociatedObject.Loaded += AssociatedObject_Loaded; base.OnAttached(); } private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { this.AssociatedObject.Loaded -= AssociatedObject_Loaded; if (this.HasInitialFocus || this.IsFocused) { this.GotFocus(); } } private void GotFocus() { this.AssociatedObject.Focus(); if (this.IsSelectAll) { if (this.AssociatedObject is TextBox) { (this.AssociatedObject as TextBox).SelectAll(); } else if (this.AssociatedObject is PasswordBox) { (this.AssociatedObject as PasswordBox).SelectAll(); } else if (this.AssociatedObject is RichTextBox) { (this.AssociatedObject as RichTextBox).SelectAll(); } } } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.Register( "IsFocused", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, (d, e) => { if ((bool)e.NewValue) { ((FocusBehavior)d).GotFocus(); } })); public bool IsFocused { get { return (bool)GetValue(IsFocusedProperty); } set { SetValue(IsFocusedProperty, value); } } public static readonly DependencyProperty HasInitialFocusProperty = DependencyProperty.Register( "HasInitialFocus", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, null)); public bool HasInitialFocus { get { return (bool)GetValue(HasInitialFocusProperty); } set { SetValue(HasInitialFocusProperty, value); } } public static readonly DependencyProperty IsSelectAllProperty = DependencyProperty.Register( "IsSelectAll", typeof(bool), typeof(FocusBehavior), new PropertyMetadata(false, null)); public bool IsSelectAll { get { return (bool)GetValue(IsSelectAllProperty); } set { SetValue(IsSelectAllProperty, value); } } } }
LoginViewModel.cs:
public class LoginModel : ViewModelBase { .... private bool _EmailFocus = false; public bool EmailFocus { get { return _EmailFocus; } set { if (value) { _EmailFocus = false; RaisePropertyChanged("EmailFocus"); } _EmailFocus = value; RaisePropertyChanged("EmailFocus"); } } ... }
логин.xaml:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:beh="clr-namespace:MyProject.Behaviors" <TextBox Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <i:Interaction.Behaviors> <beh:FocusBehavior IsFocused="{Binding EmailFocus}" IsSelectAll="True"/> </i:Interaction.Behaviors> </TextBox>
или
<TextBox Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> <i:Interaction.Behaviors> <beh:FocusBehavior HasInitialFocus="True" IsSelectAll="True"/> </i:Interaction.Behaviors> </TextBox>
чтобы установить фокус, нужно просто сделать это в коде:
EmailFocus = true;
помните, что этот плагин является частью html-страницы, поэтому другие элементы управления на странице могут иметь фокус
if (!Application.Current.IsRunningOutOfBrowser) { System.Windows.Browser.HtmlPage.Plugin.Focus(); }
вы могли бы использовать ViewCommand шаблон дизайна. В нем описывается метод для шаблона проектирования MVVM для управления представлением из ViewModel с помощью команд.
я реализовал его на основе предложения короля А. Маджида использовать класс MVVM Light Messenger. Класс ViewCommandManager обрабатывает вызов команд в подключенных представлениях. Это в основном другое направление регулярных команд, для этих случаев, когда ViewModel должен сделать какое-то действие в своем представлении. Он использует отражение, как команды с привязкой к данным и WeakReferences, чтобы избежать утечек памяти.
http://dev.unclassified.de/source/viewcommand (также опубликовано на CodeProject)
Я нашел решение путем редактирования кода в соответствии со следующим. Нет необходимости, чтобы установить первое обязательное свойство ложно, то истинно.
public static class FocusExtension { public static bool GetIsFocused(DependencyObject obj) { return (bool)obj.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject obj, bool value) { obj.SetValue(IsFocusedProperty, value); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached( "IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged)); private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d != null && d is Control) { var _Control = d as Control; if ((bool)e.NewValue) { // To set false value to get focus on control. if we don't set value to False then we have to set all binding //property to first False then True to set focus on control. OnLostFocus(_Control, null); _Control.Focus(); // Don't care about false values. } } } private static void OnLostFocus(object sender, RoutedEventArgs e) { if (sender != null && sender is Control) { (sender as Control).SetValue(IsFocusedProperty, false); } } }
похоже, никто не включил последний шаг, чтобы упростить обновление атрибутов с помощью привязанных переменных. Вот что я придумал. Дайте мне знать, если есть лучший способ сделать это.
XAML
<TextBox x:Name="txtLabel" Text="{Binding Label}" local:FocusExtension.IsFocused="{Binding txtLabel_IsFocused, Mode=TwoWay}" /> <Button x:Name="butEdit" Content="Edit" Height="40" IsEnabled="{Binding butEdit_IsEnabled}" Command="{Binding cmdCapsuleEdit.Command}" />
ViewModel
public class LoginModel : ViewModelBase { public string txtLabel_IsFocused { get; set; } public string butEdit_IsEnabled { get; set; } public void SetProperty(string PropertyName, string value) { System.Reflection.PropertyInfo propertyInfo = this.GetType().GetProperty(PropertyName); propertyInfo.SetValue(this, Convert.ChangeType(value, propertyInfo.PropertyType), null); OnPropertyChanged(PropertyName); } private void Example_function(){ SetProperty("butEdit_IsEnabled", "False"); SetProperty("txtLabel_IsFocused", "True"); } }
во-первых, я хотел бы поблагодарить Avanka за помощь мне решить мою проблему с фокусом. Однако есть ошибка в коде, который он опубликовал, а именно в строке: если (e. OldValue = = null)
проблема, с которой я столкнулся, заключалась в том, что если вы сначала щелкнете в своем представлении и сфокусируете элемент управления, e.oldValue больше не будет null. Затем, когда вы устанавливаете переменную для фокусировки элемента управления в первый раз, это приводит к тому, что обработчики lostfocus и gotfocus не задаются. Мое решение было следующим следует:
public static class ExtensionFocus { static ExtensionFocus() { BoundElements = new List<string>(); } public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(ExtensionFocus), new FrameworkPropertyMetadata(false, IsFocusedChanged)); private static List<string> BoundElements; public static bool? GetIsFocused(DependencyObject element) { if (element == null) { throw new ArgumentNullException("ExtensionFocus GetIsFocused called with null element"); } return (bool?)element.GetValue(IsFocusedProperty); } public static void SetIsFocused(DependencyObject element, bool? value) { if (element == null) { throw new ArgumentNullException("ExtensionFocus SetIsFocused called with null element"); } element.SetValue(IsFocusedProperty, value); } private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)d; // OLD LINE: // if (e.OldValue == null) // TWO NEW LINES: if (BoundElements.Contains(fe.Name) == false) { BoundElements.Add(fe.Name); fe.LostFocus += OnLostFocus; fe.GotFocus += OnGotFocus; } if (!fe.IsVisible) { fe.IsVisibleChanged += new DependencyPropertyChangedEventHandler(fe_IsVisibleChanged); } if ((bool)e.NewValue) { fe.Focus(); } } private static void fe_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { var fe = (FrameworkElement)sender; if (fe.IsVisible && (bool)((FrameworkElement)sender).GetValue(IsFocusedProperty)) { fe.IsVisibleChanged -= fe_IsVisibleChanged; fe.Focus(); } } private static void OnLostFocus(object sender, RoutedEventArgs e) { if (sender != null && sender is Control s) { s.SetValue(IsFocusedProperty, false); } } private static void OnGotFocus(object sender, RoutedEventArgs e) { if (sender != null && sender is Control s) { s.SetValue(IsFocusedProperty, true); } } }
просто сделать это:
<Window x:class... ... ... FocusManager.FocusedElement="{Binding ElementName=myTextBox}" > <Grid> <TextBox Name="myTextBox"/> ...