介绍 在Windows应用程序中,改进用户交互的一个众所周知的方法是在按钮中放置一个图像,当操作比“提交”操作更抽象时,可以将它们执行的操作可视化。最简单的方法是使用一个简单的位图图像作为按钮UIElement的背景刷。但是,有时按钮的单击动画的默认行为是不可取的。在本文中,我想做的是概述一个可重用的矩形,以模拟按钮的一般行为,但使用简单的图像转换,并显示在网格“快速栏”中。 简单地说,我开始创建一个控件,它将闪现一个图像底片,这样用户就知道他们没有使用按钮就点击了它。 在启动图像缓存 为了在显示动画时消除任何延迟,我发现最好在应用程序启动时将所有图像加载到一个字典集合中。 这些是我在制作这个的时候用到的图片。 我在项目中添加了这些图片作为资源。两者都是29x29像素。 隐藏,复制Code
// the image dictionary private Dictionary<string, ImageSource> _imageCache; // helper function to convert resource bitmaps into a BitmapSource // since resource images are loaded as plain bitmaps private BitmapSource CreateSourceFromBitmap(System.Drawing.Bitmap bitmap) { return System.Windows.Interop.Imaging.CreateBitmapSourceFrom HBitmap( bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions() ); } // loading the images into memory private void LoadImages() { _imageCache = new Dictionary<string, ImageSource>(); _imageCache.Add("Search", CreateSourceFromBitmap(Properties.Resources.Search)); _imageCache.Add("SearchInverted", CreateSourceFromBitmap(Properties.Resources.SearchInverted)); }
有了这个,我们的图像是预先加载和准备去做一个快速过渡动画。 的QuickBar QuickBar是一个简单的网格,用于包含按钮。我在网格中定义了按钮的大部分属性,以获得统一的外观。此外,所有按钮事件都连接到所有按钮的单个事件。我将很快讨论如何处理这个问题。我选择了这个方法,这样每个按钮的事件处理程序都是不必要的。 隐藏,收缩,复制Code
<Gridx:Name="_grid_Content_QuickBar"> <Grid.ColumnDefinitions> <ColumnDefinitionWidth="38"> <!-- Add more columns for more buttons --> <ColumnDefinitionWidth="1*"> </Grid.ColumnDefinitions> <Grid.Resources> <StyleTargetType="Rectangle"> <SetterProperty="VerticalAlignment"Value="Center"/> <SetterProperty="HorizontalAlignment"Value="Center"/> <SetterProperty="Margin"Value="3"/> <SetterProperty="Width"Value="32"/> <SetterProperty="Height"Value="32"/> <SetterProperty="RadiusX"Value="2"/> <SetterProperty="RadiusY"Value="2"/> <SetterProperty="Stroke"Value="#858585"/> <SetterProperty="StrokeThickness"Value="1.5"/> <EventSetterEvent="MouseEnter"Handler="QuickMenu_MouseEnter"/> <EventSetterEvent="MouseLeave"Handler="QuickMenu_MouseLeave"/> <EventSetterEvent="MouseDown"Handler="QuickMenu_MouseClick"/> </Style> </Grid.Resources> <RectangleGrid.Column="0"ToolTip="Launch new search"x:Name="rect_Search"> <Rectangle.Fill> <ImageBrushImageSource=".ResourcesSearch.png"Stretch="Uniform"/> </Rectangle.Fill> </Rectangle> </Grid>
连接事件 由于我决定通过单个事件处理程序来处理所有事件,因此需要一些代码来处理它们。虽然我使用了在单个处理程序中处理MouseDown事件的方法,但将该事件处理程序添加到每个按钮也同样容易。 隐藏,收缩,复制Code
// event function mapper private Dictionary<string, Action> _buttonEventHandlers; // a method to register events. Called during app initialization private void RegisterEventHandlers() { _buttonEventHandlers = new Dictionary<string, Action>(); _buttonEventHandlers.Add(rect_Search.Name, SearchClick); } // button event handlers // this one changes the button outline to highlight which button the mouse is over private void QuickMenu_MouseEnter(object sender, RoutedEventArgs e) { Rectangle rect = sender as Rectangle; if (rect != null) { rest.Stroke = Brushes.Thistle; // change the color of the rectangle border on mouseover } } // this one removes the highlight when the mouse leaves the button private void QuickMenu_MouseLeave(object sender, RoutedEventArgs e) { Rectangle rect = sender as Rectangle; if (rect != null) { rect.Stroke = new SolidColorBrush (Color.FromArgb(255, 85, 85, 85); // change the color back to the original on mouseleave } } // this one handles the user clicks private void QuickMenu_MouseClick(object sender, MouseButtonEventArgs e) { Rectangle rect = sender as Rectangle; Action action; if (rect != null) { action = _buttonEventHandlers[rect.Name]; action?.Invoke(); } }
处理MouseLeave事件的更好方法是在启动代码中定义一个默认的SolidColorBrush,以节省一些性能,而不是每次单击都创建一个新的画笔。 动画按钮 为了确保每个按钮正确地处理其图像,我添加了这段代码,这段代码将在映射到按钮的单击事件的Action函数中调用。动画是通过ObjectAnimationUsingKeyFrames类完成的。使用关键帧的objectanimationusingkeyframe与其他关键帧动画类的区别在于没有帧之间的过渡平滑。 隐藏,收缩,复制Code
// the animation code private void QuickButtonClicked(Rectange rect, ImageSource originalImg, ImageSource alternateImg) { ObjectAnimationUsingKeyFrames animation = new ObjectAnimationUsingKeyFrames() { AutoReverse = true, Duration = new Duration(TimeSpan.FromSeconds(0.125)), // duration is very important here RepeatBehavior = new RepeatBehavior(2D) // flash twice }; var startFrame = new DiscreteObjectKeyFrame(); startFrame.Value = originalImg; startFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.0)); animation.KeyFrames.Add(startFrame); var altFrame = new DiscreteObjectKeyFrame(); altFrame.Value = alternateImg; altFrame.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.0625)); animation.KeyFrame.Add(altFrame); rect.Fill.BeginAnimation(ImageBrush.ImageSourceProperty, animation); } // the button click handler private void SearchClick() { QuickButtonClicked(rect_Search, _imageCache["Search"], _imageCache["SearchInverted"]); // ToDo: respond to the click }
在这里,我使用离散objectkeyframe类定义动画的每一帧。因为只有两个框架,所以很简单。有两个重要的部分,一个是帧值,它被设置为将要显示的图像,另一个是KeyTime,它指示在动画时间轴中何时移动到下一帧。由于只有两帧,我将第二个键时间设置为持续时间的一半。 值得注意的是,虽然我使用了一个简单的动画在两个图像之间快速切换以响应鼠标点击,但在使用具有几十个关键帧的更复杂的图像动画时应该没有什么困难。 总之 为了创造更好的用户体验,我还在学习WPF动画的强大功能。由于我在理解这个问题上有些困难,所以我想分享一下我学到的经验。 历史 10/11/2016:最初的帖子 本文转载于:http://www.diyabc.com/frontweb/news517.html