本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Behavior)来封装事件。
Windows中的事件通过消息机制来完成,也就是Windows系统来捕获用户输入(如鼠标点击、键盘输入),然后Windows发送一个消息给应用程序,应用程序进行具体的处理。在Winform中,窗体中每个控件都是有独立的句柄,也就是每个控件都可以收到Windows系统传来的消息,但是在WPF中,窗体中的控件是没有句柄的,所以只能是窗体进行消息捕获,WPF框架经过处理再传递给相应的控件。这是WPF和Winform在事件处理上的不同之处。
常用的鼠标事件包括:
MouseEnter、MouseLeave、MouseDown、MouseUp、MouseMove、MouseLeftButtonDown、MouseLeftButtonUp、MouseRightButtonDown、MouseRightButtonUp、MouseDoubleClick
值得注意的是诸如Button一类的控件,具有Click事件,它其实是仍然是调用了MouseLeftButtonDown等底层事件,然后进行截断,也就是说Button控件只能调用Click事件而不能调用MouseLeftButtonDown事件,因为在Click事件中,调用了MouseLeftButtonDown事件,而且应用了e.Handled = true;阻止事件向下传下去。如果要在Click事件之前进行事件处理,则可以使用PreviewMouseLeftButtonDown事件。
用的最多的键盘输入事件有:
KeyDown、KeyUp、TextInput
如果要对某个键进行处理则可以
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
//e.Handled = true;//表示已经处理完成
//具体逻辑
}
}
注意TextBox是不能捕获到TextInput事件的,只能捕获到KeyDown、TextChanged等事件,但也可以捕获到PreviewTextInput事件,事件捕获顺序是KeyDown-PreviewTextInput-TextChanged。
实现方式1:可以在TextBox上增加KeyDown事件,捕获Key.Enter键。
实现方式2:增加一个Button按钮,设置<Button Content="搜索" IsDefault="True"/>
拖拽事件包括:Drop、DragLeave、DragOver、DragEnter事件

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="panel" Background="#F7F9FA">
<Border Background="Orange" Height="30" Width="30" MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
</StackPanel>
<!--必须设置Background,否则默认为null,null是没有背景和Transparent不同-->
<!--AllowDrop="True"必须设置-->
<Canvas x:Name="canvas" Grid.Column="1" Drop="Canvas_Drop" AllowDrop="True"
Background="Transparent">
</Canvas>
</Grid>
private void Canvas_Drop(object sender, DragEventArgs e)
{
var data = e.Data.GetData(typeof(Border));
//canvas.Children.Add(data);//直接这样不可以,因为同一个实例不允许在于两个容器中
//先在之前的容器中移除,再添加
panel.Children.Remove(data as UIElement);
canvas.Children.Add(data as UIElement);
//获得鼠标相对于canvas的位置
var point = e.GetPosition((Canvas)sender);
Canvas.SetLeft(data as UIElement, point.X);
Canvas.SetTop(data as UIElement, point.Y);
}
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Border border = sender as Border;
DragDrop.DoDragDrop(border, border, DragDropEffects.Copy);
}
通过一个案例来讲解
案例,实现某个控件的随意拖动

主要是通过MouseLeftButtonDown、MouseLeftButtonUp和MouseMove三个事件来实现
<Canvas>
<Border Background="Orange" Width="100" Height="50" Canvas.Left="100" Canvas.Top="100"
MouseLeftButtonDown="Border_MouseLeftButtonDown"
MouseLeftButtonUp="Border_MouseLeftButtonUp"
MouseMove="Border_MouseMove"
/>
</Canvas>
private Canvas _parentCanvas = null;
private bool _isDragging = false;
private Point _mouseCurrentPoint;
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//获得承载Border的父对象
if (_parentCanvas == null)
_parentCanvas = (Canvas)VisualTreeHelper.GetParent(sender as Border);
this._isDragging = true;
//获得相对于border的坐标
this._mouseCurrentPoint = e.GetPosition(sender as Border);
//关键,锁定鼠标即不让鼠标选中其他元素
(sender as Border).CaptureMouse();
}
private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isDragging)
{
//关键,取消锁定鼠标
(sender as Border).ReleaseMouseCapture();
_isDragging = false;
}
}
private void Border_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging)
{
//获得相对于Canvas的坐标
Point point = e.GetPosition(_parentCanvas);
(sender as Border).SetValue(Canvas.TopProperty, point.Y - _mouseCurrentPoint.Y);
(sender as Border).SetValue(Canvas.LeftProperty, point.X - _mouseCurrentPoint.X);
}
}
关键点:
在进行移动的时候,一定要锁定鼠标,也就是不让鼠标可以选中其他元素,如果不锁定会出现以下情况:
情况1:如果鼠标移动过快,会出现图形不能跟随的情况

情况2:如果有多个元素,会出现选中其他元素的情况
下图演示中,鼠标箭头未松开

锁定鼠标有两种方式
(sender as Border).CaptureMouse()//锁定
(sender as Border).ReleaseMouseCapture();//解锁
System.Windows.Input.Mouse.Capture(sender as Border);//锁定
System.Windows.Input.Mouse.Capture(null);//解锁
上文中主要是通过MouseLeftButtonDown、MouseLeftButtonUp和MouseMove三个事件来实现,在XAML中需要对这三个事件进行绑定。行为则可以将这三个事件封装在一起。
Microsoft.Xaml.Behaviors.Wpf,FrameWork版本安装System.Windows.Interactivity.WPFBehavior<T>,类中重写OnAttached()和OnDetaching()方法。OnAttached()表示当挂载到对应的对象上的时候触发
OnDetaching()在对象销毁时触发
public class DragMoveBehavior : Behavior<Border>
{
// 当挂载到对应的对象上的时候触发
protected override void OnAttached()
{
base.OnAttached();
//方法与上面相同
//this.AssociatedObject表示关联的对象
this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
}
private Canvas _parentCanvas = null;
private bool _isDragging = false;
private Point _mouseCurrentPoint;
private void AssociatedObject_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (_isDragging)
{
// 相对于Canvas的坐标
Point point = e.GetPosition(_parentCanvas);
// 设置最新坐标
this.AssociatedObject.SetValue(Canvas.TopProperty, point.Y - _mouseCurrentPoint.Y);
this.AssociatedObject.SetValue(Canvas.LeftProperty, point.X - _mouseCurrentPoint.X);
}
}
private void AssociatedObject_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (_isDragging)
{
// 释放鼠标锁定
//this.AssociatedObject.ReleaseMouseCapture();
System.Windows.Input.Mouse.Capture(null);
_isDragging = false;
}
}
private void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this._isDragging = true;
// Canvas
if (_parentCanvas == null)
_parentCanvas = (Canvas)VisualTreeHelper.GetParent(sender as Border);
// 当前鼠标坐标
this._mouseCurrentPoint = e.GetPosition(sender as Border);
// 鼠标锁定
//this.AssociatedObject.CaptureMouse();
System.Windows.Input.Mouse.Capture(this.AssociatedObject);
}
// 对象销毁
protected override void OnDetaching()
{
this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
}
}
<Canvas>
<Border Background="Orange" Width="100" Height="50" Canvas.Left="100" Canvas.Top="100">
<i:Interaction.Behaviors>
<local:DragMoveBehavior/>
</i:Interaction.Behaviors>
</Border>
</Canvas>
我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。
假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje
这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什
我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果