ตั้งโฟกัสบน TextBox ใน WPF จากมุมมองโมเดล


129

ฉันมี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

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

เมื่อคุณใช้ caliburn.micro นี่เป็นทางออกที่ยอดเยี่ยม
matze8426

คำตอบ:


265

ให้ฉันตอบคำถามของคุณเป็นสามส่วน

  1. ฉันสงสัยว่า "cs.txtCompanyID" ในตัวอย่างของคุณคืออะไร เป็นตัวควบคุมกล่องข้อความหรือไม่ ถ้าใช่แสดงว่าคุณมาผิดทาง โดยทั่วไปไม่ควรมีการอ้างอิงถึง UI ใน ViewModel ของคุณ คุณสามารถถามว่า "ทำไม" แต่นี่เป็นอีกคำถามที่จะโพสต์บน Stackoverflow :)

  2. วิธีที่ดีที่สุดในการติดตามปัญหาเกี่ยวกับ Focus คือ ... การดีบักซอร์สโค้ด. Net ไม่ได้ล้อเล่น. มันช่วยฉันได้มากหลายครั้ง หากต้องการเปิดใช้งานการดีบักซอร์สโค้ด. net โปรดดูบล็อกของ Shawn Bruke

  3. สุดท้ายวิธีการทั่วไปที่ฉันใช้ในการกำหนดโฟกัสจาก ViewModel คือ Attached Properties ฉันเขียนคุณสมบัติที่แนบง่ายมากซึ่งสามารถตั้งค่าบน UIElement ใดก็ได้ และสามารถผูกไว้กับคุณสมบัติ "IsFocused" ของ ViewModel ได้เช่นกัน นี่คือ:

    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

ไชโย


5
ไอเดียเจ๋ง. ฉันต้องตั้งค่า IsUserNameFocused เป็น true จากนั้นเป็นเท็จอีกครั้งเพื่อให้มันใช้งานได้ใช่ไหม
แซม

19
นอกจากนี้คุณควรโทรKeyboard.Focus(uie);จากOnIsFocusedPropertyChangedกิจกรรมของคุณหากคุณต้องการให้ผู้ควบคุมรับ Keyboard Focus และ Logical Focus
Rachel

6
ควรใช้อย่างไร? ถ้าฉันตั้งค่าคุณสมบัติของฉันเป็นจริงการควบคุมจะเน้น แต่มันจะถูกโฟกัสอีกครั้งเสมอเมื่อฉันกลับมาที่มุมมองนี้ การรีเซ็ตจาก OnIsFocusedPropertyChanged จะไม่เปลี่ยนสิ่งนี้ การรีเซ็ตโดยตรงหลังจากตั้งค่าจาก ViewModel ไม่ได้เน้นอะไรอีกต่อไป มันใช้ไม่ได้ ผู้โหวต 70 คนเหล่านั้นทำอะไรกันแน่?
ygoe

4
ฉันยังเปลี่ยนการโทรกลับเป็นสิ่งนี้: ...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 ... บางครั้งฉันต้องรีเซ็ต 'IsFocused' เป็นเท็จใน ViewModel หากฉันต้องการตั้งโฟกัสหลายครั้ง แต่ก็ใช้ได้ผลโดยที่วิธีอื่นบางอย่างล้มเหลว
Simon D.

3
หลังจากที่คุณตั้งโฟกัสและตัวควบคุมอื่นได้รับโฟกัสการตั้งโฟกัสอีกครั้งจะไม่ทำงานเนื่องจาก IsFocused ยังคงเป็นจริง จำเป็นต้องบังคับให้เป็นเท็จแล้วจึงเป็นจริง public bool IsFocused { get { return _isFocused; } set { if (_isFocused == value) { _isFocused = false; OnPropertyChanged(); } _isFocused = value; OnPropertyChanged(); } }
walterhuang

75

ฉันรู้ว่าคำถามนี้ได้รับคำตอบเป็นพัน ๆ ครั้งในตอนนี้ แต่ฉันได้แก้ไขการมีส่วนร่วมของ Anvaka ซึ่งฉันคิดว่าจะช่วยคนอื่นที่มีปัญหาคล้าย ๆ กันกับฉัน

ประการแรกฉันเปลี่ยนคุณสมบัติที่แนบมาข้างต้นดังนี้:

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 อีกต่อไปเนื่องจากการเปลี่ยนแปลงนี้


