Đặt tiêu điểm cho TextBox trong WPF từ mô hình xem


129

Tôi có một TextBoxvà một Buttontrong quan điểm của tôi.

Bây giờ tôi đang kiểm tra một điều kiện khi nhấp vào nút và nếu điều kiện đó là sai, hiển thị thông báo cho người dùng, và sau đó tôi phải đặt con trỏ thành TextBoxđiều khiển.

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

Đoạn mã trên nằm trong ViewModel.

Các CompanyAssociationlà tên xem.

Nhưng con trỏ không được đặt trong TextBox.

Xaml là:

<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}"/>

Khi bạn đang sử dụng caliburn.micro thì đây là một giải pháp tuyệt vời.
matze8426

Câu trả lời:


264

Hãy để tôi trả lời câu hỏi của bạn trong ba phần.

  1. Tôi đang tự hỏi "cs.txtCompanyID" trong ví dụ của bạn là gì? Đây có phải là điều khiển TextBox không? Nếu có, thì bạn đang đi sai đường. Nói chung, không nên có bất kỳ tham chiếu nào đến UI trong ViewModel của bạn. Bạn có thể hỏi "Tại sao?" nhưng đây là một câu hỏi khác để đăng trên Stackoverflow :).

  2. Cách tốt nhất để theo dõi các vấn đề với Focus là ... gỡ lỗi mã nguồn .Net. Không đua đâu. Nó đã tiết kiệm cho tôi rất nhiều thời gian nhiều lần. Để bật gỡ lỗi mã nguồn .net, hãy tham khảo blog của Shawn Bruke .

  3. Cuối cùng, cách tiếp cận chung mà tôi sử dụng để đặt tiêu điểm từ ViewModel là Thuộc tính được đính kèm. Tôi đã viết thuộc tính đính kèm rất đơn giản, có thể được đặt trên bất kỳ UIE bổ sung nào. Và nó có thể bị ràng buộc với thuộc tính "IsF Focused" của ViewModel. Đây là:

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

    Bây giờ trong Chế độ xem của bạn (bằng XAML), bạn có thể liên kết thuộc tính này với ViewModel của mình:

    <TextBox local:FocusExtension.IsFocused="{Binding IsUserNameFocused}" />

Hi vọng điêu nay co ich :). Nếu nó không tham khảo câu trả lời # 2.

Chúc mừng.


5
Ý tưởng tuyệt vời. Tôi cần đặt IsUserNameF Focused thành true, sau đó false lại để nó hoạt động, điều này có đúng không?
Sam

19
Bạn cũng nên gọi Keyboard.Focus(uie);từ OnIsFocusedPropertyChangedsự kiện của mình nếu bạn muốn quyền kiểm soát của mình nhận Focus Focus cũng như Focus Focus
Rachel

6
Làm thế nào là nó được sử dụng? Nếu tôi đặt tài sản của mình thành đúng, kiểm soát được tập trung. Nhưng nó sẽ luôn được tập trung trở lại khi tôi quay lại quan điểm này. Đặt lại nó từ OnIsF FocusedPropertyChanged không thay đổi điều này. Đặt lại trực tiếp sau khi cài đặt nó từ ViewModel không tập trung bất cứ điều gì nữa. Nó không hoạt động. 70 upvoters đã làm gì chính xác?
ygoe

4
Tôi cũng đã thay đổi cuộc gọi lại thành điều này: ...if ((bool)e.NewValue && uie.Dispatcher != null) { uie.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => uie.Focus())); // invoke behaves nicer, if e.g. you have some additional handler attached to 'GotFocus' of UIE. uie.SetValue(IsFocusedProperty, false); // reset bound value if possible, to allow setting again ... Đôi khi, tôi thậm chí phải đặt lại 'IsF Focused' thành false trong ViewModel, nếu tôi muốn đặt tiêu điểm nhiều lần. Nhưng sau đó nó hoạt động, nơi một số phương pháp khác thất bại.
Simon D.

