Archive

Posts Tagged ‘Mouse’

Fix: Hypelinks not clickable in RichTextBox under Silverlight

Just fixed a nasty bug in ClipFlair Studio (http://studio.clipflair.net), where one couldn’t click hyperlinks in the Text component when set at ReadOnly mode. In that mode hyperlinks should open up new web pages (in Edit mode you can edit/remove them only of course), but instead when clicked they would show something like a focus rectangle (which they normally never show).

image

The situation was hard to debug since it wasn’t obvious what had caused the issue (it was working some time ago). I eventually found out that when the RichTextBox in Silverlight (may occur in WPF and WinRT too, haven’t tried) has a Transparent background, then Hyperlinks in it let MouseLeftButtonDown events pass through, so OnMouseLeftButtonDown will fire at their visual parent (or other visual ancestor) if no component in the visual chain marks the event as handled (such events bubble up towards the top of the visual hierarchy/containment chain).

This shouldn’t be much of a problem, if there wasn’t another issue, where if the ancestor called CaptureMouse in their OnMouseLeftButtonDown overriden method (from Control class), which is usual in mouse dragging code (in my case it was a FloatingWindow [TextWindow] that was the visual ancestor of RichTextBox), the hyperlink fails to fire when clicked and shows that weird solid-line border arround it instead.

The fix was easy once I knew what was happening, I attached a MouseLeftButtonDown event handler to the RichTextBox (if one was subclassing it [assuming it allows to do so] they could also have opted to add an overriden OnMouseLeftButtonDown method) that sets Handled property of the event parameter to true to consume it. The fix is available at CodePlex.

One can verify that the fix works now by downloading a sample ClipFlair Activity from https://www.dropbox.com/s/1zr36190xb0m6vk/Test_Text_URLs.clipflair and opening it in ClipFlair Studio. Can also download and build/run the source code of the previous broken version 1faaa8b35749 and test with that save activity file to see that the URLs didn’t open before when clicked but showed a rectangle arround them intead.

image

HowTo: Drop files onto Silverlight controls

I was recently adding drop-files support to ClipFlair Studio, so I had to do some research on the related API that is available to Silverlight apps.

Silverlight supports a limited set of Drag-and-Drop interaction with the operating system (supposedly for security reasons, but most probably because of the classic cross-platform implementation pains).

For example it allows you to drop a collections of file objects from the operating system’s file explorer (or other similar application) onto a Silverlight drop target, but not the other way around.

Also, it doesn’t allow dropping other flavors of content, like text, images etc., only collections of file objects.

To allow dropping files onto a Silverlight control you set AllowDrop property to true and handle the Drop and optionally the DragEnter/DragOver/DragLeave events to provide visual feedback during the drop operation (mostly when over the drop target, unless you do Mouse Capturing).

<RichTextBox
  x:Name="rtb"
  …
  AllowDrop="True" 
  Drop="rtb_Drop"
  DragEnter="rtb_DragEnter"
  DragOver="rtb_DragOver"
  DragLeave="rtb_DragLeave"

  />

 

  #region DragAndDrop

    private void rtb_Drop(object sender, System.Windows.DragEventArgs e)
    {
      VisualStateManager.GoToState(this, "Normal", true);

      //the Drop event passes in an array of FileInfo objects for the list of files that were selected and drag-dropped onto the RichTextBox.
      if (e.Data == null)
        return;

     IDataObject f = e.Data as IDataObject;
      if (f != null) //checks if the dropped objects are files

      {
        object data = f.GetData(DataFormats.FileDrop); //Silverlight only supports FileDrop
        FileInfo[] files = data as FileInfo[]; //…GetData returns null if format is not supported

        e.Handled = true;

        if (files != null)

          //Walk through the list of FileInfo objects of the selected and drag-dropped files and parse the .txt and .docx files
          //and insert their content in the RichTextBox.
          foreach (FileInfo file in files)
            Load(file, false);
      }

    }

    private void rtb_DragEnter(object sender, System.Windows.DragEventArgs e)
    {
      VisualStateManager.GoToState(this, "DragOver", true);
      e.Handled = true;
    }
   
    private void rtb_DragOver(object sender, System.Windows.DragEventArgs e)
    {
      e.Handled = true;
      //NOP
    }

    private void rtb_DragLeave(object sender, System.Windows.DragEventArgs e)
    {
      VisualStateManager.GoToState(this, "Normal", true);
      e.Handled = true;
    }

    #endregion

 

For the visual feedback you can use VisualStateManager and respective VisualStates at say a Grid that wraps the drop target in the XAML layout.

<Grid x:Name="LayoutRoot">
   <VisualStateManager.VisualStateGroups>
     <VisualStateGroup x:Name="DragStates">
       <VisualStateGroup.Transitions>
         <VisualTransition GeneratedDuration="0:0:0.3">
           <VisualTransition.GeneratedEasingFunction>
             <CircleEase EasingMode="EaseIn"/>
           </VisualTransition.GeneratedEasingFunction>
         </VisualTransition>
       </VisualStateGroup.Transitions>
       <VisualState x:Name="Normal"/>
       <VisualState x:Name="DragOver">
         <Storyboard>
           <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Storyboard.TargetName="rtb">
             <EasingColorKeyFrame KeyTime="0" Value="#FFFFF7D1"/>
           </ColorAnimationUsingKeyFrames>
         </Storyboard>
       </VisualState>
     </VisualStateGroup>
   </VisualStateManager.VisualStateGroups>

 

Note that on MacOS-X Safari doesn’t pass drop events automatically to plugins and you have to catch them via Javascript and pass them to Silverlight, plus I’ve read that in MacOS-X Firefox doesn’t support this at all.

Even worse, Microsoft’s workaround article has a typo in the Javascript (should call dragDrop instead of drop) and there is a chance it doesn’t work in latest version of Safari. For more see:

http://msdn.microsoft.com/en-us/library/ee670998%28v=vs.95%29.aspx

http://social.msdn.microsoft.com/Forums/silverlight/en-US/42a6b672-7d26-4690-be80-2149da755020/silverlight-4-detect-file-drop-event-on-mac?forum=silverlightarchieve&prof=required

http://community.rightpoint.com/blogs/viewpoint/archive/2011/03/27/silverlight-4-file-drag-and-drop-on-firefox-on-mac.aspx

http://www.telerik.com/forums/dropping-files-using-safari-on-mac (this says one can use HTML5 events and the Silverlight HTML/Javascript Bridge to notify Silverlight)

http://www.thebuzzmedia.com/html5-drag-and-drop-and-file-api-tutorial/

Gotcha: OnLostMouseCapture always called by CaptureMouse at WPF

It seems that CaptureMouse is behaving differently in WPF and Silverlight, in that in the former one it immediately calls OnLostMouseCapture at a Visual, whereas in Silverlight it doesn’t get called if the element didn’t have the mouse capture already (btw, in Silverlight that method is at a UIElement – there is no Visual ancestor as in WPF).

    #region --- Events ---

    private void OnMouseLeftButtonDown(object source, MouseButtonEventArgs e)
    {
      if (IsMoveToPointEnabled)
      {
        MoveThumbToPoint(e.GetPosition(this));
        CaptureMouse(); //must do before setting dragging=true, since WPF seems
//to be always calling OnLostMouseCapture on all
//controls, even if they didn't have the mouse capture
dragging = true; //always set, we might not make it to capture the mouse } } private void OnMouseLeftButtonUp(object source, MouseButtonEventArgs e) { ReleaseMouseCapture(); dragging = false; //always clear, in case we never had the mouse capture } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (dragging && IsMoveToPointEnabled) MoveThumbToPoint(e.GetPosition(this)); } protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); dragging = false; //set dragging to false whatever the value of
//IsMoveToPointEnabled (may have changed while dragging)
} #endregion

 

The rest of the code of the SliderExt class that I’ve just added to ClipFlair codebase is below, where … is where the code region that was quoted above goes. This class is a descendent of Slider and implements IsMoveToPointEnabled property for Silverlight (in WPF that property exists), plus in WPF it fixes the behavior of the control so that when that property is set, it not only supports moving the slider thumb to the point on its track where mouse button was held down (instead of stepping up/down as is usually done in scrollbars), but also supports dragging from any point in the slider track which WPF’s implementation of IsMouseToPointEnabled didn’t do (it only allowed to drag from the thumb).

//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: SliderExt.cs
//Version: 20140313

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace SliderExtLib
{
  public class SliderExt : Slider
  {

    public SliderExt()
    {

      //peek into mouse button events (even handled ones)
AddHandler(MouseLeftButtonDownEvent,
new MouseButtonEventHandler(OnMouseLeftButtonDown), true); AddHandler(MouseLeftButtonUpEvent,
new MouseButtonEventHandler(OnMouseLeftButtonUp), true); } #region --- Fields --- protected bool dragging; #endregion #region --- Properties --- public bool IsMoveToPointEnabled //WPF has this prop (hiding it), doesn't
//implement move to point if you click and drag on the track instead of thumb
{ get { return (bool)GetValue(IsMoveToPointEnabledProperty); } set { SetValue(IsMoveToPointEnabledProperty, value); } } public static readonly DependencyProperty IsMoveToPointEnabledProperty = DependencyProperty.RegisterAttached("IsMoveToPointEnabled", typeof(bool), typeof(SliderExt),
new PropertyMetadata(false)); #endregion #region --- Methods --- private void MoveThumbToPoint(Point p) { if (Orientation == Orientation.Horizontal) Value = p.X / ActualWidth * Maximum; else //if (Orientation == Orientation.Vertical) Value = p.Y / ActualHeight * Maximum; } #endregion




...



}
}

The incentive of making such a slider was for using it at the ColorPicker control I’ve just added to the ClipFlair codebase.

fix: Microsoft mouse right-click failure

The other day my Microsoft Wireless Laser Mouse 6000 (was either a gift from Microsoft Hellas or had ordered it myself from Microsoft Company Store with the credit Microsoft used to give pro-crisis to MVPs) started behaving erratically, failing most of the times to right-click and sometimes doing left clicks instead with some delay, or doing other actions like Paste etc.

It seems the issue was with Microsoft Mouse and Keyboard center software that Windows Update installs. Uninstalling it (from the Windows Control Panel) fixed the issue immediately.

That software is a total failure since it doesn’t support correctly various Microsoft hardware device older models. It shouldn’t install at all in the first place if it finds an incompatible device or try to behave better. It’s a shame that software is from Microsoft itself.

Even worse it that at their webpage they say:

Tell us what you think!

We are interested in your experiences with Microsoft Mouse and Keyboard Center and your Microsoft device!

  • Send your comments and questions by clicking on the Send Feedback icon on the Windows 8 desktop, select Apps then Microsoft Mouse and Keyboard Center from the options listed.

Not very helpful for those wanting to send feedback from Windows 7, is it?

%d bloggers like this: