Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added RangeSelector #257

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/Callisto/Callisto.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
<Compile Include="Controls\Common\LinearClipper.cs" />
<Compile Include="Controls\CustomDialog\CustomDialog.cs" />
<Compile Include="Controls\NumericUpDown\NumericUpDown.cs" />
<Compile Include="Controls\RangeSelector\RangeSelector.cs" />
<Compile Include="Controls\Rating\Rating.cs" />
<Compile Include="Controls\Rating\RatingItem.cs" />
<Compile Include="Controls\Rating\RatingSelectionMode.cs" />
Expand Down Expand Up @@ -251,7 +252,8 @@
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<Import Project="XamlTypeInfoBuildTask.targets" Condition="Exists('XamlTypeInfoBuildTask.targets')" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
<Import Project="XamlTypeInfoBuildTask.targets" Condition="Exists('XamlTypeInfoBuildTask.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
Expand Down
276 changes: 276 additions & 0 deletions src/Callisto/Controls/RangeSelector/RangeSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
//
// Copyright (c) 2015 David Catuhe
//
// Licensed under the Microsoft Public License (Ms-PL) (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://opensource.org/licenses/Ms-PL.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace Callisto.Controls
{
/// <summary>
/// RangeSelector is a "double slider" control for range values.
/// </summary>
public sealed class RangeSelector : Control
{
/// <summary>
/// Identifies the Minimum dependency property.
/// </summary>
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSelector), new PropertyMetadata(0.0, null));

/// <summary>
/// Identifies the Maximum dependency property.
/// </summary>
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSelector), new PropertyMetadata(1.0, null));

/// <summary>
/// Identifies the RangeMin dependency property.
/// </summary>
public static readonly DependencyProperty RangeMinProperty = DependencyProperty.Register("RangeMin", typeof(double), typeof(RangeSelector), new PropertyMetadata(0.0, null));

/// <summary>
/// Identifies the RangeMax dependency property.
/// </summary>
public static readonly DependencyProperty RangeMaxProperty = DependencyProperty.Register("RangeMax", typeof(double), typeof(RangeSelector), new PropertyMetadata(1.0, null));

Rectangle ActiveRectangle;
Thumb MinThumb;
Thumb MaxThumb;
Canvas ContainerCanvas;

/// <summary>
/// Event raised when lower or upper range values are changed.
/// </summary>
public event Action ValueChanged;

/// <summary>
/// Create a default range selector control.
/// </summary>
public RangeSelector()
{
DefaultStyleKey = typeof(RangeSelector);
}

/// <summary>
/// Update the visual state of the control when its template is changed.
/// </summary>
protected override void OnApplyTemplate()
{
ActiveRectangle = GetTemplateChild("ActiveRectangle") as Rectangle;
MinThumb = GetTemplateChild("MinThumb") as Thumb;
MaxThumb = GetTemplateChild("MaxThumb") as Thumb;
ContainerCanvas = GetTemplateChild("ContainerCanvas") as Canvas;

MinThumb.DragCompleted += Thumb_DragCompleted;
MinThumb.DragDelta += MinThumb_DragDelta;
MinThumb.DragStarted += MinThumb_DragStarted;

MaxThumb.DragCompleted += Thumb_DragCompleted;
MaxThumb.DragDelta += MaxThumb_DragDelta;
MaxThumb.DragStarted += MaxThumb_DragStarted;

ContainerCanvas.SizeChanged += ContainerCanvas_SizeChanged;

base.OnApplyTemplate();
}

private void ContainerCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
SyncThumbs();
}

/// <summary>
/// Gets or sets the minimum value of the range.
/// </summary>
/// <value>
/// The minimum.
/// </value>
public double Minimum
{
get
{
return (double)GetValue(MinimumProperty);
}
set
{
if (value > Maximum)
{
value = Maximum;
}
SetValue(MinimumProperty, value);
}
}

/// <summary>
/// Gets or sets the maximum value of the range.
/// </summary>
/// <value>
/// The maximum.
/// </value>
public double Maximum
{
get
{
return (double)GetValue(MaximumProperty);
}
set
{
if (value < Minimum)
{
value = Minimum;
}
SetValue(MaximumProperty, value);
}
}