3
sau khi bạn đặt tiêu điểm và một điều khiển khác lấy tiêu điểm, để đặt lại tiêu điểm sẽ không hoạt động vì IsF Focused vẫn đúng. Cần buộc nó sai và sau đó là đúng. public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
walterhuang

75

Tôi biết câu hỏi này đã được trả lời hàng ngàn lần so với bây giờ, nhưng tôi đã thực hiện một số chỉnh sửa cho đóng góp của Anvaka mà tôi nghĩ sẽ giúp những người khác có vấn đề tương tự mà tôi gặp phải.

Đầu tiên, tôi đã thay đổi Tài sản đính kèm ở trên như sau:

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

Lý do của tôi để thêm các tham chiếu khả năng hiển thị là các tab. Rõ ràng nếu bạn đã sử dụng thuộc tính đính kèm trên bất kỳ tab nào khác ngoài tab hiển thị ban đầu, thuộc tính được đính kèm sẽ không hoạt động cho đến khi bạn tập trung điều khiển theo cách thủ công.

Trở ngại khác là tạo ra một cách thanh lịch hơn để đặt lại tài sản cơ bản thành sai khi mất tập trung. Đó là nơi các sự kiện tập trung bị mất đến.

<TextBox            
    Text="{Binding Description}"
    FocusExtension.IsFocused="{Binding IsFocused}"/>

Nếu có cách tốt hơn để xử lý vấn đề về tầm nhìn, xin vui lòng cho tôi biết.

Lưu ý: Cảm ơn Apfelkuacha vì đã đề xuất đưa BindsTwoWayByDefault vào DependencyProperty. Tôi đã thực hiện điều đó từ lâu trong mã của riêng tôi, nhưng chưa bao giờ cập nhật bài đăng này. Chế độ = TwoWay không còn cần thiết trong mã WPF do thay đổi này.


9
Điều này hoạt động tốt với tôi ngoại trừ tôi cần thêm kiểm tra "if (e.Source == e.OrigenSource)" trong GotF Focus / LostF Focus hoặc nếu không thì stackoverflows (nghĩa đen) khi được sử dụng trên UserControl của tôi, nó chuyển hướng tiêu điểm vào bên trong thành phần. Tôi đã xóa các kiểm tra có thể nhìn thấy, chấp nhận thực tế là nó hoạt động giống như phương thức. Focus (). Nếu. Focus () không hoạt động, ràng buộc không nên hoạt động - và điều đó ổn với kịch bản của tôi.
HelloSam

1
Tôi đang sử dụng điều này trong WF 4.5. Trên IsF FocusedChanged tôi có một kịch bản (một Hoạt động được tải lại) trong đó e.NewValue là null và ném ngoại lệ, vì vậy hãy kiểm tra trước. Tất cả mọi thứ hoạt động tốt với sự thay đổi nhỏ này.
Olaru Mircea

1
Cảm ơn wprks này thật tuyệt :) Tôi vừa thêm '{BindsTwoWayByDefault = true}' tại 'FrameworkPropertyMetadata' để đặt chế độ mặc định thành TwoWayBinding để không cần thiết trên mọi Binding
R00st3r

1
Tôi nhận ra đây là một câu trả lời cũ, nhưng tôi đang gặp phải tình huống trong đó thuộc tính IsEnables của điều khiển mà tôi muốn chuyển trọng tâm được gắn với một công cụ chuyển đổi đa giá trị. Rõ ràng, trình xử lý sự kiện GotF Focus được gọi trước khi trình chuyển đổi đa giá trị thực hiện ... điều đó có nghĩa là điều khiển, tại thời điểm đó, bị vô hiệu hóa, vì vậy ngay khi GotF Focus hoàn thành, LostF Focus sẽ được gọi (tôi đoán vì điều khiển vẫn bị vô hiệu hóa) . Bất kỳ suy nghĩ về cách xử lý đó?
Đánh dấu Olbert

