微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

[WP7] How to press the mouse on a control, and detect MouseLeftButtonUp on another


Another issue I found on StackOverflow,which is way more tricky that it seems.

Let’s say we have a Silverlight WP7 application,and we want to add a drag&drop scenario. The user first taps an element,drags his finger to another,and raises his finger on another element. Easy enough! Just handle the MouseLeftButtonDown on each element,store which element triggered the event in a property,handle the MouseLeftButtonUp on each element,and then we have the originator and the destination!

… right?

Well,so I would have thought.

Unfortunately,the MouseLeftButtonUp event will only be triggered if the ‘mouse’ left button (or your finger,sir Claus Jørgensen ;o) )1 is released on the very same control that it was pressed on.

So,if we use this XAML:

   1: <Grid x:Name="ContentPanel"
   2:       Grid.Row="1"
   3:       Margin="12,12,0"
   4:       MouseLeftButtonUp="ContentPanel_MouseLeftButtonUp">
   5:     Grid.RowDeFinitions>
   6:         RowDeFinition />
   7:         />
   8:     </   9:     ="g1"
  10:           Background="Green"
  11:           MouseLeftButtonDown="Grid_MouseLeftButtonDown"
  12:           ="Grid_MouseLeftButtonUp"
  13:           Tag="dragdrop"   14:     ="g2"
  15:           ="1"
  16:           ="Blue"
  17:             18:             19:             20:  
  21: Grid>

The layout looks like:

image

In the MouseLeftButtonUp event handler,we want to paint the destination grid:

private void Grid_MouseLeftButtonUp(object sender,MouseButtonEventArgs e)
   2: {
   3:     var grid = (Grid)sender;
   4:     
   5:     grid.Background = new SolidColorBrush(Colors.Red);
   6: }

I already encountered a similar issue with Silverlight a few years ago,so I knew about the CaptureMouse method. What is it? Basically it tells a control to keep track of the mouse even if events are triggered outside of the control bounds. Let’s try to use it.

In the MouseLeftButtonDown,simply capture the mouse:

void Grid_MouseLeftButtonDown( 3: ((UIElement)sender).CaptureMouse();
   4: }

Now the MouseLeftButtonUp event is triggered! Unfortunately,it’s triggered on the control we called CaptureMouse on,so we still don’t kNow on which control the finger was when released. But we have the mouse coordinates,so we should be able to find it somehow.

And that ‘somehow’ is the ‘VisualTreeHelper.FindElementsInHostCoordinates’ method. It takes a point and a control,and enumerates all the control’s child that are located at the specified coordinates. Sounds good enough.

So Now let’s rewrite our MouseLeftButtonUp event handler,without forgetting to release the mouse capture:

5: grid.ReleaseMouseCapture();
   6:  
   7:     var mouseUpGrid = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this),this.ContentPanel)
   8:         .OfType<Grid>()
   9:         .FirstOrDefault();
  10:  
  11:     if (mouseUpGrid != null)
  12:     {
  13:         Debug.WriteLine("MouseUp in " + mouseUpGrid.Name);
  14:         mouseUpGrid.Background = new SolidColorBrush(Colors.Red);
  15:     }
  16: }

Test on the emulator,and… it works!

Now let’s imagine a more complex scenario:

ottom-color:silver; border-bottom-width:1px; border-bottom-style:solid; text-align:left; border-left-color:silver; border-left-width:1px; border-left-style:solid; padding-bottom:4px; line-height:12pt; background-color:rgb(244,0" 4: 5: 7: ="g1"
   9:           ="Green"
="Grid_MouseLeftButtonDown"
="Grid_MouseLeftButtonUp"   12:     ="DummyGrid"
  14:           ="Gray"  15:         ="g2"
  16:               ="100 5 5 5"
  17:               ="Blue"
  18:                 19:                 20:     

image

We only want to be able to drag from the green grid to the blue one (and the other way around). Unfortunately,using the prevIoUs code,the drag&drop will be detected even if we lift our finger on the gray grid. So we need a way to opt-in to the drag&drop detection,rather than detect it on all the grids.

The dirty way would be to store the name of the blue and green grids,and check the name in the MouseLeftButtonDown event. But we can make something more generic using the Tag property.

What is Tag? It’s an object property available on every control. What is stored in this property? nothing. It’s meant to be used by you,and only by you,to store whichever object you want to personalize the control.

In our XAML,let’s add the “dragdrop” string in the Tag of the green and blue grids:

="Grid_MouseLeftButtonUp"
  13:     ="DummyGrid"
  16:         ="100 5 5 5"
  20:                 21:                 22:       23: Then,in the MouseLeftButtonUp event handler,it’s only a matter of filtering which controls have the appropriate tag:

   2: {
   4:     
   6:  
   8:         .OfType<Grid>()
   9:         .FirstOrDefault(element => element.Tag is string && (string)element.Tag == "dragdrop");
  10:  
  12:     {
new SolidColorBrush(Colors.Red);
Now the gray grid is excluded,as expected.

And that’s how a seemingly easy problem turns out on a solution requiring the use of Tag,VisualTreeHelper.FindElementsInHostCoordinates,and CaptureMouse. Quite instructive if you ask me.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