9
สิ่งนี้ใช้ได้ดีสำหรับฉันยกเว้นฉันต้องเพิ่มการตรวจสอบ "if (e.Source == e.OriginalSource)" ใน GotFocus / LostFocus หรืออื่น ๆ ก็คือ stackoverflows (ตามตัวอักษร) เมื่อใช้กับ UserControl ของฉันซึ่งจะเปลี่ยนเส้นทางโฟกัสไปที่ด้านใน ส่วนประกอบ ฉันลบการตรวจสอบที่มองเห็นได้โดยยอมรับความจริงว่ามันใช้งานได้เหมือนกับเมธอด. โฟกัส () ถ้า. Focus () ไม่ทำงานการเชื่อมโยงไม่ควรใช้งานได้และนั่นก็โอเคสำหรับสถานการณ์ของฉัน
HelloSam

1
ฉันใช้สิ่งนี้ใน WF 4.5 บน IsFocusedChanged ฉันมีสถานการณ์ (กิจกรรมได้รับการโหลดซ้ำ) โดยที่ e.NewValue เป็นโมฆะและแสดงข้อยกเว้นดังนั้นให้ตรวจสอบก่อน ทุกอย่างทำงานได้ดีกับการเปลี่ยนแปลงเล็กน้อยนี้
Olaru Mircea

1
ขอบคุณ wprks นี้เยี่ยมมาก :) ฉันเพิ่งเพิ่ม '{BindsTwoWayByDefault = true}' ที่ 'FrameworkPropertyMetadata' เพื่อตั้งค่าโหมดเริ่มต้นเป็น TwoWayBinding ดังนั้นจึงไม่จำเป็นสำหรับทุกการผูก
R00st3r

1
ฉันรู้ว่านี่เป็นคำตอบเก่า ๆ แต่ฉันกำลังตกอยู่ในสถานการณ์ที่คุณสมบัติ IsEnabled ของตัวควบคุมที่ฉันต้องการเปลี่ยนโฟกัสนั้นเชื่อมโยงกับตัวแปลงหลายค่า เห็นได้ชัดว่าตัวจัดการเหตุการณ์ GotFocus จะถูกเรียกก่อนที่ตัวแปลงหลายค่าจะทำ ... ซึ่งหมายความว่าการควบคุม ณ จุดนั้นถูกปิดใช้งานดังนั้นทันทีที่ GotFocus เสร็จสิ้น LostFocus จะถูกเรียก (ฉันเดาว่าเพราะการควบคุมยังคงปิดใช้งาน) . มีความคิดเกี่ยวกับวิธีจัดการหรือไม่?
Mark Olbert

1
@MarkOlbert ใช้fe.Dispatcher.BeginInvoke(new Action(() => { fe.Focus(); }), DispatcherPriority.Loaded);ว่ามีการอัปเดตหลังจากโหลดแล้ว ข้อมูลเพิ่มเติมที่นี่: telerik.com/forums/isfocused-property#OXgFYZFOg0WZ2rxidln61Q
Apfelkuacha

32

ฉันคิดว่าวิธีที่ดีที่สุดคือการรักษาหลักการ MVVM ให้สะอาดโดยพื้นฐานแล้วคุณต้องใช้ Messenger Class ที่มาพร้อมกับ MVVM Light และนี่คือวิธีใช้:

ใน viewmodel ของคุณ (exampleViewModel.cs): เขียนสิ่งต่อไปนี้

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