1
@MarkOlbert sử dụng fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);nó được cập nhật sau khi được tải. Thêm thông tin tại đây: telerik.com/forums/isf Focused
property#OXgFYZFOg0WZ2rxidln61Q

32

Tôi nghĩ cách tốt nhất là giữ nguyên tắc MVVM sạch sẽ, vì vậy về cơ bản, bạn phải sử dụng Lớp Messenger được cung cấp với MVVM Light và đây là cách sử dụng nó:

trong viewmodel của bạn (exampleViewModel.cs): viết như sau

 Messenger.Default.Send<string>("focus", "DoFocus");

bây giờ trong View.cs của bạn (không phải XAML, view.xaml.cs) hãy viết như sau trong hàm tạo

 public MyView()
        {
            InitializeComponent();

            Messenger.Default.Register<string>(this, "DoFocus", doFocus);
        }
        public void doFocus(string msg)
        {
            if (msg == "focus")
                this.txtcode.Focus();
        }

phương thức đó nợ tốt, với ít mã hơn và duy trì các tiêu chuẩn MVVM


9
Chà, nếu bạn muốn giữ nguyên tắc MVVM sạch sẽ, bạn sẽ không viết mã trong mã của mình ở vị trí đầu tiên. Tôi tin rằng cách tiếp cận tài sản đính kèm là sạch sẽ hơn nhiều. Nó cũng không giới thiệu nhiều chuỗi ma thuật trong mô hình xem của bạn.
Ε é И

32
El Nino: Chính xác thì bạn đã có ý tưởng ở đâu không nên có bất cứ điều gì trong chế độ xem mã phía sau của bạn? Bất cứ điều gì liên quan đến giao diện người dùng nên nằm trong mã phía sau của khung nhìn. Đặt trọng tâm của các thành phần UI chắc chắn phải nằm trong mã phía sau của khung nhìn. Hãy để viewmodel tìm ra thời điểm gửi tin nhắn; để cho xem để biết phải làm gì với tin nhắn. Đó là những gì MV-VM làm: tách biệt mối quan tâm của mô hình dữ liệu, logic nghiệp vụ và giao diện người dùng.
Kyle Hale

Dựa trên đề xuất này, tôi đã triển khai ViewCommandManager của riêng mình để xử lý các lệnh gọi trong các chế độ xem được kết nối. Về cơ bản, đó là hướng khác của các Lệnh thông thường, đối với những trường hợp này khi ViewModel cần thực hiện một số hành động trong (các) Chế độ xem của nó. Nó sử dụng sự phản chiếu như các lệnh liên kết dữ liệu và WeakReferences để tránh rò rỉ bộ nhớ. dev.unclassified.de/source/viewcommand (cũng trên CodeProject)
ygoe

Tôi đã sử dụng phương pháp này để in ra WPF FlowDocument. Làm việc độc đáo. Cảm ơn
Gordon Slysz

Tôi muốn một cái trong Silverlight? Chúng ta có thể sử dụng nó?
Bigeyes

18

Không ai trong số này làm việc cho tôi chính xác, nhưng vì lợi ích của người khác, đây là những gì tôi đã viết cuối cùng dựa trên một số mã đã được cung cấp ở đây.

Cách sử dụng sẽ như sau:

<TextBox ... h:FocusBehavior.IsFocused="True"/>

Và việc thực hiện sẽ như sau:

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

11

Đây là một chủ đề cũ, nhưng dường như không có câu trả lời với mã giải quyết các vấn đề với câu trả lời được chấp nhận của Anavanka: nó không hoạt động nếu bạn đặt thuộc tính trong chế độ xem thành sai hoặc nếu bạn đặt thuộc tính của mình thành đúng, người dùng nhấp thủ công vào thứ khác và sau đó bạn đặt lại thành đúng. Tôi cũng không thể có được giải pháp của Zamotic để làm việc đáng tin cậy trong những trường hợp này.

Tổng hợp một số cuộc thảo luận ở trên cung cấp cho tôi mã dưới đây giải quyết những vấn đề này tôi nghĩ:

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

