2012-12-21 3 views
2

이것은 이상한 것이고 무엇을 검색해야할지 모르지만 실제로는 저를 신뢰합니다.WPF TextBox 커서 이동 및 포커스 변경

텍스트 상자가 있고 그 내용이 OnTextChanged이면 다음과 같은 방법입니다.

여기서 텍스트 상자에 포커스를 지정하고 커서를 TextBox의 끝으로 이동 한 다음 실제로 포커스가 설정된 항목 (일반적으로 단추)으로 포커스를 되돌립니다. 문제는 TextBox가 "redrawn"(더 좋은 단어가 없기 때문에) 인 것 같아요. 포커스를 원래의 포커스 된 요소로 되돌려 보내 커서 위치가 화면에 나타나지 않도록합니다. .

현재이 문제를 근본적으로 해킹하여 기본적으로 이전에 초점을 맞춘 항목의 초점을 10ms 지연시키고 다른 스레드에서 UI를 업데이트 할 시간을 갖습니다. 자, 이것은 분명히 임의의 시간이며 내 컴퓨터에서 잘 작동하지만 이전 컴퓨터에서이 응용 프로그램을 실행하는 사용자는 문제가있을 수 있습니다.

적절한 방법이 있습니까? 나는 그것을 이해할 수 없다.

private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) 
{ 
    if (sender == null) return; 
    var box = sender as TextBox; 

    if (!box.IsFocused) 
    { 

     var oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this)); 
     box.Select(box.Text.Length, 0); 
     Keyboard.Focus(box); // or box.Focus(); both have the same results 

     var thread = new Thread(new ThreadStart(delegate 
                { 
                 Thread.Sleep(10); 
                 Dispatcher.Invoke(new Action(() => oldFocus.Focus())); 
                })); 
     thread.Start(); 
    } 
} 

편집 내가 가진 새로운 아이디어는 내가 다음을 시도 UI가 너무 업데이트 완료되면 oldFocus.Focus() 메소드를 실행했지만, 난 같은 결과를 얻을 :(

var oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this)); 

Dispatcher.Invoke(DispatcherPriority.Send, new Action(delegate 
{ 
    box.Select(box.Text.Length, 0); 
    box.Focus(); 
})); 

Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => oldFocus.Focus())); 
+0

이 태그는 WPF 응용 프로그램이라고 생각합니다. 태그를 변경했습니다. 일반적으로 우리가 어떤 종류의 응용 프로그램을 작성하는지 알려주어 잘못 추측 할 필요가 없습니다. –

+0

사과드립니다. 나는 앞으로 잊지 않을 것이다. – Skinner927

+0

강제로 다시 그리기 위해 oldFocus.Invalidate()를 시도 했습니까? –

답변

0

며칠 후에, 나는 마침내 그것을 작동시킬 수있었습니다. Dispatcher는 텍스트 상자에 focus와 keyboardfocus가 있고 많은 루프가 있는지 확인해야했습니다.

다음은 참조 용 코드입니다. 거기에 몇 가지 의견이 있지만 누군가가 대답을 찾고이 페이지를 친다면, 당신은 그것을 통해 독서해야합니다. 알림, 텍스트 변경 중입니다.

protected void TextBox_ShowEndOfLine(object sender, TextChangedEventArgs e) 
    { 
     if (sender == null) return; 
     var box = sender as TextBox; 

     if (!box.IsFocused && box.IsVisible) 
     { 
      IInputElement oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this)); 
      box.Focus(); 
      box.Select(box.Text.Length, 0); 
      box.Focus(); 

      // We wait for keyboard focus and regular focus before returning focus to the button 
      var thread = new Thread((ThreadStart)delegate 
             { 
              // wait till focused 
              while (true) 
              { 
               var focused = (bool)Dispatcher.Invoke(new Func<bool>(() => box.IsKeyboardFocusWithin && box.IsFocused && box.IsInputMethodEnabled), DispatcherPriority.Send); 
               if (!focused) 
                Thread.Sleep(1); 
               else 
                break; 
              } 

              // Focus the old element 
              Dispatcher.Invoke(new Action(() => oldFocus.Focus()), DispatcherPriority.SystemIdle); 
             }); 
      thread.Start(); 
     } 
     else if (!box.IsVisible) 
     { 
      // If the textbox is not visible, the cursor will not be moved to the end. Wait till it's visible. 
      var thread = new Thread((ThreadStart)delegate 
             { 
              while (true) 
              { 
               Thread.Sleep(10); 
               if (box.IsVisible) 
               { 
                Dispatcher.Invoke(new Action(delegate 
                        { 
                         box.Focus(); 
                         box.Select(box.Text.Length, 0); 
                         box.Focus(); 

                        }), DispatcherPriority.ApplicationIdle); 
                return; 
               } 
              } 
             }); 
      thread.Start(); 
     } 
    } 
1