ตอนนี้ใน View.cs ของคุณ (ไม่ใช่ XAML ที่ view.xaml.cs) เขียนสิ่งต่อไปนี้ในตัวสร้าง

 public MyView()
        {
            InitializeComponent();

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

วิธีการนั้นใช้ได้ดีและมีโค้ดน้อยลงและรักษามาตรฐาน MVVM


9
ถ้าคุณต้องการรักษาหลักการ MVVM ให้สะอาดคุณจะไม่ต้องเขียนโค้ดในโค้ดของคุณในตอนแรก ฉันเชื่อว่าแนวทางคุณสมบัติที่แนบมานั้นสะอาดกว่ามาก มันไม่ได้แนะนำสายเวทย์จำนวนมากในโมเดลมุมมองของคุณเช่นกัน
ΕГИІИО

32
เอลนิโญ: คุณได้แนวคิดมาจากไหนว่าไม่ควรมีอะไรอยู่เบื้องหลังโค้ดมุมมองของคุณ? สิ่งใดก็ตามที่เกี่ยวข้องกับ UI ควรอยู่ในโค้ดหลังของมุมมอง การตั้งจุดสำคัญขององค์ประกอบ UI ควรแน่นอนจะอยู่ในมุมมองของรหัสที่อยู่เบื้องหลัง ให้ viewmodel คิดว่าเมื่อใดที่จะส่งข้อความ ให้มุมมองคิดว่าจะทำอย่างไรกับข้อความ นั่นคือสิ่งที่ MV-VM ทำ: แยกความกังวลของโมเดลข้อมูลตรรกะทางธุรกิจและ UI
Kyle Hale

จากคำแนะนำนี้ฉันได้ใช้ ViewCommandManager ของฉันเองที่จัดการกับคำสั่งเรียกใช้ในมุมมองที่เชื่อมต่อ โดยพื้นฐานแล้วเป็นอีกทิศทางหนึ่งของคำสั่งปกติสำหรับกรณีเหล่านี้เมื่อ ViewModel ต้องดำเนินการบางอย่างใน View (s) ใช้การสะท้อนเช่นคำสั่งผูกข้อมูลและ WeakReferences เพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ dev.unclassified.de/source/viewcommand (เช่นเดียวกับ CodeProject)
ygoe

ฉันใช้วิธีนี้เพื่อพิมพ์ WPF FlowDocuments ทำงานได้ดี ขอบคุณ
Gordon Slysz

ฉันต้องการหนึ่งใน Silverlight? เราใช้ได้ไหม
Bigeyes

18

สิ่งเหล่านี้ไม่ได้ผลสำหรับฉันอย่างแน่นอน แต่เพื่อประโยชน์ของผู้อื่นนี่คือสิ่งที่ฉันลงเอยด้วยการเขียนตามโค้ดบางส่วนที่ให้ไว้ที่นี่แล้ว

การใช้งานจะเป็นดังนี้:

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

11

นี่เป็นกระทู้เก่า แต่ดูเหมือนจะไม่มีคำตอบเกี่ยวกับรหัสที่แก้ไขปัญหาเกี่ยวกับคำตอบที่ยอมรับของ Anavanka: จะไม่ทำงานหากคุณตั้งค่าคุณสมบัติใน viewmodel เป็นเท็จหรือหากคุณตั้งค่าคุณสมบัติเป็น จริงผู้ใช้คลิกอย่างอื่นด้วยตนเองจากนั้นคุณตั้งค่าเป็นจริงอีกครั้ง ฉันไม่สามารถให้โซลูชันของ Zamotic ทำงานได้อย่างน่าเชื่อถือในกรณีเหล่านี้เช่นกัน

การรวบรวมการอภิปรายด้านบนบางส่วนเข้าด้วยกันทำให้ฉันมีรหัสด้านล่างซึ่งจะช่วยแก้ปัญหาเหล่านี้ฉันคิดว่า:

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 อาจเป็นวิธีที่จะไป


1
สิ่งนี้ใช้ได้ผลอย่างสม่ำเสมอในขณะที่คำตอบที่ยอมรับไม่ได้ ขอบคุณ!
NathanAldenSr

4

ในกรณีของฉัน FocusExtension ใช้ไม่ได้จนกว่าฉันจะเปลี่ยนวิธี OnIsFocusedPropertyChanged อันเดิมทำงานเฉพาะในการดีบักเมื่อจุดพักหยุดกระบวนการ ในรันไทม์กระบวนการนี้เร็วเกินไปและไม่มีอะไรเกิดขึ้น ด้วยการปรับเปลี่ยนเพียงเล็กน้อยนี้และความช่วยเหลือจาก Task ของเพื่อนเราสิ่งนี้ทำงานได้ดีในทั้งสองสถานการณ์

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

ปัญหาคือเมื่อตั้งค่า IsUserNameFocused เป็น true แล้วจะไม่มีวันเป็นเท็จ สิ่งนี้แก้ไขได้โดยการจัดการ GotFocus และ LostFocus สำหรับ FrameworkElement

ฉันมีปัญหากับการจัดรูปแบบซอร์สโค้ดดังนั้นนี่คือลิงค์


1
ฉัน chhanged "object fe = (FrameworkElement) d;" ถึง "FrameworkElement fe = (FrameworkElement) d;" ดังนั้น intellisense จึงใช้งานได้

ยังไม่สามารถแก้ปัญหาได้ องค์ประกอบยังคงมีสมาธิทุกครั้งที่ฉันกลับมา
ygoe

3

Anvakas รหัสที่ยอดเยี่ยมสำหรับแอปพลิเคชัน Windows Desktop หากคุณเป็นเหมือนฉันและต้องการโซลูชันเดียวกันสำหรับแอพ Windows Store รหัสนี้อาจมีประโยชน์:

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

สำหรับผู้ที่พยายามใช้โซลูชันของ Anvaka ข้างต้นฉันมีปัญหากับการเชื่อมต่อที่ใช้งานได้ในครั้งแรกเท่านั้นเนื่องจาก lostfocus จะไม่อัปเดตคุณสมบัติเป็นเท็จ คุณสามารถตั้งค่าคุณสมบัติเป็นเท็จด้วยตนเองแล้วจึงเป็นจริงทุกครั้ง แต่วิธีแก้ปัญหาที่ดีกว่าคือการทำสิ่งนี้ในคุณสมบัติของคุณ:

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

ด้วยวิธีนี้คุณจะต้องตั้งค่าเป็นจริงเท่านั้นและจะได้รับโฟกัส


ทำไมคุณถึงมีคำสั่ง if? _isFocused เมื่อตั้งค่าเป็นเท็จจะเปลี่ยนเป็นค่าในบรรทัดถัดไป
Damien McGivern

1
@Tyrsius คุณสามารถแก้ไขปัญหานี้ได้โดยรับคุณสมบัติการพึ่งพาเพื่อ Coerce ดูที่นี่ - social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
RichardOD


1

ฉันพบวิธีแก้ปัญหาโดยการแก้ไขโค้ดตามต่อไปนี้ ไม่จำเป็นต้องตั้งค่าคุณสมบัติการผูกก่อน False แล้วจึงเป็น 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

สำหรับ 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");
            }
        }
       ...
   }

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