Có nói rằng, điều này vẫn còn phức tạp đối với một cái gì đó có thể được thực hiện trong một dòng trong cơ sở mã hóa, và CoerceValue không thực sự được sử dụng theo cách này, vì vậy có lẽ codebehind là cách để đi.


1
Điều này hoạt động nhất quán, trong khi câu trả lời được chấp nhận thì không. Cảm ơn!
NathanAldenSr

4

Trong trường hợp của tôi, FocusExtension không hoạt động cho đến khi tôi thay đổi phương thức OnIsF FocusedPropertyChanged. Bản gốc chỉ hoạt động trong gỡ lỗi khi điểm dừng dừng quá trình. Trong thời gian chạy, quá trình này quá nhanh và không có gì xảy ra. Với sửa đổi nhỏ này và sự giúp đỡ của Nhiệm vụ bạn bè của chúng tôi, điều này hoạt động tốt trong cả hai kịch bản.

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

3

Vấn đề là một khi IsUserNameF Focused được đặt thành true, nó sẽ không bao giờ sai. Điều này giải quyết nó bằng cách xử lý GotF Focus và LostF Focus cho FrameworkEuity.

Tôi đã gặp sự cố với định dạng mã nguồn vì vậy đây là một liên kết


1
Tôi đã nói "đối tượng fe = (FrameworkEuity) d;" thành "FrameworkEuity fe = (FrameworkEuity) d;" vì vậy, intellisense hoạt động

Vẫn không giải quyết được vấn đề. Yếu tố tập trung mỗi khi tôi trở lại nó.
ygoe

3

Mã tuyệt vời của Anvakas dành cho các ứng dụng Windows Desktop. Nếu bạn giống tôi và cần cùng một giải pháp cho các ứng dụng Windows Store, mã này có thể có ích:

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

1

Đối với những người đang cố gắng sử dụng giải pháp của Anvaka ở trên, tôi đã gặp vấn đề với ràng buộc chỉ hoạt động lần đầu tiên, vì mất tập trung sẽ không cập nhật thuộc tính thành sai. Bạn có thể tự đặt thuộc tính thành false và sau đó là true, nhưng một giải pháp tốt hơn có thể là làm một cái gì đó như thế này trong tài sản của bạn:

bool _isFocused = false;
    public bool IsFocused 
    {
        get { return _isFocused ; }
        set
        {
            _isFocused = false;
            _isFocused = value;
            base.OnPropertyChanged("IsFocused ");
        }
    }

Bằng cách này, bạn chỉ cần đặt nó thành đúng, và nó sẽ được tập trung.


Tại sao có một tuyên bố if? _isF Focused một khi được đặt thành false sẽ chỉ được thay đổi thành giá trị trên dòng tiếp theo.
Damien McGivern

1
@Tyrsius Bạn có thể làm tròn vấn đề này bằng cách lấy tài sản phụ thuộc vào Coerce
RichardOD


1

Tôi đã tìm thấy giải pháp bằng cách chỉnh sửa mã theo sau. Không cần thiết phải đặt thuộc tính Binding trước Sai sau đó là 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, 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);
        }
    }
}

0

Đối với 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); }
        }

    }
}

Đăng nhậpViewModel.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");
            }
        }
       ...
   }

Đăng nhập.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>

HOẶC LÀ

<TextBox Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <i:Interaction.Behaviors>
        <beh:FocusBehavior HasInitialFocus="True" IsSelectAll="True"/>
    </i:Interaction.Behaviors>
</TextBox>

Để đặt tiêu điểm, chỉ cần thực hiện trong mã:

EmailFocus = true;

Hãy nhớ rằng plugin này là một phần của trang html, vì vậy các điều khiển khác trong trang có thể có trọng tâm

if (!Application.Current.IsRunningOutOfBrowser)
{
    System.Windows.Browser.HtmlPage.Plugin.Focus();
}

0