당신이 올바른 궤도에있어, 문제는 .Focus() 전화가 막대기를 들어, 디스패처 기계가 나중에에 전화를 지연시킬 필요가있다.
대신 t를 사용하여 그는 DispatcherPriority 값이 (가장 큼)을 전송하십시오. Dispatcher를 사용하여 나중에 DispatcherPriority에서 포커스를 설정하십시오 (예 : 입력).

Dispatcher.BeginInvoke(DispatcherPriority.Input, 
new Action(delegate() { 
    oldFocus.Focus();   // Set Logical Focus 
    Keyboard.Focus(oldFocus); // Set Keyboard Focus 
})); 

알다시피, 저는 키보드 포커스도 설정하고 있습니다.
WPF는 여러 Focus Scopes를 가질 수 있으며 하나 이상의 요소에 논리적 포커스 (IsFocused = true)를 가질 수 있습니다. 그러나 하나만 요소는 키보드 포커스를 가질 수 있으며 키보드 입력을 수신합니다.

+0

응답 해 주셔서 감사합니다. 그래도 나는 편집에서 이미 그렇게하고 있다고 생각합니다. 선택 및 최고 수준 대리인 (보내기) 텍스트 상자에 포커스를 다음 포커스를 반환하려면 가장 낮은 수준의 대리자 (유휴) 호출합니다. 결과는 더 좋지 않습니다. – Skinner927

0

마지막으로, 나는 (하단에 전체 솔루션)이 문제에 대한 "올바른"해결책을 발견 : 사실

if (!tb.IsFocused) 
{ 
    tb.Dispatcher.BeginInvoke(new Action(() => 
     tb.ScrollToHorizontalOffset(1000.0)), DispatcherPriority.Input); 
} 

, 당신은 텍스트 상자를 집중하고 싶지 않아 -이 해킹이 필요했다 왜냐하면 TextBox.CaretIndex, TextBox.Select() 등은 TextBox에 포커스가 없으면 아무 것도하지 않기 때문입니다. Scroll 메서드 중 하나를 사용하는 대신 집중하지 않고 작동합니다. 정확히 double offset이 무엇이되어야하는지 모르겠다. (과도한 가치 인 1000.0을 사용했다.)값은 픽셀처럼 동작하므로 시나리오에 충분히 큰 값인지 확인하십시오.

다음으로 사용자가 키보드 입력을 사용하여 값을 편집 할 때이 동작을 트리거하지 않으려합니다. 보너스로, 한 줄의 TextBox가 가로로 스크롤하는 동안 여러 줄의 TextBox가 세로로 스크롤되는 곳에서 수직 및 수평 스크롤을 결합했습니다. 마지막으로이 속성을 연결된 속성/동작으로 다시 사용할 수 있습니다. 이 솔루션을 즐기시기 바랍니다 :

/// <summary>The attached dependency property.</summary> 
    public static readonly DependencyProperty AutoScrollToEndProperty = 
     DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(TextBoxBehavior), 
      new UIPropertyMetadata(false, AutoScrollToEndPropertyChanged)); 

    /// <summary>Gets the value.</summary> 
    /// <param name="obj">The object.</param> 
    /// <returns>The value.</returns> 
    public static bool GetAutoScrollToEnd(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(AutoScrollToEndProperty); 
    } 

    /// <summary>Enables automatic scrolling behavior, unless the <c>TextBox</c> has focus.</summary> 
    /// <param name="obj">The object.</param> 
    /// <param name="value">The value.</param> 
    public static void SetAutoScrollToEnd(DependencyObject obj, bool value) 
    { 
     obj.SetValue(AutoScrollToEndProperty, value); 
    } 

    private static void AutoScrollToEndPropertyChanged(DependencyObject dependencyObject, 
     DependencyPropertyChangedEventArgs e) 
    { 
     var textBox = dependencyObject as TextBox; 
     var newValue = (bool)e.NewValue; 
     if (textBox == null || (bool)e.OldValue == newValue) 
     { 
      return; 
     } 
     if (newValue) 
     { 
      textBox.TextChanged += AutoScrollToEnd_TextChanged; 
     } 
     else 
     { 
      textBox.TextChanged -= AutoScrollToEnd_TextChanged; 
     } 
    } 

    private static void AutoScrollToEnd_TextChanged(object sender, TextChangedEventArgs args) 
    { 
     var tb = (TextBox)sender; 
     if (tb.IsFocused) 
     { 
      return; 
     } 
     if (tb.LineCount > 1) // scroll to bottom 
     { 
      tb.ScrollToEnd(); 
     } 
     else // scroll horizontally (what about FlowDirection ??) 
     { 
      tb.Dispatcher.BeginInvoke(new Action(() => tb.ScrollToHorizontalOffset(1000.0)), DispatcherPriority.Input); 
     } 
    } 

XAML 사용 : xmlns:b는 해당 CLR-공간입니다

 <TextBox b:TextBoxBehavior.AutoScrollToEnd="True" 
       Text="{Binding Filename}"/> 

. 해피 코딩!