C# 切换两个WPF文本框和ScrollToEnd()的内容

C# 切换两个WPF文本框和ScrollToEnd()的内容,c#,wpf,C#,Wpf,我试图切换两个WPF文本框的文本,将插入符号设置到每个文本框的末尾,并将这些插入符号位置滚动到视图中(以便每个文本框的最后一个字符仍然可见) 这是该窗口的一个简单版本: <Window ...> <StackPanel> <TextBox x:Name="textBox1" Text="aaabbbcccd" Height="25" Width="100" Margin="2" /> <TextBox x:Name

我试图切换两个WPF文本框的文本,将插入符号设置到每个文本框的末尾,并将这些插入符号位置滚动到视图中(以便每个文本框的最后一个字符仍然可见)

这是该窗口的一个简单版本:

<Window ...>
    <StackPanel>
        <TextBox x:Name="textBox1" Text="aaabbbcccd" Height="25" Width="100" Margin="2" />
        <TextBox x:Name="textBox2" Text="aaaaabbbbbcccccd" Height="25" Width="100" Margin="2" />
        <Button Content="Switch text" Width="70" Margin="4" Click="OnClick_button_switchText" />
    </StackPanel>
</Window>
不幸的是,ScrollToEnd()没有按预期工作:什么也没有发生。因此,我还尝试了其他几种可能性——在我开始设定焦点之前,这些可能性也没有起到任何作用:

private void OnClick_button_switchText( object sender, RoutedEventArgs e )
{
    ...

    // 2nd try
    textBox1.Focus();
    textBox1.CaretIndex = textBox1.Text.Length;
    textBox2.Focus();
    textBox2.CaretIndex = textBox2.Text.Length;

    // 3rd try
    textBox1.Focus();
    EditingCommands.MoveToLineEnd.Execute( null, textBox1 );
    textBox2.Focus();
    EditingCommands.MoveToLineEnd.Execute( null, textBox2 );

    // 4th try
    textBox1.Focus();
    Rect a_charIndexRect = textBox1.GetRectFromCharacterIndex( textBox1.CaretIndex );
    textBox1.ScrollToHorizontalOffset( a_charIndexRect.Right );
    textBox2.Focus();
    a_charIndexRect = textBox2.GetRectFromCharacterIndex( textBox2.CaretIndex );
    textBox2.ScrollToHorizontalOffset( a_charIndexRect.Right );
}
如果焦点设置在实际滚动方法之前,它几乎起作用了(第二种和第三种可能性):textBox2正确显示文本,但textBox1仍然不会滚动到末尾-但它会将插入符号设置到末尾。
如果按tab键聚焦此控件,您会注意到光标位于末端,但不在视图中

所以我认为这可能是某种计时问题,并尝试在文本框的“GotFocus”事件中执行相应的scroll方法,但没有成功

有没有办法切换两个文本框的文本并仍然显示最后的字符?

任何帮助都将不胜感激。

经过几次尝试和Peter Duniho发布的提示,我找到了解决方法。
请注意,这仍然不是一个完美的解决方案,因为它不是MVVM方法,而是一种可行的方法。
如果要在滚动到末尾后聚焦其中一个文本框,则必须使用不同的方法:

private void OnClick_button_switchText( object sender, RoutedEventArgs e )
{
     // Switch text
     string text1  = textBox1.Text;
     textBox1.Text = textBox2.Text;
     textBox2.Text = text1;

     // Scroll to end - without focus
     textBox1.ScrollToHorizontalOffset( double.PositiveInfinity );
     textBox2.ScrollToHorizontalOffset( double.PositiveInfinity );

     // Scroll to end - and focus the first TextBox
     textBox1.Focus();
     EditingCommands.MoveToLineEnd.Execute( null, textBox1 );
     textBox2.ScrollToHorizontalOffset( double.PositiveInfinity );

     // Scroll to end - and focus the second TextBox
     textBox1.ScrollToHorizontalOffset( double.PositiveInfinity );
     textBox2.Focus();
     EditingCommands.MoveToLineEnd.Execute( null, textBox2 );
}
编辑:这是我的MVVM方法(我保留了现有的焦点)

行为:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace Behaviors
{
   public static class TextBoxScrollToEndBehavior
   {
      private static bool _isActive = false;
      public static bool IsActive
      {
         get
         {
            return _isActive;
         }
         set
         {
            _isActive = value;
         }
      }




      public static readonly DependencyProperty ScrollToEndOnEnabledBehavior = 
         DependencyProperty.RegisterAttached( "ScrollToEndOnEnabledBehavior",
                                              typeof( bool ),
                                              typeof( TextBoxScrollToEndBehavior ),
                                              new UIPropertyMetadata( false, OnScrollToEndOnEnabledBehavior ) );




      public static bool GetScrollToEndOnEnabledBehavior( DependencyObject obj )
      {
         return (bool)obj.GetValue( ScrollToEndOnEnabledBehavior );
      }




      public static void SetScrollToEndOnEnabledBehavior( DependencyObject obj, bool value )
      {
         obj.SetValue( ScrollToEndOnEnabledBehavior, value );
      }




      private static void OnScrollToEndOnEnabledBehavior( object sender, DependencyPropertyChangedEventArgs e )
      {
         TextBox textBox = sender as TextBox;
         if( textBox != null )
         {
            bool isEnabled = (bool)e.NewValue;
            if( isEnabled )
            {
               textBox.TextChanged += OnTextChanged_textBox;
            }
            else
            {
               textBox.TextChanged -= OnTextChanged_textBox;
            }
         }
      }




      static void OnTextChanged_textBox( object sender, TextChangedEventArgs e )
      {
         TextBox textBox = sender as TextBox;
         if( null != textBox )
         {
            if( IsActive )
            {
               if( textBox.IsFocused )
               {
                  EditingCommands.MoveToLineEnd.Execute( null, textBox );
               }
               else
               {
                  textBox.ScrollToHorizontalOffset( double.PositiveInfinity );
               }
            }
         }
      }
   }
}

