|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThis is a second part of creating a movable usercontrol in WPF. I assume you have read the part I (even though that was poorly written). I have made a lot of improvement to the file in that article. In this part, you can download the latest base MovabaleControl class and two controls that inherit it. BackgroundIn HCI (human-computer interaction), we use Fitts' Law to measure the performance of selecting a target. It is based on D (the distance between the cursor and the selecting target), and W (the width of the target), and it calculates the average time taken to complete the selection. Now, when study fall to a moving target selection, things are becoming more interesting. The goal of this series is to build up a WPF application to perform a test on moving target selection. We already have built this base class, now we can create some different moving targets, so we can find out if there is any technique we can use to enhance the moving target selection performance. Normal TargetThe first thing is, we need a really simple and base moving target, without any technique (aid) that could facilitate the target selection. Well, this is easy. <local:MovableUserControl x:Class="WPFTest.Targets.NormalTarget"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="40" Width="100" xmlns:local="clr-namespace:WPFTest.Targets">
<Grid>
<Rectangle Width="100" Height="40" Stroke="Black" Focusable="True" Name="TextRect" Fill="LightGray"></Rectangle>
<TextBlock Text="Normal Target" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Name="TargetText"></TextBlock>
</Grid>
</local:MovableUserControl>
The xmlns:local is a self-defined tag and it's pointing to the namespace we are after. Then we can reference our own UserControl. public partial class NormalTarget : MovableUserControl
{
public NormalTarget()
{
InitializeComponent();
}
public override string Text
{
get { return TargetText.Text; }
set { TargetText.Text = value; TargetText.HorizontalAlignment = HorizontalAlignment.Center; }
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
MouseClicked = true;
}
public override void Start()
{
if (this.IsTarget)
{
this.TextRect.Fill = Brushes.Yellow;
}
base.Start();
}
}
In the Base class, I have two properties:
You can see from the code, I changed the control background color if it's a target. ExpandedClick TechniqueThis is a simple technique that I created might potentially enhance the selection performance. If you ever used MacOS or had any experience in HCI, you must have known there is something called "Expanded Target". It's just like the MacOS task bar, when your mouse is approaching the target, the target itself will grow bigger so that the user can easily select it.
Let's look at the XAML code first: <local:MovableUserControl x:Class="WPFTest.Targets.ExpandedClick"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="80" Width="160" xmlns:local="clr-namespace:WPFTest.Targets">
<Grid>
<Rectangle Width="160" Height="80" Fill="Transparent" Name="BackRect"></Rectangle>
<Rectangle Width="100" Height="40" Stroke="Black" Focusable="True" Name="TextRect" Fill="LightGray"></Rectangle>
<TextBlock Text="Expanded Target" Margin="38,32,40,32" Name="TargetText"></TextBlock>
</Grid>
</local:MovableUserControl>
It's very similar to the Normal target; however, it has one more Rectangle that is the clickable area. By default, it's transparent; when the mouse is hover, it will change the color. The code behind will be like: public partial class ExpandedClick : MovableUserControl
{
// these two values define the allowable offset of this control, because the control itself actually is larger than
// what the user can see due to the clickable area is expanded
private int horizontalOffset = 0;
private int verticalOffset = 0;
public ExpandedClick()
{
InitializeComponent();
}
private Brush m_ExpandedBackground = Brushes.Azure;
public Brush BackgroundBrush
{
get { return m_ExpandedBackground; }
set { m_ExpandedBackground = value; }
}
public override string Text
{
get { return TargetText.Text; }
set { TargetText.Text = value; TargetText.TextAlignment = TextAlignment.Center; }
}
protected override void OnMouseEnter(MouseEventArgs e)
{
BackRect.Fill = BackgroundBrush;
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
BackRect.Fill = Brushes.Transparent;
base.OnMouseLeave(e);
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
BackRect.Fill = Brushes.Transparent;
base.OnMouseDown(e);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
BackRect.Fill = BackgroundBrush;
this.TextRect.Fill = Brushes.LightGray;
MouseClicked = true;
}
public override void Start()
{
if (this.IsTarget)
{
TextRect.Fill = Brushes.Yellow;
}
base.Start();
}
private void precal()
{
if (horizontalOffset == 0 && verticalOffset == 0)
{
// do the pre-calculation in here
horizontalOffset = (int)(this.Width - TextRect.Width) / 2;
verticalOffset = (int)(this.Height - TextRect.Height) / 2;
}
}
public override int LeftOffset
{
get
{
precal();
return -horizontalOffset;
}
}
public override int TopOffset
{
get
{
precal();
return -verticalOffset;
}
}
public override int RightOffset
{
get
{
precal();
return horizontalOffset;
}
}
public override int BottomOffset
{
get
{
precal();
return verticalOffset;
}
}
}It's a little more compared to the normal target. First, in the base control, I defined four properties:
They are used to detect if the control hits the boundary. If yes, we need to re-calculate where the control is heading in the next interval. The detection code is like: if (currentX + this.ActualWidth >= parentWidth + RightOffset ||
currentY + this.ActualHeight >= parentHeight + BottomOffset ||
currentX <= LeftOffset ||
currentY <= TopOffset)
{
restart = true;
}
In this control, we don't want the user see such a large "block" on the screen, because the actual visual target part doesn't include the clickable area. Hence, we want to tell the parent, here is a small offset you can go, don't bounce yet. Points of InterestNext time I will show you another technique I created. Then I will introduce how to create a platform to run the experiment. Oh, right, the source code is here Download MovableTargets.zip - 5.21 KB History10/10/2008 - first edition
|
||||||||||||||||||||||