WPF 滚动选取框 - HandOffBehavior

Posted

技术标签:

【中文标题】WPF 滚动选取框 - HandOffBehavior【英文标题】:WPF scrolling marquee - HandOffBehavior 【发布时间】:2015-11-22 08:30:49 【问题描述】:

我试图弄清楚如何最好地使用 WPF 的 HandOffBehavior 技术。现在,我的应用程序中有一个滚动错误选取框,它会动画打开、滚动错误消息,然后自行关闭。

这周我刚刚开始构建这个 WPF 应用程序。问题是我似乎无法优雅地为这样的场景做准备,比如当用户在很小的连续间隔内单击登录按钮两次或更多次时。我希望看到错误字幕仅在用户使用登录按钮提交此类行为而不是多次重复打开和关闭字幕等时继续在屏幕上发送错误文本。这是相关代码。

MainWindow.xaml.cs:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.Entity;
using System.Windows.Media.Animation;
using PasswordHash;

namespace ChatClient

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();
        

        private void logIn(object sender, RoutedEventArgs e)
        
            string nameRecord = "";
            string passRecord = "";

            if (UsernameField.Text == "" || UserPassField.Password == "")
            
                openErrorMarquee("Username and password required");
            
            else
            
                using (otongadgethubEntities logCheck = new otongadgethubEntities())
                
                    var userNullCheck = logCheck.users.FirstOrDefault(a => a.username == UsernameField.Text);
                    if (userNullCheck == null)
                    
                        openErrorMarquee("Username does not exist");
                    

                    if (userNullCheck != null)
                    
                        nameRecord = userNullCheck.username;
                    

                    if (nameRecord == UsernameField.Text)
                    
                        passRecord = Encrypt.MD5(UserPassField.Password).ToLower();
                        if (passRecord == userNullCheck.password)
                        
                            //Yay! User logged in!
                        
                        else
                        
                            openErrorMarquee("Password invalid");
                        
                    
                
            
        

        private void openErrorMarquee(string errorMessage)
        
            errorMarquee.Visibility = System.Windows.Visibility.Visible;

            DoubleAnimation openMarquee = new DoubleAnimation();
            openMarquee.From = 0;
            openMarquee.To = 17;
            openMarquee.Duration = new Duration(TimeSpan.FromSeconds(1.0));
            openMarquee.Completed += (s, doneEvent) => errorMarqueeScroll(errorMessage);
            errorMarquee.BeginAnimation(Rectangle.HeightProperty, openMarquee, HandoffBehavior.Compose);
        

        private void errorMarqueeScroll(string errorMessage)
        
            errorText.Text = errorMessage;
            errorText.Visibility = System.Windows.Visibility.Visible;

            double height = errorCanvas.ActualHeight - errorText.ActualHeight;
            errorText.Margin = new Thickness(0, height / 2, 0, 0);
            DoubleAnimation doubleErrorAnimation = new DoubleAnimation();
            doubleErrorAnimation.From = -errorText.ActualWidth;
            doubleErrorAnimation.To = errorCanvas.ActualWidth;

            //doubleErrorAnimation.RepeatBehavior = RepeatBehavior.Forever;
            doubleErrorAnimation.Completed += new EventHandler(closeErrorMarquee);
            doubleErrorAnimation.Duration = new Duration(TimeSpan.FromSeconds(7.0));
            errorText.BeginAnimation(Canvas.RightProperty, doubleErrorAnimation);
        

        private void closeErrorMarquee(object sender, EventArgs e)
        
            DoubleAnimation closeMarquee = new DoubleAnimation();
            closeMarquee.From = 17;
            closeMarquee.To = 0;
            closeMarquee.Duration = new Duration(TimeSpan.FromSeconds(1.0));
            closeMarquee.Completed += (s, doneEvent) => 
                errorMarquee.Visibility = System.Windows.Visibility.Hidden;
                errorText.Visibility = System.Windows.Visibility.Hidden;
            ;
            errorMarquee.BeginAnimation(Rectangle.HeightProperty, closeMarquee, HandoffBehavior.Compose);
        
    