“开始使用WPF,他们说会很有趣”。。。如果你对乐趣的想法是确切地知道WPF在幕后是如何工作的,然后坚持他们的设计理念,永远不要偏离它。您可能需要根据MVVM标准重新构建应用程序,以使其正常工作。但更严重的是。。。以编程方式更改WPF中的内容是不确定的,您可能还需要将键盘焦点设置在文本框上,然后滚动。你可能需要等待x帧之间做的事情。。。WPF从来就不是为了让ppl通过编程来做事情。“他们说这会很有趣,很可怕……”不幸的是,你的问题并不完全清楚你期望发生什么。但是,您的示例看起来不像一个多行文本框。如果您希望
文本框
视图水平滚动,则您的期望不正确。
ScrollToEnd()
方法仅垂直滚动到内容的最后一行。事实上,它会同时水平向左滚动(即开始)。也就是说,我希望在这种情况下,
ScrollToHorizontalOffset(double.PositiveInfinity)
能够正常工作。请澄清您实际期望发生的情况。@DanielMesSer:是的,WPF使一些困难的任务变得非常容易,但有些简单的任务却非常困难。。。我将研究它,并尝试找出一种MVVM方法来实现它。@PeterDuniho:这个问题与发布的XAML代码有关,所以我想像您假设的那样水平滚动到文本框的末尾。好的,这解释了为什么ScrollToEnd()完全失败。如果我根本不聚焦任何东西,那么使用ScrollToHorizontalOffset(double.PositiveInfinity)似乎是可行的。一旦文本框在此之后聚焦,插入符号将保持在左侧的起始位置。因此,如果你想在切换文本后在其中一个文本框上闪烁插入符号,它也会失败。@PeterDuniho:但将其与上面发布的第二个或第三个可能性相结合会起作用!我会发布一个答案,让大家都清楚。
<Window ...>
    <StackPanel>
        <TextBox x:Name="textBox1" Text="{Binding Text1}" behaviors:TextBoxScrollToEndBehavior.ScrollToEndOnEnabledBehavior="True" Height="25" Width="100" Margin="2"/>
        <TextBox x:Name="textBox2" Text="{Binding Text2}" behaviors:TextBoxScrollToEndBehavior.ScrollToEndOnEnabledBehavior="True" Height="25" Width="100" Margin="2"/>
        <Button Content="Switch text" Width="70" Margin="4" Command="{Binding CommandSwitchText}" />
    </StackPanel>
</Window>
using Behaviors;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

namespace ViewModels
{
   class MainWindowViewModel : ObservableObject
   {
      public MainWindowViewModel()
      {
         CommandSwitchText = new RelayCommand( SwitchText );
      }


      public RelayCommand CommandSwitchText
      {
         get;
         private set;
      }


      private void SwitchText()
      {
         TextBoxScrollToEndBehavior.IsActive = true;

         string textTemp = Text1;
         Text1 = Text2;
         Text2 = textTemp;

         TextBoxScrollToEndBehavior.IsActive = false;
      }


      private string _text1 = "aaabbbcccd";
      public string Text1
      {
         get
         {
            return _text1;
         }
         set
         {
            _text1 = value;
            RaisePropertyChanged();
         }
      }


      private string _text2 = "aaaaabbbbbcccccd";
      public string Text2
      {
         get
         {
            return _text2;
         }
         set
         {
            _text2 = value;
            RaisePropertyChanged();
         }
      }
   }
}
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace Behaviors
{
   public static class TextBoxScrollToEndBehavior
   {
      private static bool _isActive = false;
      public static bool IsActive
      {
         get
         {
            return _isActive;
         }
         set
         {
            _isActive = value;
         }
      }




      public static readonly DependencyProperty ScrollToEndOnEnabledBehavior = 
         DependencyProperty.RegisterAttached( "ScrollToEndOnEnabledBehavior",
                                              typeof( bool ),
                                              typeof( TextBoxScrollToEndBehavior ),
                                              new UIPropertyMetadata( false, OnScrollToEndOnEnabledBehavior ) );




      public static bool GetScrollToEndOnEnabledBehavior( DependencyObject obj )
      {
         return (bool)obj.GetValue( ScrollToEndOnEnabledBehavior );
      }




      public static void SetScrollToEndOnEnabledBehavior( DependencyObject obj, bool value )
      {
         obj.SetValue( ScrollToEndOnEnabledBehavior, value );
      }




      private static void OnScrollToEndOnEnabledBehavior( object sender, DependencyPropertyChangedEventArgs e )
      {
         TextBox textBox = sender as TextBox;
         if( textBox != null )
         {
            bool isEnabled = (bool)e.NewValue;
            if( isEnabled )
            {
               textBox.TextChanged += OnTextChanged_textBox;
            }
            else
            {
               textBox.TextChanged -= OnTextChanged_textBox;
            }
         }
      }




      static void OnTextChanged_textBox( object sender, TextChangedEventArgs e )
      {
         TextBox textBox = sender as TextBox;
         if( null != textBox )
         {
            if( IsActive )
            {
               if( textBox.IsFocused )
               {
                  EditingCommands.MoveToLineEnd.Execute( null, textBox );
               }
               else
               {
                  textBox.ScrollToHorizontalOffset( double.PositiveInfinity );
               }
            }
         }
      }
   }
}