/// <summary>
/// Gets or sets the current lower limit value of the range.
/// </summary>
/// <value>
/// The current lower limit.
/// </value>
public double RangeMin
{
get
{
return (double)GetValue(RangeMinProperty);
}
set
{
if (value < Minimum)
{
value = Minimum;
}

if (value > Maximum)
{
value = Maximum;
}

if (value > RangeMax)
{
RangeMax = value;
}

SetValue(RangeMinProperty, value);
SyncThumbs();
}
}

/// <summary>
/// Gets or sets the current upper limit value of the range.
/// </summary>
/// <value>
/// The current upper limit.
/// </value>
public double RangeMax
{
get
{
return (double)GetValue(RangeMaxProperty);
}
set
{
if (value < Minimum)
{
value = Minimum;
}

if (value > Maximum)
{
value = Maximum;
}

if (value < RangeMin)
{
RangeMin = value;
}
SetValue(RangeMaxProperty, value);
SyncThumbs();
}
}

private void SyncThumbs()
{
if (ContainerCanvas == null)
{
return;
}

var relativeLeft = ((RangeMin - Minimum) / (Maximum - Minimum)) * ContainerCanvas.ActualWidth;
var relativeRight = ((RangeMax - Minimum) / (Maximum - Minimum)) * ContainerCanvas.ActualWidth;

Canvas.SetLeft(MinThumb, relativeLeft);
Canvas.SetLeft(ActiveRectangle, relativeLeft);

Canvas.SetLeft(MaxThumb, relativeRight);

ActiveRectangle.Width = Canvas.GetLeft(MaxThumb) - Canvas.GetLeft(MinThumb);
}

private void MinThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
RangeMin = DragThumb(MinThumb, 0, Canvas.GetLeft(MaxThumb), e);
}

private void MaxThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
RangeMax = DragThumb(MaxThumb, Canvas.GetLeft(MinThumb), ContainerCanvas.ActualWidth, e);
}

private double DragThumb(Thumb thumb, double min, double max, DragDeltaEventArgs e)
{
var currentPos = Canvas.GetLeft(thumb);
var nextPos = currentPos + e.HorizontalChange;

nextPos = Math.Max(min, nextPos);
nextPos = Math.Min(max, nextPos);

Canvas.SetLeft(thumb, nextPos);

return (Minimum + (nextPos / ContainerCanvas.ActualWidth) * (Maximum - Minimum)); ;
}

private void MinThumb_DragStarted(object sender, DragStartedEventArgs e)
{
Canvas.SetZIndex(MinThumb, 10);
Canvas.SetZIndex(MaxThumb, 0);
}

private void MaxThumb_DragStarted(object sender, DragStartedEventArgs e)
{
Canvas.SetZIndex(MinThumb, 0);
Canvas.SetZIndex(MaxThumb, 10);
}

private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
if (ValueChanged != null)
{
ValueChanged();
}
}
}
}
32 changes: 32 additions & 0 deletions src/Callisto/themes/Generic.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -1514,4 +1514,36 @@ limitations under the License.-->
</Setter.Value>
</Setter>
</Style>

<Style TargetType="local:RangeSelector" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:RangeSelector">
<Grid Height="32">
<Grid.Resources>
<Style TargetType="Thumb">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Ellipse Width="32" Height="32" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" StrokeThickness="4" RenderTransformOrigin="0.5 0.5">
<Ellipse.RenderTransform>
<TranslateTransform X="-16"></TranslateTransform>
</Ellipse.RenderTransform>
</Ellipse>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Rectangle Height="8" Fill="{TemplateBinding Background}" Margin="12,0"></Rectangle>
<Canvas x:Name="ContainerCanvas" Margin="16,0">
<Rectangle x:Name="ActiveRectangle" Fill="{TemplateBinding Foreground}" Height="8" Canvas.Top="12"></Rectangle>
<Thumb x:Name="MinThumb" Background="{TemplateBinding Background}" />
<Thumb x:Name="MaxThumb" Background="{TemplateBinding Background}"/>
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>