Bạn có thể sử dụng mẫu thiết kế ViewCommand . Nó mô tả một phương thức cho mẫu thiết kế MVVM để điều khiển View từ ViewModel bằng các lệnh.

Tôi đã triển khai nó dựa trên đề xuất của King A.Majid để sử dụng lớp Light Messenger MVVM. Lớp ViewCommandManager xử lý các lệnh gọi trong các khung nhìn được kết nối. Về cơ bản, đó là hướng khác của các Lệnh thông thường, đối với những trường hợp này khi ViewModel cần thực hiện một số hành động trong Chế độ xem của nó. Nó sử dụng sự phản chiếu như các lệnh liên kết dữ liệu và WeakReferences để tránh rò rỉ bộ nhớ.

http://dev.unclassified.de/source/viewcommand (cũng được xuất bản trên CodeProject)


0

Dường như không có ai bao gồm bước cuối cùng để giúp dễ dàng cập nhật các thuộc tính thông qua các biến bị ràng buộc. Đây là những gì tôi nghĩ ra. Hãy cho tôi biết nếu có một cách tốt hơn để làm điều này.

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

    }

0

Trước hết tôi muốn cảm ơn Avanka vì đã giúp tôi giải quyết vấn đề tập trung của mình. Tuy nhiên, có một lỗi trong mã anh ấy đã đăng, cụ thể là trong dòng: if (e.OldValue == null)

Vấn đề tôi gặp phải là nếu lần đầu tiên bạn nhấp vào chế độ xem và tập trung điều khiển, e.oldValue không còn là null. Sau đó, khi bạn đặt biến để tập trung điều khiển lần đầu tiên, điều này dẫn đến các trình xử lý lấy nét bị mất và lấy nét không được đặt. Giải pháp của tôi cho vấn đề này như sau:

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

0

Chỉ cần làm điều này:

<Window x:class...
   ...
   ...
   FocusManager.FocusedElement="{Binding ElementName=myTextBox}"
>
<Grid>
<TextBox Name="myTextBox"/>
...

Tôi thích điều này. Điều này hoạt động tốt nếu bạn muốn đặt trọng tâm ban đầu.
dùng2430797

0

Sau khi thực hiện câu trả lời được chấp nhận, tôi đã gặp phải một vấn đề là khi điều hướng các khung nhìn bằng Prism, TextBox vẫn không được lấy nét. Một thay đổi nhỏ đối với trình xử lý PropertyChanged đã giải quyết nó

    private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var uie = (UIElement)d;
        if ((bool)e.NewValue)
        {
            uie.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
            {
                uie.Focus();
            }));
        }
    }

0

Một cách tiếp cận khác dựa trên câu trả lời @Sheridan tại đây

 <TextBox Text="{Binding SomeText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <TextBox.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding SomeTextIsFocused, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

Trong mô hình chế độ xem của bạn, hãy thiết lập ràng buộc của bạn theo cách thông thường và sau đó đặt một số Tiêu đề thành đúng để đặt tiêu điểm trên hộp văn bản của bạn


-1

Tôi thấy rất quan trọng của giải pháp cho vấn đề IsVisible rất hữu ích. Nó không hoàn toàn giải quyết vấn đề của tôi, nhưng một số mã bổ sung theo cùng một mẫu cho mẫu IsEnables đã làm.

Đối với phương thức IsF FocusedChanged tôi đã thêm:

    if (!fe.IsEnabled)
    {
        fe.IsEnabledChanged += fe_IsEnabledChanged;
    }

Và đây là xử lý:

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

-1
public class DummyViewModel : ViewModelBase
    {
        private bool isfocused= false;
        public bool IsFocused
        {
            get
            {
                return isfocused;
            }
            set
            {
                isfocused= value;
                OnPropertyChanged("IsFocused");
            }
        }
    }

-7
System.Windows.Forms.Application.DoEvents();
Keyboard.Focus(tbxLastName);

1
OP đang sử dụng WPF. Mã tập trung cho WinForms sẽ không giúp ích.
Josh G
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.