0

คุณสามารถใช้ViewCommandรูปแบบการออกแบบอธิบายวิธีการสำหรับรูปแบบการออกแบบ MVVM เพื่อควบคุมมุมมองจาก ViewModel ด้วยคำสั่ง

ฉันได้ดำเนินการตามคำแนะนำของ King A. มาจิดในการใช้คลาส MVVM Light Messenger คลาส ViewCommandManager จัดการการเรียกใช้คำสั่งในมุมมองที่เชื่อมต่อ โดยพื้นฐานแล้วเป็นอีกทิศทางหนึ่งของคำสั่งปกติสำหรับกรณีเหล่านี้เมื่อ ViewModel ต้องดำเนินการบางอย่างใน View ใช้การสะท้อนเช่นคำสั่งผูกข้อมูลและ WeakReferences เพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ

http://dev.unclassified.de/source/viewcommand (เผยแพร่บน CodeProject ด้วย)


0

ดูเหมือนว่าจะไม่มีขั้นตอนสุดท้ายในการอัปเดตแอตทริบิวต์ผ่านตัวแปรที่ผูกไว้ นี่คือสิ่งที่ฉันคิดขึ้นมา โปรดแจ้งให้เราทราบหากมีวิธีที่ดีกว่านี้

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

ก่อนอื่นฉันขอขอบคุณ Avanka ที่ช่วยฉันแก้ปัญหาโฟกัสของฉัน อย่างไรก็ตามมีข้อผิดพลาดในรหัสที่เขาโพสต์กล่าวคือในบรรทัด: if (e.OldValue == null)

ปัญหาที่ฉันพบคือถ้าคุณคลิกในมุมมองของคุณเป็นครั้งแรกและโฟกัสการควบคุม e.oldValue จะไม่เป็นโมฆะอีกต่อไป จากนั้นเมื่อคุณตั้งค่าตัวแปรเพื่อโฟกัสการควบคุมเป็นครั้งแรกสิ่งนี้จะส่งผลให้ตัวจัดการโฟกัสหายไปและตัวจัดการโฟกัสไม่ได้รับการตั้งค่า วิธีแก้ปัญหาของฉันมีดังนี้:

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

เพียงทำสิ่งนี้:

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

ฉันชอบสิ่งนี้. วิธีนี้ใช้ได้ดีหากคุณต้องการตั้งค่าโฟกัสเริ่มต้น
user2430797

0

หลังจากใช้คำตอบที่ยอมรับแล้วฉันพบปัญหาที่เมื่อนำทางมุมมองด้วย Prism TextBox จะยังไม่ได้รับโฟกัส การเปลี่ยนแปลงเล็กน้อยในตัวจัดการ PropertyChanged แก้ไขได้

    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

แนวทางอื่นตามคำตอบของ @Sheridan ที่นี่

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

ในโมเดลมุมมองของคุณตั้งค่าการผูกของคุณตามปกติแล้วตั้งค่า SomeTextIsFocused เป็น true เพื่อกำหนดโฟกัสบนกล่องข้อความของคุณ


-1

ฉันพบว่าCrucialการแก้ปัญหาของปัญหาการ 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();
    }
}

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.