对于那些也需要查看窗口的人来说,这里是 XAML:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="ChatClient.MainWindow"
        Title="MainWindow" Height="350" Width="525" Icon="media/favicon.gif" Background="#FF3C3636" Foreground="x:Null">
    <Window.Resources>
        <Storyboard x:Key="logolayer2excompsoundfad_mp4"/>
    </Window.Resources>
    <Window.BorderBrush>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="Black" Offset="0"/>
            <GradientStop Color="#FF6F6D95" Offset="1"/>
        </LinearGradientBrush>
    </Window.BorderBrush>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="StaticResource logolayer2excompsoundfad_mp4"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid>
        <Rectangle x:Name="Menu" Fill="#755E5E83" HorizontalAlignment="Left" Height="273" Margin="35,23,0,0" Stroke="Black" VerticalAlignment="Top" Width="446" RadiusY="27.5" RadiusX="27.5"/>
        <Button Content="Log In" HorizontalAlignment="Left" Height="80" Margin="162,200,0,0" Style="DynamicResource OTonButtonStyle1" VerticalAlignment="Top" Width="187" FontFamily="Impact" FontSize="26.667" Foreground="#FF1C045B" Click="logIn"/>
        <TextBox x:Name="UsernameField" HorizontalAlignment="Left" Height="25" Margin="204,57,0,0" TextWrapping="Wrap" Text="[Username]" VerticalAlignment="Top" Width="193" Background="#BD251E1E" UseLayoutRounding="False" FontFamily="Copperplate Gothic Light" FontSize="16">
            <TextBox.Foreground>
                <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                    <GradientStop Color="#FF1E2E95" Offset="0.5"/>
                    <GradientStop Color="White" Offset="1"/>
                </LinearGradientBrush>
            </TextBox.Foreground>
        </TextBox>
        <TextBlock HorizontalAlignment="Left" Height="16" Margin="98,57,0,0" TextWrapping="Wrap" Text="Username:" VerticalAlignment="Top" Width="101" FontFamily="Copperplate Gothic Light" FontSize="16">
            <TextBlock.Foreground>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FF0A1D5F" Offset="0.374"/>
                    <GradientStop Color="#FF6E7FB9" Offset="1"/>
                </LinearGradientBrush>
            </TextBlock.Foreground>
        </TextBlock>
        <PasswordBox x:Name="UserPassField" HorizontalAlignment="Left" Height="25" Margin="204,99,0,0" VerticalAlignment="Top" Width="193" Background="#BD251E1E" UseLayoutRounding="False" FontFamily="Copperplate Gothic Light" FontSize="16">
            <PasswordBox.Foreground>
                <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
                    <GradientStop Color="#FF1E2E95" Offset="0.5"/>
                    <GradientStop Color="White" Offset="1"/>
                </LinearGradientBrush>
            </PasswordBox.Foreground>
        </PasswordBox>
        <TextBlock HorizontalAlignment="Left" Height="16" Margin="98,99,0,0" TextWrapping="Wrap" Text="Password:" VerticalAlignment="Top" Width="101" FontFamily="Copperplate Gothic Light" FontSize="16">
            <TextBlock.Foreground>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FF0A1D5F" Offset="0.374"/>
                    <GradientStop Color="#FF6E7FB9" Offset="1"/>
                </LinearGradientBrush>
            </TextBlock.Foreground>
        </TextBlock>
        <Canvas ClipToBounds="True" Name="errorCanvas" Width="446" Height="17" Margin="36,152,35,151">
            <Rectangle x:Name="errorMarquee" Fill="#FF0A0A0C" HorizontalAlignment="Left" Height="17" Stroke="#FF5B1D1D" VerticalAlignment="Top" Width="446" Canvas.Left="-1" Visibility="Hidden"/>
            <TextBlock x:Name="errorText" HorizontalAlignment="Left" Height="16" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="690" FontFamily="Copperplate Gothic Bold" FontSize="16" Foreground="#FF7E0202" Visibility="Hidden"/>
        </Canvas>
    </Grid>
</Window>

大家有什么建议吗?

【问题讨论】:

【参考方案1】:

你可以只用一个标志来检查这个

private static flag = true;
private void logIn(object sender, RoutedEventArgs e)
    
        if(flag)
        flag=false;
        string nameRecord = "";
        string passRecord = "";

        if (UsernameField.Text == "" || UserPassField.Password == "")
        
            openErrorMarquee("Username and password required");
        
        else
        
            using (otongadgethubEntities logCheck = new otongadgethubEntities())
            
                var userNullCheck = logCheck.users.FirstOrDefault(a => a.username == UsernameField.Text);
                if (userNullCheck == null)
                
                    openErrorMarquee("Username does not exist");
                

                if (userNullCheck != null)
                
                    nameRecord = userNullCheck.username;
                

                if (nameRecord == UsernameField.Text)
                
                    passRecord = Encrypt.MD5(UserPassField.Password).ToLower();
                    if (passRecord == userNullCheck.password)
                    
                        //Yay! User logged in!
                    
                    else
                    
                        openErrorMarquee("Password invalid");
                    
                
            
        
      
      else
      
      openErrorMarquee("you pressed more one time");
       
     

【讨论】:

感谢您的回复。我不得不说,由于事件的结构(尤其是当另一个动画完成时调用时,打开选取框矩形,关闭它等),这并不能充分解决问题。它只会导致所有事件再次触发,然后同样的问题仍然会持续存在 - 比应有的更早关闭,即使我只希望它“打开”一次以滚动错误消息等,也会再次打开。我是尝试修复此 UI 中的中断。 如果我以超快的速度按三下登录按钮,整个 errorMarquee 动画行为及其 errorText 将变得混乱,最终 errorText 会隐藏一段时间,字幕打开和打开等.【参考方案2】:

您可以使用秒表检查快速点击。在下面给出的代码示例中,尝试缓慢和快速地按下按钮。

    Stopwatch w = new Stopwatch();
    long milliseconds_prev = 0;

    private void Button_Click(object sender, RoutedEventArgs e)
    
        if (_checkFastClicks())
        
            Debug.WriteLine("Fast click");
        
        else
            Debug.WriteLine("Slow click");
    

    bool _checkFastClicks()
    
        bool result = false;

        if (!w.IsRunning)
        
            w.Start();
        
        if (w.IsRunning)
        
            if (w.ElapsedMilliseconds - milliseconds_prev < 500)//imp
            
                if (w.ElapsedMilliseconds > 0)
                
                    milliseconds_prev = w.ElapsedMilliseconds;
                    result = true;
                
                else
                    result = false;
            
            else
            
                milliseconds_prev = 0;
                w.Reset();
                w.Stop();
                result = false;
            
        

        return result;
    

【讨论】:

有趣的想法。稍后我将不得不尝试,也许当我们到达丹佛机场时。谢谢!如果我发现了什么,我会告诉你的

以上是关于WPF 滚动选取框 - HandOffBehavior的主要内容,如果未能解决你的问题,请参考以下文章

使用 WPF 平滑文本动画(选取框)

其他元素后面的 Wpf 文本选取框

如何在 WPF 中制作选取框进度条?

Android Widget:Textview 在主页滚动时停止选取框

WPF 文本框和滚动行为

WPF列表框在拖动时自动滚动