215 changed files with 13070 additions and 435 deletions
@ -0,0 +1,66 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Binding.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
public enum BindingMode |
||||
|
{ |
||||
|
Default, |
||||
|
OneWay, |
||||
|
TwoWay, |
||||
|
OneTime, |
||||
|
OneWayToSource, |
||||
|
} |
||||
|
|
||||
|
public struct Binding |
||||
|
{ |
||||
|
public BindingMode Mode |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public BindingPriority Priority |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public PerspexProperty Property |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public PerspexObject Source |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public static Binding operator !(Binding binding) |
||||
|
{ |
||||
|
return binding.WithMode(BindingMode.TwoWay); |
||||
|
} |
||||
|
|
||||
|
public static Binding operator ~(Binding binding) |
||||
|
{ |
||||
|
return binding.WithMode(BindingMode.TwoWay); |
||||
|
} |
||||
|
|
||||
|
public Binding WithMode(BindingMode mode) |
||||
|
{ |
||||
|
this.Mode = mode; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public Binding WithPriority(BindingPriority priority) |
||||
|
{ |
||||
|
this.Priority = priority; |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Contract.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
public static class Contract |
||||
|
{ |
||||
|
public static void Requires<TException>(bool condition) where TException : Exception, new() |
||||
|
{ |
||||
|
#if DEBUG
|
||||
|
if (!condition) |
||||
|
{ |
||||
|
throw new TException(); |
||||
|
} |
||||
|
#endif
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IDescription.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
public interface IDescription |
||||
|
{ |
||||
|
string Description { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IObservableDescription.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
public interface IObservableDescription |
||||
|
{ |
||||
|
string Description { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
||||
|
<PropertyGroup> |
||||
|
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProjectGuid>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex</RootNamespace> |
||||
|
<AssemblyName>Perspex.Base</AssemblyName> |
||||
|
<DefaultLanguage>en-US</DefaultLanguage> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile> |
||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="Binding.cs" /> |
||||
|
<Compile Include="Contract.cs" /> |
||||
|
<Compile Include="IDescription.cs" /> |
||||
|
<Compile Include="IObservableDescription.cs" /> |
||||
|
<Compile Include="PerspexList.cs" /> |
||||
|
<Compile Include="PerspexObject.cs" /> |
||||
|
<Compile Include="PerspexProperty.cs" /> |
||||
|
<Compile Include="PerspexPropertyChangedEventArgs.cs" /> |
||||
|
<Compile Include="Platform\IPlatformThreadingInterface.cs" /> |
||||
|
<Compile Include="PriorityValue.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
<Compile Include="Threading\Dispatcher.cs" /> |
||||
|
<Compile Include="Threading\DispatcherFrame.cs" /> |
||||
|
<Compile Include="Threading\DispatcherOperation.cs" /> |
||||
|
<Compile Include="Threading\DispatcherTimer.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Splat"> |
||||
|
<HintPath>..\packages\Splat.1.3.3\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Core"> |
||||
|
<HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Core.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Interfaces"> |
||||
|
<HintPath>..\packages\Rx-Interfaces.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Interfaces.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Linq"> |
||||
|
<HintPath>..\packages\Rx-Linq.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Linq.dll</HintPath> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
||||
|
<Import Project="..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets" Condition="Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" /> |
||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> |
||||
|
<PropertyGroup> |
||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> |
||||
|
</PropertyGroup> |
||||
|
<Error Condition="!Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets'))" /> |
||||
|
</Target> |
||||
|
<!-- 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> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,50 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PerspexList.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.ObjectModel; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
public class PerspexList<T> : ObservableCollection<T> |
||||
|
{ |
||||
|
public PerspexList() |
||||
|
{ |
||||
|
this.Initialize(); |
||||
|
} |
||||
|
|
||||
|
public PerspexList(IEnumerable<T> items) |
||||
|
: base(items) |
||||
|
{ |
||||
|
this.Initialize(); |
||||
|
} |
||||
|
|
||||
|
public IObservable<NotifyCollectionChangedEventArgs> Changed |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public void AddRange(IEnumerable<T> items) |
||||
|
{ |
||||
|
foreach (T item in items) |
||||
|
{ |
||||
|
this.Add(item); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void Initialize() |
||||
|
{ |
||||
|
this.Changed = Observable.FromEvent<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>( |
||||
|
handler => (sender, e) => handler(e), |
||||
|
handler => this.CollectionChanged += handler, |
||||
|
handler => this.CollectionChanged -= handler); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,646 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PerspexObject.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using System.Reflection; |
||||
|
using Splat; |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The priority of a binding.
|
||||
|
/// </summary>
|
||||
|
public enum BindingPriority |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A local value.
|
||||
|
/// </summary>
|
||||
|
LocalValue, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A triggered style binding.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// A style trigger is a selector such as .class which overrides a
|
||||
|
/// <see cref="TemplatedParent"/> binding. In this way, a basic control can have
|
||||
|
/// for example a Background from the templated parent which changes when the
|
||||
|
/// control has the :pointerover class.
|
||||
|
/// </remarks>
|
||||
|
StyleTrigger, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A binding to a property on the templated parent.
|
||||
|
/// </summary>
|
||||
|
TemplatedParent, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A style binding.
|
||||
|
/// </summary>
|
||||
|
Style, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The binding is uninitialized.
|
||||
|
/// </summary>
|
||||
|
Unset = int.MaxValue, |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// An object with <see cref="PerspexProperty"/> support.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This class is analogous to DependencyObject in WPF.
|
||||
|
/// </remarks>
|
||||
|
public class PerspexObject : IEnableLogger |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The registered properties by type.
|
||||
|
/// </summary>
|
||||
|
private static Dictionary<Type, List<PerspexProperty>> registered = |
||||
|
new Dictionary<Type, List<PerspexProperty>>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The parent object that inherited values are inherited from.
|
||||
|
/// </summary>
|
||||
|
private PerspexObject inheritanceParent; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The set values/bindings on this object.
|
||||
|
/// </summary>
|
||||
|
private Dictionary<PerspexProperty, PriorityValue> values = |
||||
|
new Dictionary<PerspexProperty, PriorityValue>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raised when a <see cref="PerspexProperty"/> value changes on this object/
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<PerspexPropertyChangedEventArgs> PropertyChanged; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the parent object that inherited <see cref="PerspexProperty"/> values
|
||||
|
/// are inherited from.
|
||||
|
/// </summary>
|
||||
|
protected PerspexObject InheritanceParent |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.inheritanceParent; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (this.inheritanceParent != value) |
||||
|
{ |
||||
|
if (this.inheritanceParent != null) |
||||
|
{ |
||||
|
this.inheritanceParent.PropertyChanged -= this.ParentPropertyChanged; |
||||
|
} |
||||
|
|
||||
|
var inherited = (from property in GetProperties(this.GetType()) |
||||
|
where property.Inherits |
||||
|
select new |
||||
|
{ |
||||
|
Property = property, |
||||
|
Value = this.GetValue(property), |
||||
|
}).ToList(); |
||||
|
|
||||
|
this.inheritanceParent = value; |
||||
|
|
||||
|
foreach (var i in inherited) |
||||
|
{ |
||||
|
object newValue = this.GetValue(i.Property); |
||||
|
|
||||
|
if (!object.Equals(i.Value, newValue)) |
||||
|
{ |
||||
|
this.RaisePropertyChanged(i.Property, i.Value, newValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (this.inheritanceParent != null) |
||||
|
{ |
||||
|
this.inheritanceParent.PropertyChanged += this.ParentPropertyChanged; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the value of a <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
public object this[PerspexProperty property] |
||||
|
{ |
||||
|
get { return this.GetValue(property); } |
||||
|
set { this.SetValue(property, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a binding for a <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="binding">The binding information.</param>
|
||||
|
public Binding this[Binding binding] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return new Binding |
||||
|
{ |
||||
|
Mode = binding.Mode, |
||||
|
Priority = binding.Priority, |
||||
|
Property = binding.Property, |
||||
|
Source = this, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
BindingMode mode = (binding.Mode == BindingMode.Default) ? |
||||
|
binding.Property.DefaultBindingMode : |
||||
|
binding.Mode; |
||||
|
|
||||
|
switch (mode) |
||||
|
{ |
||||
|
case BindingMode.Default: |
||||
|
case BindingMode.OneWay: |
||||
|
this.Bind(binding.Property, value.Source.GetObservable(value.Property), binding.Priority); |
||||
|
break; |
||||
|
case BindingMode.OneTime: |
||||
|
this.SetValue(binding.Property, value.Source.GetValue(value.Property)); |
||||
|
break; |
||||
|
case BindingMode.OneWayToSource: |
||||
|
value.Source.Bind(value.Property, this.GetObservable(binding.Property), binding.Priority); |
||||
|
break; |
||||
|
case BindingMode.TwoWay: |
||||
|
this.BindTwoWay(binding.Property, value.Source, value.Property); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets all <see cref="PerspexProperty"/>s registered on a type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type.</param>
|
||||
|
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
|
||||
|
public static IEnumerable<PerspexProperty> GetProperties(Type type) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(type != null); |
||||
|
|
||||
|
TypeInfo i = type.GetTypeInfo(); |
||||
|
|
||||
|
while (type != null) |
||||
|
{ |
||||
|
List<PerspexProperty> list; |
||||
|
|
||||
|
if (registered.TryGetValue(type, out list)) |
||||
|
{ |
||||
|
foreach (PerspexProperty p in list) |
||||
|
{ |
||||
|
yield return p; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type = type.GetTypeInfo().BaseType; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers a <see cref="PerspexProperty"/> on a type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type.</param>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <remarks>
|
||||
|
/// You won't usually want to call this method directly, instead use the
|
||||
|
/// <see cref="PerspexProperty.Register"/> method.
|
||||
|
/// </remarks>
|
||||
|
public static void Register(Type type, PerspexProperty property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(type != null); |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
List<PerspexProperty> list; |
||||
|
|
||||
|
if (!registered.TryGetValue(type, out list)) |
||||
|
{ |
||||
|
list = new List<PerspexProperty>(); |
||||
|
registered.Add(type, list); |
||||
|
} |
||||
|
|
||||
|
if (!list.Contains(property)) |
||||
|
{ |
||||
|
list.Add(property); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clears a <see cref="PerspexProperty"/>'s local value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
public void ClearValue(PerspexProperty property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
this.SetValue(property, PerspexProperty.UnsetValue); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an observable for a <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>An observable.</returns>
|
||||
|
public IObservable<object> GetObservable(PerspexProperty property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
return Observable.Create<object>(observer => |
||||
|
{ |
||||
|
EventHandler<PerspexPropertyChangedEventArgs> handler = (s, e) => |
||||
|
{ |
||||
|
if (e.Property == property) |
||||
|
{ |
||||
|
observer.OnNext(e.NewValue); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
this.PropertyChanged += handler; |
||||
|
observer.OnNext(this.GetValue(property)); |
||||
|
|
||||
|
return () => |
||||
|
{ |
||||
|
this.PropertyChanged -= handler; |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an observable for a <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T"></typeparam>
|
||||
|
/// <param name="property"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public IObservable<T> GetObservable<T>(PerspexProperty<T> property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
return this.GetObservable((PerspexProperty)property).Cast<T>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an observable for a <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T"></typeparam>
|
||||
|
/// <param name="property"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public IObservable<Tuple<T, T>> GetObservableWithHistory<T>(PerspexProperty<T> property) |
||||
|
{ |
||||
|
return Observable.Create<Tuple<T, T>>(observer => |
||||
|
{ |
||||
|
EventHandler<PerspexPropertyChangedEventArgs> handler = (s, e) => |
||||
|
{ |
||||
|
if (e.Property == property) |
||||
|
{ |
||||
|
observer.OnNext(Tuple.Create((T)e.OldValue, (T)e.NewValue)); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
this.PropertyChanged += handler; |
||||
|
|
||||
|
return () => |
||||
|
{ |
||||
|
this.PropertyChanged -= handler; |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a <see cref="PerspexProperty"/> value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>The value.</returns>
|
||||
|
public object GetValue(PerspexProperty property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
object result; |
||||
|
|
||||
|
PriorityValue value; |
||||
|
|
||||
|
if (this.values.TryGetValue(property, out value)) |
||||
|
{ |
||||
|
result = value.Value; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = PerspexProperty.UnsetValue; |
||||
|
} |
||||
|
|
||||
|
if (result == PerspexProperty.UnsetValue) |
||||
|
{ |
||||
|
result = this.GetDefaultValue(property); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a <see cref="PerspexProperty"/> value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>The value.</returns>
|
||||
|
public T GetValue<T>(PerspexProperty<T> property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
return (T)this.GetValue((PerspexProperty)property); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets all of the <see cref="PerspexProperty"/> values explicitly set on this object.
|
||||
|
/// </summary>
|
||||
|
public IEnumerable<Tuple<PerspexProperty, object, BindingPriority>> GetSetValues() |
||||
|
{ |
||||
|
foreach (var value in this.values) |
||||
|
{ |
||||
|
yield return Tuple.Create( |
||||
|
value.Key, |
||||
|
value.Value.Value, |
||||
|
(BindingPriority)value.Value.ValuePriority); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks whether a <see cref="PerspexProperty"/> is set on this object.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>True if the property is set, otherwise false.</returns>
|
||||
|
public bool IsSet(PerspexProperty property) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
return this.values.ContainsKey(property); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks whether a <see cref="PerspexProperty"/> is registered on this class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>True if the property is registered, otherwise false.</returns>
|
||||
|
private bool IsRegistered(PerspexProperty property) |
||||
|
{ |
||||
|
Type type = this.GetType(); |
||||
|
|
||||
|
while (type != null) |
||||
|
{ |
||||
|
List<PerspexProperty> list; |
||||
|
|
||||
|
if (registered.TryGetValue(type, out list)) |
||||
|
{ |
||||
|
if (list.Contains(property)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type = type.GetTypeInfo().BaseType; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a <see cref="PerspexProperty"/> value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <param name="value">The value.</param>
|
||||
|
public void SetValue(PerspexProperty property, object value) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
const int Priority = (int)BindingPriority.LocalValue; |
||||
|
PriorityValue v; |
||||
|
|
||||
|
if (!this.IsRegistered(property)) |
||||
|
{ |
||||
|
throw new InvalidOperationException(string.Format( |
||||
|
"Property '{0}' not registered on '{1}'", |
||||
|
property.Name, |
||||
|
this.GetType())); |
||||
|
} |
||||
|
|
||||
|
if (!PriorityValue.IsValidValue(value, property.PropertyType)) |
||||
|
{ |
||||
|
throw new InvalidOperationException(string.Format( |
||||
|
"Invalid value for Property '{0}': {1} ({2})", |
||||
|
property.Name, |
||||
|
value, |
||||
|
value.GetType().FullName)); |
||||
|
} |
||||
|
|
||||
|
if (!this.values.TryGetValue(property, out v)) |
||||
|
{ |
||||
|
if (value == PerspexProperty.UnsetValue) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
v = this.CreatePriorityValue(property); |
||||
|
this.values.Add(property, v); |
||||
|
} |
||||
|
|
||||
|
this.Log().Debug(string.Format( |
||||
|
"Set local value of {0}.{1} (#{2:x8}) to {3}", |
||||
|
this.GetType().Name, |
||||
|
property.Name, |
||||
|
this.GetHashCode(), |
||||
|
value)); |
||||
|
|
||||
|
v.Replace(Observable.Never<object>().StartWith(value), Priority); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a <see cref="PerspexProperty"/> value.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of the property.</typeparam>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <param name="value">The value.</param>
|
||||
|
public void SetValue<T>(PerspexProperty<T> property, T value) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
this.SetValue((PerspexProperty)property, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Binds a <see cref="PerspexProperty"/> to an observable.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of the property.</typeparam>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <param name="source">The observable.</param>
|
||||
|
/// <param name="priority">The priority of the binding.</param>
|
||||
|
/// <returns>
|
||||
|
/// A disposable which can be used to terminate the binding.
|
||||
|
/// </returns>
|
||||
|
public IDisposable Bind( |
||||
|
PerspexProperty property, |
||||
|
IObservable<object> source, |
||||
|
BindingPriority priority = BindingPriority.LocalValue) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
PriorityValue v; |
||||
|
IDescription description = source as IDescription; |
||||
|
|
||||
|
if (!this.values.TryGetValue(property, out v)) |
||||
|
{ |
||||
|
v = this.CreatePriorityValue(property); |
||||
|
this.values.Add(property, v); |
||||
|
} |
||||
|
|
||||
|
this.Log().Debug(string.Format( |
||||
|
"Bound value of {0}.{1} (#{2:x8}) to {3}", |
||||
|
this.GetType().Name, |
||||
|
property.Name, |
||||
|
this.GetHashCode(), |
||||
|
description != null ? description.Description : "[Anonymous]")); |
||||
|
|
||||
|
if (priority == BindingPriority.LocalValue) |
||||
|
{ |
||||
|
return v.Replace(source, (int)priority); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return v.Add(source, (int)priority); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Binds a <see cref="PerspexProperty"/> to an observable.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of the property.</typeparam>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <param name="source">The observable.</param>
|
||||
|
/// <param name="priority">The priority of the binding.</param>
|
||||
|
/// <returns>
|
||||
|
/// A disposable which can be used to terminate the binding.
|
||||
|
/// </returns>
|
||||
|
public IDisposable Bind<T>( |
||||
|
PerspexProperty<T> property, |
||||
|
IObservable<T> source, |
||||
|
BindingPriority priority = BindingPriority.LocalValue) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
return this.Bind((PerspexProperty)property, (IObservable<object>)source, priority); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initialites a two-way bind between <see cref="PerspexProperty"/>s.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property on this object.</param>
|
||||
|
/// <param name="source">The source object.</param>
|
||||
|
/// <param name="sourceProperty">The property on the source object.</param>
|
||||
|
/// <returns>
|
||||
|
/// A disposable which can be used to terminate the binding.
|
||||
|
/// </returns>
|
||||
|
/// <remarks>
|
||||
|
/// The binding is first carried out from <paramref name="source"/> to this. Two-way
|
||||
|
/// bindings are always at the LocalValue priority.
|
||||
|
/// </remarks>
|
||||
|
public void BindTwoWay( |
||||
|
PerspexProperty property, |
||||
|
PerspexObject source, |
||||
|
PerspexProperty sourceProperty) |
||||
|
{ |
||||
|
source.GetObservable(sourceProperty).Subscribe(x => this.SetValue(property, x)); |
||||
|
this.GetObservable(property).Subscribe(x => source.SetValue(sourceProperty, x)); |
||||
|
} |
||||
|
|
||||
|
private PriorityValue CreatePriorityValue(PerspexProperty property) |
||||
|
{ |
||||
|
PriorityValue result = new PriorityValue(property.Name, property.PropertyType); |
||||
|
|
||||
|
result.Changed.Subscribe(x => |
||||
|
{ |
||||
|
object oldValue = (x.Item1 == PerspexProperty.UnsetValue) ? |
||||
|
this.GetDefaultValue(property) : |
||||
|
x.Item1; |
||||
|
object newValue = (x.Item2 == PerspexProperty.UnsetValue) ? |
||||
|
this.GetDefaultValue(property) : |
||||
|
x.Item2; |
||||
|
|
||||
|
if (!object.Equals(oldValue, newValue)) |
||||
|
{ |
||||
|
this.RaisePropertyChanged(property, oldValue, newValue); |
||||
|
|
||||
|
this.Log().Debug(string.Format( |
||||
|
"Value of {0}.{1} (#{2:x8}) changed from {3} to {4}", |
||||
|
this.GetType().Name, |
||||
|
property.Name, |
||||
|
this.GetHashCode(), |
||||
|
oldValue, |
||||
|
newValue)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the default value for a property.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>The default value.</returns>
|
||||
|
private object GetDefaultValue(PerspexProperty property) |
||||
|
{ |
||||
|
if (property.Inherits && this.inheritanceParent != null) |
||||
|
{ |
||||
|
return this.inheritanceParent.GetValue(property); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return property.GetDefaultValue(this.GetType()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when a property is changed on the current <see cref="InheritanceParent"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The event sender.</param>
|
||||
|
/// <param name="e">The event args.</param>
|
||||
|
/// <remarks>
|
||||
|
/// Checks for changes in an inherited property value.
|
||||
|
/// </remarks>
|
||||
|
private void ParentPropertyChanged(object sender, PerspexPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(e != null); |
||||
|
|
||||
|
if (e.Property.Inherits && !this.IsSet(e.Property)) |
||||
|
{ |
||||
|
this.RaisePropertyChanged(e.Property, e.OldValue, e.NewValue); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the <see cref="PropertyChanged"/> event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property that has changed.</param>
|
||||
|
/// <param name="oldValue">The old property value.</param>
|
||||
|
/// <param name="newValue">The new property value.</param>
|
||||
|
private void RaisePropertyChanged(PerspexProperty property, object oldValue, object newValue) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(property != null); |
||||
|
|
||||
|
if (this.PropertyChanged != null) |
||||
|
{ |
||||
|
PerspexPropertyChangedEventArgs e = new PerspexPropertyChangedEventArgs(this, property, oldValue, newValue); |
||||
|
property.NotifyChanged(e); |
||||
|
this.PropertyChanged(this, e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,333 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PerspexProperty.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Subjects; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A perspex property.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This class is analogous to DependencyProperty in WPF.
|
||||
|
/// </remarks>
|
||||
|
public class PerspexProperty |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents an unset property value.
|
||||
|
/// </summary>
|
||||
|
public static readonly object UnsetValue = new Unset(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The default values for the property, by type.
|
||||
|
/// </summary>
|
||||
|
private Dictionary<Type, object> defaultValues = new Dictionary<Type, object>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Observable fired when this property changes on any <see cref="PerspexObject"/>.
|
||||
|
/// </summary>
|
||||
|
private Subject<PerspexPropertyChangedEventArgs> changed = new Subject<PerspexPropertyChangedEventArgs>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PerspexProperty"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name of the property.</param>
|
||||
|
/// <param name="valueType">The type of the property's value.</param>
|
||||
|
/// <param name="ownerType">The type of the class that registers the property.</param>
|
||||
|
/// <param name="defaultValue">The default value of the property.</param>
|
||||
|
/// <param name="inherits">Whether the property inherits its value.</param>
|
||||
|
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
|
||||
|
public PerspexProperty( |
||||
|
string name, |
||||
|
Type valueType, |
||||
|
Type ownerType, |
||||
|
object defaultValue, |
||||
|
bool inherits, |
||||
|
BindingMode defaultBindingMode) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
Contract.Requires<NullReferenceException>(valueType != null); |
||||
|
Contract.Requires<NullReferenceException>(ownerType != null); |
||||
|
|
||||
|
this.Name = name; |
||||
|
this.PropertyType = valueType; |
||||
|
this.OwnerType = ownerType; |
||||
|
this.Inherits = inherits; |
||||
|
this.DefaultBindingMode = defaultBindingMode; |
||||
|
this.defaultValues.Add(ownerType, defaultValue); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the name of the property.
|
||||
|
/// </summary>
|
||||
|
public string Name { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the type of the property's value.
|
||||
|
/// </summary>
|
||||
|
public Type PropertyType { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the type of the class that registers the property.
|
||||
|
/// </summary>
|
||||
|
public Type OwnerType { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether the property inherits its value.
|
||||
|
/// </summary>
|
||||
|
public bool Inherits { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the default binding mode for the property.
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
public BindingMode DefaultBindingMode { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets an observable that is fired when this property changes on any
|
||||
|
/// <see cref="PerspexObject"/> instance.
|
||||
|
/// </summary>
|
||||
|
public IObservable<PerspexPropertyChangedEventArgs> Changed |
||||
|
{ |
||||
|
get { return this.changed; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers a <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TOwner">The type of the class that is registering the property.</typeparam>
|
||||
|
/// <typeparam name="TValue">The type of the property's value.</typeparam>
|
||||
|
/// <param name="name">The name of the property.</param>
|
||||
|
/// <param name="defaultValue">The default value of the property.</param>
|
||||
|
/// <param name="inherits">Whether the property inherits its value.</param>
|
||||
|
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
|
||||
|
/// <returns>A <see cref="PerspexProperty{TValue}"/></returns>
|
||||
|
public static PerspexProperty<TValue> Register<TOwner, TValue>( |
||||
|
string name, |
||||
|
TValue defaultValue = default(TValue), |
||||
|
bool inherits = false, |
||||
|
BindingMode defaultBindingMode = BindingMode.OneWay) |
||||
|
where TOwner : PerspexObject |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
|
||||
|
PerspexProperty<TValue> result = new PerspexProperty<TValue>( |
||||
|
name, |
||||
|
typeof(TOwner), |
||||
|
defaultValue, |
||||
|
inherits, |
||||
|
defaultBindingMode); |
||||
|
|
||||
|
PerspexObject.Register(typeof(TOwner), result); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers an attached <see cref="PerspexProperty"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TOwner">The type of the class that is registering the property.</typeparam>
|
||||
|
/// <typeparam name="THost">The type of the class that the property is to be registered on.</typeparam>
|
||||
|
/// <typeparam name="TValue">The type of the property's value.</typeparam>
|
||||
|
/// <param name="name">The name of the property.</param>
|
||||
|
/// <param name="defaultValue">The default value of the property.</param>
|
||||
|
/// <param name="inherits">Whether the property inherits its value.</param>
|
||||
|
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
|
||||
|
/// <returns>A <see cref="PerspexProperty{TValue}"/></returns>
|
||||
|
public static PerspexProperty<TValue> RegisterAttached<TOwner, THost, TValue>( |
||||
|
string name, |
||||
|
TValue defaultValue = default(TValue), |
||||
|
bool inherits = false, |
||||
|
BindingMode defaultBindingMode = BindingMode.OneWay) |
||||
|
where TOwner : PerspexObject |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
|
||||
|
PerspexProperty<TValue> result = new PerspexProperty<TValue>( |
||||
|
name, |
||||
|
typeof(TOwner), |
||||
|
defaultValue, |
||||
|
inherits, |
||||
|
defaultBindingMode); |
||||
|
|
||||
|
PerspexObject.Register(typeof(THost), result); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides access to a property's binding via the <see cref="PerspexObject"/>
|
||||
|
/// indexer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>A <see cref="Binding"/> describing the binding.</returns>
|
||||
|
public static Binding operator!(PerspexProperty property) |
||||
|
{ |
||||
|
return new Binding |
||||
|
{ |
||||
|
Priority = BindingPriority.LocalValue, |
||||
|
Property = property, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides access to a property's template binding via the <see cref="PerspexObject"/>
|
||||
|
/// indexer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="property">The property.</param>
|
||||
|
/// <returns>A <see cref="Binding"/> describing the binding.</returns>
|
||||
|
public static Binding operator ~(PerspexProperty property) |
||||
|
{ |
||||
|
return new Binding |
||||
|
{ |
||||
|
Priority = BindingPriority.TemplatedParent, |
||||
|
Property = property, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns a binding accessor that can be passed to <see cref="PerspexObject"/>'s []
|
||||
|
/// operator to initiate a binding.
|
||||
|
/// </summary>
|
||||
|
/// <returns>A <see cref="Binding"/>.</returns>
|
||||
|
/// <remarks>
|
||||
|
/// The ! and ~ operators are short forms of this.
|
||||
|
/// </remarks>
|
||||
|
public Binding Bind() |
||||
|
{ |
||||
|
return new Binding |
||||
|
{ |
||||
|
Property = this, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the default value for the property on the specified type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type.</param>
|
||||
|
/// <returns>The default value.</returns>
|
||||
|
public object GetDefaultValue(Type type) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(type != null); |
||||
|
|
||||
|
while (type != null) |
||||
|
{ |
||||
|
object result; |
||||
|
|
||||
|
if (this.defaultValues.TryGetValue(type, out result)) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
type = type.GetTypeInfo().BaseType; |
||||
|
} |
||||
|
|
||||
|
return this.defaultValues[this.OwnerType]; |
||||
|
} |
||||
|
|
||||
|
public bool IsValidValue(object value) |
||||
|
{ |
||||
|
if (value == UnsetValue) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else if (value == null) |
||||
|
{ |
||||
|
return !this.PropertyType.GetTypeInfo().IsValueType || |
||||
|
Nullable.GetUnderlyingType(this.PropertyType) != null; |
||||
|
} |
||||
|
|
||||
|
return this.PropertyType.GetTypeInfo().IsAssignableFrom(value.GetType().GetTypeInfo()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the default value for the property on the specified type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type.</param>
|
||||
|
/// <param name="defaultValue">The default value.</param>
|
||||
|
public void OverrideDefaultValue(Type type, object defaultValue) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(type != null); |
||||
|
|
||||
|
// TODO: Ensure correct type.
|
||||
|
|
||||
|
if (this.defaultValues.ContainsKey(type)) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Default value is already set for this property."); |
||||
|
} |
||||
|
|
||||
|
this.defaultValues.Add(type, defaultValue); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return this.Name; |
||||
|
} |
||||
|
|
||||
|
internal void NotifyChanged(PerspexPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
this.changed.OnNext(e); |
||||
|
} |
||||
|
|
||||
|
private class Unset |
||||
|
{ |
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return "{Unset}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A typed perspex property.
|
||||
|
/// </summary>
|
||||
|
public class PerspexProperty<TValue> : PerspexProperty |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PerspexProperty"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name of the property.</param>
|
||||
|
/// <param name="ownerType">The type of the class that registers the property.</param>
|
||||
|
/// <param name="defaultValue">The default value of the property.</param>
|
||||
|
/// <param name="inherits">Whether the property inherits its value.</param>
|
||||
|
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
|
||||
|
public PerspexProperty( |
||||
|
string name, |
||||
|
Type ownerType, |
||||
|
TValue defaultValue, |
||||
|
bool inherits, |
||||
|
BindingMode defaultBindingMode) |
||||
|
: base(name, typeof(TValue), ownerType, defaultValue, inherits, defaultBindingMode) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
Contract.Requires<NullReferenceException>(ownerType != null); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers the property on another type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TOwner">The type of the additional owner.</typeparam>
|
||||
|
/// <returns>The property.</returns>
|
||||
|
public PerspexProperty<TValue> AddOwner<TOwner>() |
||||
|
{ |
||||
|
PerspexObject.Register(typeof(TOwner), this); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the default value for the property on the specified type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type.</typeparam>
|
||||
|
/// <returns>The default value.</returns>
|
||||
|
public TValue GetDefaultValue<T>() |
||||
|
{ |
||||
|
return (TValue)this.GetDefaultValue(typeof(T)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PerspexPropertyChangedEventArgs.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides information for a perspex property change.
|
||||
|
/// </summary>
|
||||
|
public class PerspexPropertyChangedEventArgs |
||||
|
{ |
||||
|
public PerspexPropertyChangedEventArgs( |
||||
|
PerspexObject sender, |
||||
|
PerspexProperty property, |
||||
|
object oldValue, |
||||
|
object newValue) |
||||
|
{ |
||||
|
this.Sender = sender; |
||||
|
this.Property = property; |
||||
|
this.OldValue = oldValue; |
||||
|
this.NewValue = newValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="PerspexObject"/> that the property changed on.
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
public PerspexObject Sender { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the property that changed.
|
||||
|
/// </summary>
|
||||
|
public PerspexProperty Property { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the old value of the property.
|
||||
|
/// </summary>
|
||||
|
public object OldValue { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the new value of the property.
|
||||
|
/// </summary>
|
||||
|
public object NewValue { get; private set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IPlatformThreadingInterface.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Platform |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Threading; |
||||
|
|
||||
|
public interface IPlatformThreadingInterface |
||||
|
{ |
||||
|
Dispatcher GetThreadDispatcher(); |
||||
|
|
||||
|
void KillTimer(object timerHandle); |
||||
|
|
||||
|
object StartTimer(TimeSpan interval, Action internalTick); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,405 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PriorityValue.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Disposables; |
||||
|
using System.Reactive.Subjects; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Maintains a list of prioritised bindings together with a current value.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Bindings, in the form of <see cref="IObservable<object>"/>s are added to the object using
|
||||
|
/// the <see cref="Add"/> method. With the observable is passed a priority, where lower values
|
||||
|
/// represent higher priorites. The current <see cref="Value"/> is selected from the highest
|
||||
|
/// priority binding that doesn't return <see cref="PerspexProperty.UnsetValue"/>. Where there
|
||||
|
/// are multiple bindings registered with the same priority, the most recently added binding
|
||||
|
/// has a higher priority. Each time the value changes to a distinct new value, the
|
||||
|
/// <see cref="Changed"/> observable is fired with the old and new values.
|
||||
|
/// </remarks>
|
||||
|
public class PriorityValue |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The name of the property.
|
||||
|
/// </summary>
|
||||
|
private string name; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The value type.
|
||||
|
/// </summary>
|
||||
|
private Type valueType; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The currently registered binding entries.
|
||||
|
/// </summary>
|
||||
|
private LinkedList<BindingEntry> bindings = new LinkedList<BindingEntry>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The changed observable.
|
||||
|
/// </summary>
|
||||
|
private Subject<Tuple<object, object>> changed = new Subject<Tuple<object, object>>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The current value.
|
||||
|
/// </summary>
|
||||
|
private object value; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PriorityValue"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">The name of the property.</param>
|
||||
|
/// <param name="valueType">The value type.</param>
|
||||
|
public PriorityValue(string name, Type valueType) |
||||
|
{ |
||||
|
this.name = name; |
||||
|
this.valueType = valueType; |
||||
|
this.value = PerspexProperty.UnsetValue; |
||||
|
this.ValuePriority = int.MaxValue; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fired whenever the current <see cref="Value"/> changes to a new distinct value.
|
||||
|
/// </summary>
|
||||
|
public IObservable<Tuple<object, object>> Changed |
||||
|
{ |
||||
|
get { return this.changed; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the current value.
|
||||
|
/// </summary>
|
||||
|
public object Value |
||||
|
{ |
||||
|
get { return this.value; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the priority of the binding that is currently active.
|
||||
|
/// </summary>
|
||||
|
public int ValuePriority |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks whether a value is valid for a type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value"></param>
|
||||
|
/// <param name="propertyType"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static bool IsValidValue(object value, Type propertyType) |
||||
|
{ |
||||
|
TypeInfo type = propertyType.GetTypeInfo(); |
||||
|
|
||||
|
if (value == PerspexProperty.UnsetValue) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else if (value == null) |
||||
|
{ |
||||
|
if (type.IsValueType && |
||||
|
(!type.IsGenericType || !(type.GetGenericTypeDefinition() == typeof(Nullable<>)))) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (!type.IsAssignableFrom(value.GetType().GetTypeInfo())) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds a new binding.
|
||||
|
/// </summary>
|
||||
|
/// <param name="binding">The binding.</param>
|
||||
|
/// <param name="priority">The binding priority.</param>
|
||||
|
/// <returns>
|
||||
|
/// A disposable that will remove the binding.
|
||||
|
/// </returns>
|
||||
|
public IDisposable Add(IObservable<object> binding, int priority) |
||||
|
{ |
||||
|
BindingEntry entry = new BindingEntry(); |
||||
|
LinkedListNode<BindingEntry> insert = this.bindings.First; |
||||
|
|
||||
|
while (insert != null && insert.Value.Priority < priority) |
||||
|
{ |
||||
|
insert = insert.Next; |
||||
|
} |
||||
|
|
||||
|
if (insert == null) |
||||
|
{ |
||||
|
this.bindings.AddLast(entry); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.bindings.AddBefore(insert, entry); |
||||
|
} |
||||
|
|
||||
|
entry.Start(binding, priority, this.EntryChanged, this.EntryCompleted); |
||||
|
|
||||
|
return Disposable.Create(() => |
||||
|
{ |
||||
|
this.Remove(entry); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds a new binding, replacing all those of the same priority.
|
||||
|
/// </summary>
|
||||
|
/// <param name="binding">The binding.</param>
|
||||
|
/// <param name="priority">The binding priority.</param>
|
||||
|
/// <returns>
|
||||
|
/// A disposable that will remove the binding.
|
||||
|
/// </returns>
|
||||
|
public IDisposable Replace(IObservable<object> binding, int priority) |
||||
|
{ |
||||
|
BindingEntry entry = new BindingEntry(); |
||||
|
LinkedListNode<BindingEntry> insert = this.bindings.First; |
||||
|
|
||||
|
while (insert != null && insert.Value.Priority < priority) |
||||
|
{ |
||||
|
insert = insert.Next; |
||||
|
} |
||||
|
|
||||
|
while (insert != null && insert.Value.Priority == priority) |
||||
|
{ |
||||
|
LinkedListNode<BindingEntry> next = insert.Next; |
||||
|
insert.Value.Dispose(); |
||||
|
this.bindings.Remove(insert); |
||||
|
insert = next; |
||||
|
} |
||||
|
|
||||
|
if (insert == null) |
||||
|
{ |
||||
|
this.bindings.AddLast(entry); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.bindings.AddBefore(insert, entry); |
||||
|
} |
||||
|
|
||||
|
entry.Start(binding, priority, this.EntryChanged, this.EntryCompleted); |
||||
|
|
||||
|
return Disposable.Create(() => |
||||
|
{ |
||||
|
this.Remove(entry); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes all bindings with the specified priority.
|
||||
|
/// </summary>
|
||||
|
/// <param name="priority">The priority.</param>
|
||||
|
public void Clear(int priority) |
||||
|
{ |
||||
|
LinkedListNode<BindingEntry> item = this.bindings.First; |
||||
|
bool removed = false; |
||||
|
|
||||
|
while (item != null && item.Value.Priority <= priority) |
||||
|
{ |
||||
|
LinkedListNode<BindingEntry> next = item.Next; |
||||
|
|
||||
|
if (item.Value.Priority == priority) |
||||
|
{ |
||||
|
item.Value.Dispose(); |
||||
|
this.bindings.Remove(item); |
||||
|
removed = true; |
||||
|
} |
||||
|
|
||||
|
item = next; |
||||
|
} |
||||
|
|
||||
|
if (removed && priority <= this.ValuePriority) |
||||
|
{ |
||||
|
this.UpdateValue(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the currently active bindings on this object.
|
||||
|
/// </summary>
|
||||
|
/// <returns>An enumerable collection of bindings.</returns>
|
||||
|
public IEnumerable<BindingEntry> GetBindings() |
||||
|
{ |
||||
|
return this.bindings; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when a binding's value changes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="changed">The changed entry.</param>
|
||||
|
private void EntryChanged(BindingEntry changed) |
||||
|
{ |
||||
|
if (changed.Priority <= this.ValuePriority) |
||||
|
{ |
||||
|
this.UpdateValue(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Called when a binding completes.
|
||||
|
/// </summary>
|
||||
|
/// <param name="changed">The completed entry.</param>
|
||||
|
private void EntryCompleted(BindingEntry entry) |
||||
|
{ |
||||
|
this.Remove(entry); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the current value and notifies all observers.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">The new value.</param>
|
||||
|
/// <param name="priority">The priority of the binding which produced the value.</param>
|
||||
|
private void SetValue(object value, int priority) |
||||
|
{ |
||||
|
if (!IsValidValue(value, this.valueType)) |
||||
|
{ |
||||
|
throw new InvalidOperationException(string.Format( |
||||
|
"Invalid value for Property '{0}': {1} ({2})", |
||||
|
this.name, |
||||
|
value, |
||||
|
value.GetType().FullName)); |
||||
|
} |
||||
|
|
||||
|
object old = this.value; |
||||
|
|
||||
|
this.ValuePriority = priority; |
||||
|
|
||||
|
if (!EqualityComparer<object>.Default.Equals(old, value)) |
||||
|
{ |
||||
|
this.value = value; |
||||
|
this.changed.OnNext(Tuple.Create(old, value)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes the specified binding entry and updates the current value.
|
||||
|
/// </summary>
|
||||
|
/// <param name="entry">The binding entry to remove.</param>
|
||||
|
private void Remove(BindingEntry entry) |
||||
|
{ |
||||
|
entry.Dispose(); |
||||
|
this.bindings.Remove(entry); |
||||
|
this.UpdateValue(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Updates the current value.
|
||||
|
/// </summary>
|
||||
|
private void UpdateValue() |
||||
|
{ |
||||
|
foreach (BindingEntry entry in this.bindings) |
||||
|
{ |
||||
|
if (entry.Value != PerspexProperty.UnsetValue) |
||||
|
{ |
||||
|
this.SetValue(entry.Value, entry.Priority); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.SetValue(PerspexProperty.UnsetValue, int.MaxValue); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A registered binding.
|
||||
|
/// </summary>
|
||||
|
public class BindingEntry : IDisposable |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The binding subscription.
|
||||
|
/// </summary>
|
||||
|
private IDisposable subscription; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a description of the binding.
|
||||
|
/// </summary>
|
||||
|
public string Description |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The priority of the binding.
|
||||
|
/// </summary>
|
||||
|
public int Priority |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The current value of the binding.
|
||||
|
/// </summary>
|
||||
|
public object Value |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Starts listening to the specified binding.
|
||||
|
/// </summary>
|
||||
|
/// <param name="binding">The binding.</param>
|
||||
|
/// <param name="priority">The binding priority.</param>
|
||||
|
/// <param name="changed">Called when the binding changes.</param>
|
||||
|
/// <param name="completed">Called when the binding completes.</param>
|
||||
|
public void Start( |
||||
|
IObservable<object> binding, |
||||
|
int priority, |
||||
|
Action<BindingEntry> changed, |
||||
|
Action<BindingEntry> completed) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(binding != null); |
||||
|
Contract.Requires<ArgumentNullException>(changed != null); |
||||
|
Contract.Requires<ArgumentNullException>(completed != null); |
||||
|
|
||||
|
if (this.subscription != null) |
||||
|
{ |
||||
|
throw new Exception("PriorityValue.Entry.Start() called more than once."); |
||||
|
} |
||||
|
|
||||
|
this.Priority = priority; |
||||
|
this.Value = PerspexProperty.UnsetValue; |
||||
|
|
||||
|
if (binding is IDescription) |
||||
|
{ |
||||
|
this.Description = ((IDescription)binding).Description; |
||||
|
} |
||||
|
|
||||
|
this.subscription = binding.Subscribe( |
||||
|
value => |
||||
|
{ |
||||
|
this.Value = value; |
||||
|
changed(this); |
||||
|
}, |
||||
|
() => completed(this)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Ends the binding subscription.
|
||||
|
/// </summary>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (this.subscription != null) |
||||
|
{ |
||||
|
this.subscription.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Resources; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
[assembly: AssemblyTitle("Perspex.Base")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.Base")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2014")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
[assembly: NeutralResourcesLanguage("en")] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,102 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Dispatcher.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Security; |
||||
|
using System.Threading; |
||||
|
using Perspex.Platform; |
||||
|
using Splat; |
||||
|
|
||||
|
public enum DispatcherPriority |
||||
|
{ |
||||
|
Invalid = -1, |
||||
|
Inactive = 0, |
||||
|
SystemIdle = 1, |
||||
|
ApplicationIdle = 2, |
||||
|
ContextIdle = 3, |
||||
|
Background = 4, |
||||
|
Input = 5, |
||||
|
Loaded = 6, |
||||
|
Render = 7, |
||||
|
DataBind = 8, |
||||
|
Normal = 9, |
||||
|
Send = 10, |
||||
|
} |
||||
|
|
||||
|
[Flags] |
||||
|
internal enum Flags |
||||
|
{ |
||||
|
ShutdownStarted = 1, |
||||
|
Shutdown = 2, |
||||
|
Disabled = 4 |
||||
|
} |
||||
|
|
||||
|
public abstract class Dispatcher |
||||
|
{ |
||||
|
private static DispatcherFrame mainExecutionFrame = new DispatcherFrame(); |
||||
|
|
||||
|
public static Dispatcher CurrentDispatcher |
||||
|
{ |
||||
|
get { return Locator.Current.GetService<IPlatformThreadingInterface>().GetThreadDispatcher(); } |
||||
|
} |
||||
|
|
||||
|
public abstract bool HasShutdownFinished |
||||
|
{ |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
public abstract DispatcherFrame CurrentFrame |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public static void PushFrame(DispatcherFrame frame) |
||||
|
{ |
||||
|
if (frame == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException("frame"); |
||||
|
} |
||||
|
|
||||
|
Dispatcher dis = CurrentDispatcher; |
||||
|
|
||||
|
if (dis.HasShutdownFinished) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The Dispatcher has shut down"); |
||||
|
} |
||||
|
|
||||
|
if (frame.Running != null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Frame is already running on a different dispatcher"); |
||||
|
} |
||||
|
|
||||
|
frame.ParentFrame = dis.CurrentFrame; |
||||
|
dis.CurrentFrame = frame; |
||||
|
|
||||
|
frame.Running = dis; |
||||
|
|
||||
|
dis.RunFrame(frame); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static void Run() |
||||
|
{ |
||||
|
PushFrame(mainExecutionFrame); |
||||
|
} |
||||
|
|
||||
|
public abstract DispatcherOperation BeginInvoke(Action method); |
||||
|
|
||||
|
public abstract DispatcherOperation BeginInvoke(DispatcherPriority priority, Action method); |
||||
|
|
||||
|
protected internal abstract void Reprioritize(DispatcherOperation op, DispatcherPriority oldpriority); |
||||
|
|
||||
|
protected abstract void RunFrame(DispatcherFrame frame); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="DispatcherFrame.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Threading |
||||
|
{ |
||||
|
public class DispatcherFrame |
||||
|
{ |
||||
|
public DispatcherFrame() |
||||
|
{ |
||||
|
this.Continue = true; |
||||
|
this.ExitOnRequest = true; |
||||
|
} |
||||
|
|
||||
|
public bool Continue { get; set; } |
||||
|
|
||||
|
public bool ExitOnRequest { get; set; } |
||||
|
|
||||
|
public Dispatcher Running { get; set; } |
||||
|
|
||||
|
public DispatcherFrame ParentFrame { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="DispatcherOperation.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Security; |
||||
|
|
||||
|
public enum DispatcherOperationStatus |
||||
|
{ |
||||
|
Pending = 0, |
||||
|
Aborted = 1, |
||||
|
Completed = 2, |
||||
|
Executing = 3 |
||||
|
} |
||||
|
|
||||
|
public sealed class DispatcherOperation |
||||
|
{ |
||||
|
private DispatcherOperationStatus status; |
||||
|
private DispatcherPriority priority; |
||||
|
private Dispatcher dispatcher; |
||||
|
private object result; |
||||
|
private Delegate delegateMethod; |
||||
|
private object[] delegateArgs; |
||||
|
|
||||
|
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio) |
||||
|
{ |
||||
|
this.dispatcher = dis; |
||||
|
this.priority = prio; |
||||
|
if (this.Dispatcher.HasShutdownFinished) |
||||
|
{ |
||||
|
this.status = DispatcherOperationStatus.Aborted; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.status = DispatcherOperationStatus.Pending; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d) |
||||
|
: this(dis, prio) |
||||
|
{ |
||||
|
this.delegateMethod = d; |
||||
|
} |
||||
|
|
||||
|
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d, object arg) |
||||
|
: this(dis, prio) |
||||
|
{ |
||||
|
this.delegateMethod = d; |
||||
|
this.delegateArgs = new object[1]; |
||||
|
this.delegateArgs[0] = arg; |
||||
|
} |
||||
|
|
||||
|
public DispatcherOperation(Dispatcher dis, DispatcherPriority prio, Delegate d, object arg, object[] args) |
||||
|
: this(dis, prio) |
||||
|
{ |
||||
|
this.delegateMethod = d; |
||||
|
this.delegateArgs = new object[args.Length + 1]; |
||||
|
this.delegateArgs[0] = arg; |
||||
|
Array.Copy(args, 1, this.delegateArgs, 0, args.Length); |
||||
|
} |
||||
|
|
||||
|
public event EventHandler Completed; |
||||
|
|
||||
|
public DispatcherOperationStatus Status |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.status; |
||||
|
} |
||||
|
|
||||
|
internal set |
||||
|
{ |
||||
|
this.status = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Dispatcher Dispatcher |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.dispatcher; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DispatcherPriority Priority |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.priority; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (this.priority != value) |
||||
|
{ |
||||
|
DispatcherPriority old = this.priority; |
||||
|
this.priority = value; |
||||
|
this.dispatcher.Reprioritize(this, old); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object Result |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool Abort() |
||||
|
{ |
||||
|
this.status = DispatcherOperationStatus.Aborted; |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public DispatcherOperationStatus Wait() |
||||
|
{ |
||||
|
if (this.status == DispatcherOperationStatus.Executing) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Already executing"); |
||||
|
} |
||||
|
|
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
[SecurityCritical] |
||||
|
public DispatcherOperationStatus Wait(TimeSpan timeout) |
||||
|
{ |
||||
|
if (this.status == DispatcherOperationStatus.Executing) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Already executing"); |
||||
|
} |
||||
|
|
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Invoke() |
||||
|
{ |
||||
|
this.status = DispatcherOperationStatus.Executing; |
||||
|
|
||||
|
if (this.delegateMethod != null) |
||||
|
{ |
||||
|
this.result = this.delegateMethod.DynamicInvoke(this.delegateArgs); |
||||
|
} |
||||
|
|
||||
|
this.status = DispatcherOperationStatus.Completed; |
||||
|
|
||||
|
if (this.Completed != null) |
||||
|
{ |
||||
|
this.Completed(this, EventArgs.Empty); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,140 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="DispatcherTimer.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Threading |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Platform; |
||||
|
using Splat; |
||||
|
|
||||
|
public class DispatcherTimer |
||||
|
{ |
||||
|
private object timerHandle; |
||||
|
|
||||
|
private DispatcherPriority priority; |
||||
|
|
||||
|
private TimeSpan interval; |
||||
|
|
||||
|
public DispatcherTimer() |
||||
|
{ |
||||
|
this.priority = DispatcherPriority.Normal; |
||||
|
this.Dispatcher = Dispatcher.CurrentDispatcher; |
||||
|
} |
||||
|
|
||||
|
public DispatcherTimer(DispatcherPriority priority) |
||||
|
{ |
||||
|
this.priority = priority; |
||||
|
this.Dispatcher = Dispatcher.CurrentDispatcher; |
||||
|
} |
||||
|
|
||||
|
public DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher) |
||||
|
{ |
||||
|
this.priority = priority; |
||||
|
this.Dispatcher = dispatcher; |
||||
|
} |
||||
|
|
||||
|
public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback, Dispatcher dispatcher) |
||||
|
{ |
||||
|
this.priority = priority; |
||||
|
this.Dispatcher = dispatcher; |
||||
|
this.Interval = interval; |
||||
|
this.Tick += callback; |
||||
|
} |
||||
|
|
||||
|
~DispatcherTimer() |
||||
|
{ |
||||
|
if (this.timerHandle != null) |
||||
|
{ |
||||
|
this.Stop(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event EventHandler Tick; |
||||
|
|
||||
|
public Dispatcher Dispatcher |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public TimeSpan Interval |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.interval; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
bool enabled = this.IsEnabled; |
||||
|
this.Stop(); |
||||
|
this.interval = value; |
||||
|
this.IsEnabled = enabled; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool IsEnabled |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.timerHandle != null; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (this.IsEnabled != value) |
||||
|
{ |
||||
|
if (value) |
||||
|
{ |
||||
|
this.Start(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.Stop(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object Tag |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public void Start() |
||||
|
{ |
||||
|
if (!this.IsEnabled) |
||||
|
{ |
||||
|
IPlatformThreadingInterface threading = Locator.Current.GetService<IPlatformThreadingInterface>(); |
||||
|
this.timerHandle = threading.StartTimer(this.Interval, this.InternalTick); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Stop() |
||||
|
{ |
||||
|
if (this.IsEnabled) |
||||
|
{ |
||||
|
IPlatformThreadingInterface threading = Locator.Current.GetService<IPlatformThreadingInterface>(); |
||||
|
threading.KillTimer(this.timerHandle); |
||||
|
this.timerHandle = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void InternalTick() |
||||
|
{ |
||||
|
this.Dispatcher.BeginInvoke(this.priority, (Action)this.RaiseTick); |
||||
|
} |
||||
|
|
||||
|
private void RaiseTick() |
||||
|
{ |
||||
|
if (this.Tick != null) |
||||
|
{ |
||||
|
this.Tick(this, EventArgs.Empty); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="Rx-Core" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Interfaces" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Linq" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Main" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-PlatformServices" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Splat" version="1.3.3" targetFramework="portable-net45+win" /> |
||||
|
<package id="StyleCop.MSBuild" version="4.7.49.0" targetFramework="portable-net45+win" developmentDependency="true" /> |
||||
|
</packages> |
||||
@ -0,0 +1,125 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProjectGuid>{5CCB5571-7C30-4E7D-967D-0E2158EBD91F}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex.Controls.UnitTests</RootNamespace> |
||||
|
<AssemblyName>Perspex.Controls.UnitTests</AssemblyName> |
||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> |
||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
||||
|
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> |
||||
|
<IsCodedUITest>False</IsCodedUITest> |
||||
|
<TestProjectType>UnitTest</TestProjectType> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Moq"> |
||||
|
<HintPath>..\packages\Moq.4.2.1312.1622\lib\net40\Moq.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="Splat"> |
||||
|
<HintPath>..\packages\Splat.1.3.3\lib\Net45\Splat.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System" /> |
||||
|
</ItemGroup> |
||||
|
<Choose> |
||||
|
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> |
||||
|
</ItemGroup> |
||||
|
</When> |
||||
|
<Otherwise> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" /> |
||||
|
</ItemGroup> |
||||
|
</Otherwise> |
||||
|
</Choose> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="ContentControlTests.cs" /> |
||||
|
<Compile Include="ControlTests.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
<Compile Include="TemplatedControlTests.cs" /> |
||||
|
<Compile Include="TestRoot.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Perspex.Base\Perspex.Base.csproj"> |
||||
|
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
||||
|
<Name>Perspex.Base</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Controls\Perspex.Controls.csproj"> |
||||
|
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
||||
|
<Name>Perspex.Controls</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Input\Perspex.Input.csproj"> |
||||
|
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
||||
|
<Name>Perspex.Input</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
||||
|
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
||||
|
<Name>Perspex.Interactivity</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Layout\Perspex.Layout.csproj"> |
||||
|
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
||||
|
<Name>Perspex.Layout</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
||||
|
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
||||
|
<Name>Perspex.SceneGraph</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Styling\Perspex.Styling.csproj"> |
||||
|
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
||||
|
<Name>Perspex.Styling</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
</ItemGroup> |
||||
|
<Choose> |
||||
|
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> |
||||
|
<Private>False</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> |
||||
|
<Private>False</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> |
||||
|
<Private>False</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> |
||||
|
<Private>False</Private> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
</When> |
||||
|
</Choose> |
||||
|
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> |
||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
[assembly: AssemblyTitle("Perspex.Controls.UnitTests")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.Controls.UnitTests")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2014")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
|
||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
|
// to COM components. If you need to access a type in this assembly from
|
||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||
|
[assembly: ComVisible(false)] |
||||
|
|
||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
|
[assembly: Guid("0430478f-6126-4390-b94b-c02d91fd4f7c")] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="Moq" version="4.2.1312.1622" targetFramework="net45" /> |
||||
|
<package id="Splat" version="1.3.3" targetFramework="net45" /> |
||||
|
</packages> |
||||
@ -0,0 +1,62 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Border.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Layout; |
||||
|
using Perspex.Media; |
||||
|
|
||||
|
public class Border : Decorator |
||||
|
{ |
||||
|
static Border() |
||||
|
{ |
||||
|
AffectsRender(BackgroundProperty); |
||||
|
AffectsRender(BorderBrushProperty); |
||||
|
} |
||||
|
|
||||
|
public override void Render(IDrawingContext context) |
||||
|
{ |
||||
|
Brush background = this.Background; |
||||
|
Brush borderBrush = this.BorderBrush; |
||||
|
double borderThickness = this.BorderThickness; |
||||
|
Rect rect = new Rect(this.ActualSize).Deflate(BorderThickness / 2); |
||||
|
|
||||
|
if (background != null) |
||||
|
{ |
||||
|
context.FillRectange(background, rect); |
||||
|
} |
||||
|
|
||||
|
if (borderBrush != null && borderThickness > 0) |
||||
|
{ |
||||
|
context.DrawRectange(new Pen(borderBrush, borderThickness), rect); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
Control content = this.Content; |
||||
|
|
||||
|
if (content != null) |
||||
|
{ |
||||
|
Thickness padding = this.Padding + new Thickness(this.BorderThickness); |
||||
|
content.Arrange(new Rect(finalSize).Deflate(padding)); |
||||
|
} |
||||
|
|
||||
|
return finalSize; |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
return LayoutHelper.MeasureDecorator( |
||||
|
this, |
||||
|
this.Content, |
||||
|
availableSize, |
||||
|
this.Padding + new Thickness(this.BorderThickness)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Button.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Interactivity; |
||||
|
|
||||
|
public class Button : ContentControl |
||||
|
{ |
||||
|
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent = |
||||
|
RoutedEvent.Register<Button, RoutedEventArgs>("Click", RoutingStrategy.Bubble); |
||||
|
|
||||
|
static Button() |
||||
|
{ |
||||
|
FocusableProperty.OverrideDefaultValue(typeof(Button), true); |
||||
|
} |
||||
|
|
||||
|
public Button() |
||||
|
{ |
||||
|
this.PointerPressed += (s, e) => |
||||
|
{ |
||||
|
this.Classes.Add(":pressed"); |
||||
|
e.Device.Capture(this); |
||||
|
}; |
||||
|
|
||||
|
this.PointerReleased += (s, e) => |
||||
|
{ |
||||
|
e.Device.Capture(null); |
||||
|
this.Classes.Remove(":pressed"); |
||||
|
|
||||
|
if (this.Classes.Contains(":pointerover")) |
||||
|
{ |
||||
|
RoutedEventArgs click = new RoutedEventArgs(ClickEvent, this); |
||||
|
this.RaiseEvent(click); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<RoutedEventArgs> Click |
||||
|
{ |
||||
|
add { this.AddHandler(ClickEvent, value); } |
||||
|
remove { this.RemoveHandler(ClickEvent, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="CheckBox.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class CheckBox : ToggleButton |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ColumnDefinition.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class ColumnDefinition : DefinitionBase |
||||
|
{ |
||||
|
public static readonly PerspexProperty<double> MaxWidthProperty = |
||||
|
PerspexProperty.Register<ColumnDefinition, double>("MaxWidth", double.PositiveInfinity); |
||||
|
|
||||
|
public static readonly PerspexProperty<double> MinWidthProperty = |
||||
|
PerspexProperty.Register<ColumnDefinition, double>("MinWidth"); |
||||
|
|
||||
|
public static readonly PerspexProperty<GridLength> WidthProperty = |
||||
|
PerspexProperty.Register<ColumnDefinition, GridLength>("Width", new GridLength(1, GridUnitType.Star)); |
||||
|
|
||||
|
public ColumnDefinition() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public ColumnDefinition(GridLength width) |
||||
|
{ |
||||
|
this.Width = width; |
||||
|
} |
||||
|
|
||||
|
public double ActualWidth |
||||
|
{ |
||||
|
get; |
||||
|
internal set; |
||||
|
} |
||||
|
|
||||
|
public double MaxWidth |
||||
|
{ |
||||
|
get { return this.GetValue(MaxWidthProperty); } |
||||
|
set { this.SetValue(MaxWidthProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public double MinWidth |
||||
|
{ |
||||
|
get { return this.GetValue(MinWidthProperty); } |
||||
|
set { this.SetValue(MinWidthProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public GridLength Width |
||||
|
{ |
||||
|
get { return this.GetValue(WidthProperty); } |
||||
|
set { this.SetValue(WidthProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ColumnDefinitions.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class ColumnDefinitions : PerspexList<ColumnDefinition> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ContentControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
public class ContentControl : TemplatedControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<object> ContentProperty = |
||||
|
PerspexProperty.Register<ContentControl, object>("Content"); |
||||
|
|
||||
|
public object Content |
||||
|
{ |
||||
|
get { return this.GetValue(ContentProperty); } |
||||
|
set { this.SetValue(ContentProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,151 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ContentPresenter.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Perspex.Layout; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Styling; |
||||
|
|
||||
|
public class ContentPresenter : Control, IVisual |
||||
|
{ |
||||
|
public static readonly PerspexProperty<object> ContentProperty = |
||||
|
ContentControl.ContentProperty.AddOwner<Control>(); |
||||
|
|
||||
|
public static readonly PerspexProperty<Func<object, Visual>> DataTemplateProperty = |
||||
|
PerspexProperty.Register<ContentPresenter, Func<object, Visual>>("DataTemplate"); |
||||
|
|
||||
|
public ContentPresenter() |
||||
|
{ |
||||
|
this.GetObservableWithHistory(ContentProperty).Subscribe(this.ContentChanged); |
||||
|
} |
||||
|
|
||||
|
public object Content |
||||
|
{ |
||||
|
get { return this.GetValue(ContentProperty); } |
||||
|
set { this.SetValue(ContentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
protected override IEnumerable<Visual> CreateVisualChildren() |
||||
|
{ |
||||
|
object content = this.Content; |
||||
|
|
||||
|
if (content != null) |
||||
|
{ |
||||
|
Control result; |
||||
|
|
||||
|
if (content is Control) |
||||
|
{ |
||||
|
result = (Control)content; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
DataTemplate dataTemplate = this.FindDataTemplate(content); |
||||
|
|
||||
|
if (dataTemplate != null) |
||||
|
{ |
||||
|
result = dataTemplate.Build(content); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = new TextBlock |
||||
|
{ |
||||
|
Text = content.ToString(), |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result.TemplatedParent = null; |
||||
|
return Enumerable.Repeat(result, 1); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Enumerable.Empty<Visual>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; |
||||
|
|
||||
|
if (child != null) |
||||
|
{ |
||||
|
double left; |
||||
|
double top; |
||||
|
double width; |
||||
|
double height; |
||||
|
|
||||
|
switch (child.HorizontalAlignment) |
||||
|
{ |
||||
|
case HorizontalAlignment.Left: |
||||
|
left = 0; |
||||
|
width = child.DesiredSize.Value.Width; |
||||
|
break; |
||||
|
case HorizontalAlignment.Center: |
||||
|
left = (finalSize.Width / 2) - (child.DesiredSize.Value.Width / 2); |
||||
|
width = child.DesiredSize.Value.Width; |
||||
|
break; |
||||
|
case HorizontalAlignment.Right: |
||||
|
left = finalSize.Width - child.DesiredSize.Value.Width; |
||||
|
width = child.DesiredSize.Value.Width; |
||||
|
break; |
||||
|
default: |
||||
|
left = 0; |
||||
|
width = finalSize.Width; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
switch (child.VerticalAlignment) |
||||
|
{ |
||||
|
case VerticalAlignment.Top: |
||||
|
top = 0; |
||||
|
height = child.DesiredSize.Value.Height; |
||||
|
break; |
||||
|
case VerticalAlignment.Center: |
||||
|
top = (finalSize.Height / 2) - (child.DesiredSize.Value.Height / 2); |
||||
|
height = child.DesiredSize.Value.Height; |
||||
|
break; |
||||
|
case VerticalAlignment.Bottom: |
||||
|
top = finalSize.Height - child.DesiredSize.Value.Height; |
||||
|
height = child.DesiredSize.Value.Height; |
||||
|
break; |
||||
|
default: |
||||
|
top = 0; |
||||
|
height = finalSize.Height; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
child.Arrange(new Rect(left, top, width, height)); |
||||
|
} |
||||
|
|
||||
|
return finalSize; |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; |
||||
|
|
||||
|
if (child != null) |
||||
|
{ |
||||
|
child.Measure(availableSize); |
||||
|
return child.DesiredSize.Value; |
||||
|
} |
||||
|
|
||||
|
return new Size(); |
||||
|
} |
||||
|
|
||||
|
private void ContentChanged(Tuple<object, object> content) |
||||
|
{ |
||||
|
var children = this.CreateVisualChildren(); |
||||
|
this.ClearVisualChildren(); |
||||
|
this.AddVisualChildren(children); |
||||
|
this.InvalidateMeasure(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,270 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Control.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Rendering; |
||||
|
using Perspex.Styling; |
||||
|
using Splat; |
||||
|
|
||||
|
public class Control : InputElement, ILogical, IStyleable, IStyleHost |
||||
|
{ |
||||
|
public static readonly PerspexProperty<Brush> BackgroundProperty = |
||||
|
PerspexProperty.Register<Control, Brush>("Background", inherits: true); |
||||
|
|
||||
|
public static readonly PerspexProperty<Brush> BorderBrushProperty = |
||||
|
PerspexProperty.Register<Control, Brush>("BorderBrush"); |
||||
|
|
||||
|
public static readonly PerspexProperty<double> BorderThicknessProperty = |
||||
|
PerspexProperty.Register<Control, double>("BorderThickness"); |
||||
|
|
||||
|
public static readonly PerspexProperty<double> FontSizeProperty = |
||||
|
PerspexProperty.Register<Control, double>( |
||||
|
"FontSize", |
||||
|
defaultValue: 12.0, |
||||
|
inherits: true); |
||||
|
|
||||
|
public static readonly PerspexProperty<Brush> ForegroundProperty = |
||||
|
PerspexProperty.Register<Control, Brush>("Foreground", new SolidColorBrush(0xff000000), true); |
||||
|
|
||||
|
public static readonly PerspexProperty<Control> ParentProperty = |
||||
|
PerspexProperty.Register<Control, Control>("Parent"); |
||||
|
|
||||
|
public static readonly PerspexProperty<ITemplatedControl> TemplatedParentProperty = |
||||
|
PerspexProperty.Register<Control, ITemplatedControl>("TemplatedParent", inherits: true); |
||||
|
|
||||
|
private Classes classes; |
||||
|
|
||||
|
private DataTemplates dataTemplates; |
||||
|
|
||||
|
private string id; |
||||
|
|
||||
|
private Styles styles; |
||||
|
|
||||
|
static Control() |
||||
|
{ |
||||
|
AffectsMeasure(IsVisibleProperty); |
||||
|
} |
||||
|
|
||||
|
public Control() |
||||
|
{ |
||||
|
this.classes = new Classes(); |
||||
|
this.AddPseudoClass(IsPointerOverProperty, ":pointerover"); |
||||
|
this.AddPseudoClass(IsFocusedProperty, ":focus"); |
||||
|
} |
||||
|
|
||||
|
public Brush Background |
||||
|
{ |
||||
|
get { return this.GetValue(BackgroundProperty); } |
||||
|
set { this.SetValue(BackgroundProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Brush BorderBrush |
||||
|
{ |
||||
|
get { return this.GetValue(BorderBrushProperty); } |
||||
|
set { this.SetValue(BorderBrushProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public double BorderThickness |
||||
|
{ |
||||
|
get { return this.GetValue(BorderThicknessProperty); } |
||||
|
set { this.SetValue(BorderThicknessProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Classes Classes |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.classes; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (this.classes != value) |
||||
|
{ |
||||
|
this.classes.Clear(); |
||||
|
this.classes.Add(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DataTemplates DataTemplates |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (this.dataTemplates == null) |
||||
|
{ |
||||
|
this.dataTemplates = new DataTemplates(); |
||||
|
} |
||||
|
|
||||
|
return this.dataTemplates; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this.dataTemplates = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public double FontSize |
||||
|
{ |
||||
|
get { return this.GetValue(FontSizeProperty); } |
||||
|
set { this.SetValue(FontSizeProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Brush Foreground |
||||
|
{ |
||||
|
get { return this.GetValue(ForegroundProperty); } |
||||
|
set { this.SetValue(ForegroundProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public string Id |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.id; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (this.id != null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("ID already set."); |
||||
|
} |
||||
|
|
||||
|
if (((IVisual)this).VisualParent != null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("Cannot set ID : control already added to tree."); |
||||
|
} |
||||
|
|
||||
|
this.id = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Control Parent |
||||
|
{ |
||||
|
get { return this.GetValue(ParentProperty); } |
||||
|
protected set { this.SetValue(ParentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Styles Styles |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (this.styles == null) |
||||
|
{ |
||||
|
this.styles = new Styles(); |
||||
|
} |
||||
|
|
||||
|
return this.styles; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this.styles = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ITemplatedControl TemplatedParent |
||||
|
{ |
||||
|
get { return this.GetValue(TemplatedParentProperty); } |
||||
|
internal set { this.SetValue(TemplatedParentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
ILogical ILogical.LogicalParent |
||||
|
{ |
||||
|
get { return this.Parent; } |
||||
|
set { this.Parent = (Control)value; } |
||||
|
} |
||||
|
|
||||
|
IEnumerable<ILogical> ILogical.LogicalChildren |
||||
|
{ |
||||
|
get { return Enumerable.Empty<ILogical>(); } |
||||
|
} |
||||
|
|
||||
|
protected override void OnAttachedToVisualTree(IRenderRoot root) |
||||
|
{ |
||||
|
IStyler styler = Locator.Current.GetService<IStyler>(); |
||||
|
styler.ApplyStyles(this); |
||||
|
} |
||||
|
|
||||
|
protected void AddPseudoClass(PerspexProperty<bool> property, string className) |
||||
|
{ |
||||
|
this.GetObservable(property).Subscribe(x => |
||||
|
{ |
||||
|
if (x) |
||||
|
{ |
||||
|
this.classes.Add(className); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.classes.Remove(className); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected virtual DataTemplate FindDataTemplate(object content) |
||||
|
{ |
||||
|
Control control = content as Control; |
||||
|
|
||||
|
if (control != null) |
||||
|
{ |
||||
|
return new DataTemplate(x => control); |
||||
|
} |
||||
|
|
||||
|
ILogical node = this; |
||||
|
|
||||
|
while (node != null) |
||||
|
{ |
||||
|
control = node as Control; |
||||
|
|
||||
|
if (control != null) |
||||
|
{ |
||||
|
foreach (DataTemplate dt in control.DataTemplates.Reverse()) |
||||
|
{ |
||||
|
if (dt.Match(content)) |
||||
|
{ |
||||
|
return dt; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
node = node.LogicalParent; |
||||
|
|
||||
|
if (node == null && control != null) |
||||
|
{ |
||||
|
node = control.TemplatedParent as ILogical; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
IGlobalDataTemplates global = Locator.Current.GetService<IGlobalDataTemplates>(); |
||||
|
|
||||
|
if (global != null) |
||||
|
{ |
||||
|
foreach (DataTemplate dt in global.Reverse()) |
||||
|
{ |
||||
|
if (dt.Match(content)) |
||||
|
{ |
||||
|
return dt; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected DataTemplate GetDataTemplate(object content) |
||||
|
{ |
||||
|
return this.FindDataTemplate(content) ?? DataTemplate.Default; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ControlExtensions.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Styling; |
||||
|
|
||||
|
public static class ControlExtensions |
||||
|
{ |
||||
|
public static IEnumerable<Control> GetTemplateControls(this ITemplatedControl control) |
||||
|
{ |
||||
|
return GetTemplateControls(control, (IVisual)control); |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<Control> GetTemplateControls(ITemplatedControl templated, IVisual parent) |
||||
|
{ |
||||
|
IVisual visual = parent as IVisual; |
||||
|
|
||||
|
foreach (IVisual child in visual.VisualChildren.OfType<Control>().Where(x => x.TemplatedParent == templated)) |
||||
|
{ |
||||
|
yield return (Control)child; |
||||
|
|
||||
|
foreach (IVisual grandchild in GetTemplateControls(templated, child)) |
||||
|
{ |
||||
|
yield return (Control)grandchild; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ControlTemplate.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Styling; |
||||
|
|
||||
|
public class ControlTemplate |
||||
|
{ |
||||
|
private Func<ITemplatedControl, Control> build; |
||||
|
|
||||
|
public ControlTemplate(Func<ITemplatedControl, Control> build) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(build != null); |
||||
|
|
||||
|
this.build = build; |
||||
|
} |
||||
|
|
||||
|
public static ControlTemplate Create<TControl>(Func<TControl, Control> build) |
||||
|
where TControl : ITemplatedControl |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(build != null); |
||||
|
|
||||
|
return new ControlTemplate(c => build((TControl)c)); |
||||
|
} |
||||
|
|
||||
|
public Control Build(ITemplatedControl templatedParent) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(templatedParent != null); |
||||
|
|
||||
|
Control root = this.build(templatedParent); |
||||
|
root.TemplatedParent = templatedParent; |
||||
|
return root; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Controls.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class Controls : PerspexList<Control> |
||||
|
{ |
||||
|
public Controls() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Controls(IEnumerable<Control> items) |
||||
|
: base(items) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="DataTemplate.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
public class DataTemplate |
||||
|
{ |
||||
|
public static readonly DataTemplate Default = |
||||
|
new DataTemplate(typeof(object), o => new TextBlock { Text = o.ToString() }); |
||||
|
|
||||
|
public DataTemplate(Func<object, Control> build) |
||||
|
: this(o => true, build) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public DataTemplate(Type type, Func<object, Control> build) |
||||
|
: this(o => IsInstance(o, type), build) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public DataTemplate(Func<object, bool> match, Func<object, Control> build) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(match != null); |
||||
|
Contract.Requires<ArgumentNullException>(build != null); |
||||
|
|
||||
|
this.Match = match; |
||||
|
this.Build = build; |
||||
|
} |
||||
|
|
||||
|
public static bool IsInstance(object o, Type t) |
||||
|
{ |
||||
|
return t.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo()); |
||||
|
} |
||||
|
|
||||
|
public Func<object, bool> Match { get; private set; } |
||||
|
|
||||
|
public Func<object, Control> Build { get; private set; } |
||||
|
} |
||||
|
|
||||
|
public class DataTemplate<T> : DataTemplate |
||||
|
{ |
||||
|
public DataTemplate(Func<T, Control> build) |
||||
|
: base(typeof(T), Cast(build)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public DataTemplate(Func<T, bool> match, Func<T, Control> build) |
||||
|
: base(CastMatch(match), Cast(build)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private static Func<object, bool> CastMatch(Func<T, bool> f) |
||||
|
{ |
||||
|
return o => (o is T) ? f((T)o) : false; |
||||
|
} |
||||
|
|
||||
|
private static Func<object, TResult> Cast<TResult>(Func<T, TResult> f) |
||||
|
{ |
||||
|
return o => f((T)o); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="DataTemplates.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.Linq; |
||||
|
using System.Reactive; |
||||
|
using System.Reactive.Subjects; |
||||
|
|
||||
|
public class DataTemplates : PerspexList<DataTemplate> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Decorator.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Layout; |
||||
|
|
||||
|
public class Decorator : Control, IVisual |
||||
|
{ |
||||
|
public static readonly PerspexProperty<Control> ContentProperty = |
||||
|
PerspexProperty.Register<Decorator, Control>("Content"); |
||||
|
|
||||
|
public static readonly PerspexProperty<Thickness> PaddingProperty = |
||||
|
PerspexProperty.Register<Decorator, Thickness>("Padding"); |
||||
|
|
||||
|
public Decorator() |
||||
|
{ |
||||
|
this.GetObservable(ContentProperty).Subscribe(x => |
||||
|
{ |
||||
|
this.ClearVisualChildren(); |
||||
|
|
||||
|
if (x != null) |
||||
|
{ |
||||
|
this.AddVisualChild(x); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public Control Content |
||||
|
{ |
||||
|
get { return this.GetValue(ContentProperty); } |
||||
|
set { this.SetValue(ContentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Thickness Padding |
||||
|
{ |
||||
|
get { return this.GetValue(PaddingProperty); } |
||||
|
set { this.SetValue(PaddingProperty, value); } |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
Control content = this.Content; |
||||
|
|
||||
|
if (content != null) |
||||
|
{ |
||||
|
content.Arrange(new Rect(finalSize).Deflate(this.Padding)); |
||||
|
} |
||||
|
|
||||
|
return finalSize; |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
return LayoutHelper.MeasureDecorator(this, this.Content, availableSize, this.Padding); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="DefinitionBase.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class DefinitionBase : PerspexObject |
||||
|
{ |
||||
|
public static readonly PerspexProperty<string> SharedSizeGroupProperty = |
||||
|
PerspexProperty.Register<DefinitionBase, string>("SharedSizeGroup", inherits: true); |
||||
|
|
||||
|
public string SharedSizeGroup |
||||
|
{ |
||||
|
get { return this.GetValue(SharedSizeGroupProperty); } |
||||
|
set { this.SetValue(SharedSizeGroupProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,805 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Grid.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
public class Grid : Panel |
||||
|
{ |
||||
|
public static readonly PerspexProperty<int> ColumnProperty = |
||||
|
PerspexProperty.RegisterAttached<Grid, Control, int>("Column"); |
||||
|
|
||||
|
public static readonly PerspexProperty<int> ColumnSpanProperty = |
||||
|
PerspexProperty.RegisterAttached<Grid, Control, int>("ColumnSpan", 1); |
||||
|
|
||||
|
public static readonly PerspexProperty<bool> IsSharedSizeScopeProperty = |
||||
|
PerspexProperty.RegisterAttached<Grid, Control, bool>("IsSharedSizeScopeProperty"); |
||||
|
|
||||
|
public static readonly PerspexProperty<int> RowProperty = |
||||
|
PerspexProperty.RegisterAttached<Grid, Control, int>("Row"); |
||||
|
|
||||
|
public static readonly PerspexProperty<int> RowSpanProperty = |
||||
|
PerspexProperty.RegisterAttached<Grid, Control, int>("RowSpan", 1); |
||||
|
|
||||
|
private Segment[,] rowMatrix; |
||||
|
private Segment[,] colMatrix; |
||||
|
|
||||
|
public Grid() |
||||
|
{ |
||||
|
this.ColumnDefinitions = new ColumnDefinitions(); |
||||
|
this.RowDefinitions = new RowDefinitions(); |
||||
|
} |
||||
|
|
||||
|
public ColumnDefinitions ColumnDefinitions |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public RowDefinitions RowDefinitions |
||||
|
{ |
||||
|
get; |
||||
|
set; |
||||
|
} |
||||
|
|
||||
|
public static int GetColumn(PerspexObject element) |
||||
|
{ |
||||
|
return element.GetValue(ColumnProperty); |
||||
|
} |
||||
|
|
||||
|
public static int GetColumnSpan(PerspexObject element) |
||||
|
{ |
||||
|
return element.GetValue(ColumnSpanProperty); |
||||
|
} |
||||
|
|
||||
|
public static int GetRow(PerspexObject element) |
||||
|
{ |
||||
|
return element.GetValue(RowProperty); |
||||
|
} |
||||
|
|
||||
|
public static int GetRowSpan(PerspexObject element) |
||||
|
{ |
||||
|
return element.GetValue(RowSpanProperty); |
||||
|
} |
||||
|
|
||||
|
public static void SetColumn(PerspexObject element, int value) |
||||
|
{ |
||||
|
element.SetValue(ColumnProperty, value); |
||||
|
} |
||||
|
|
||||
|
public static void SetColumnSpan(PerspexObject element, int value) |
||||
|
{ |
||||
|
element.SetValue(ColumnSpanProperty, value); |
||||
|
} |
||||
|
|
||||
|
public static void SetRow(PerspexObject element, int value) |
||||
|
{ |
||||
|
element.SetValue(RowProperty, value); |
||||
|
} |
||||
|
|
||||
|
public static void SetRowSpan(PerspexObject element, int value) |
||||
|
{ |
||||
|
element.SetValue(RowSpanProperty, value); |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size constraint) |
||||
|
{ |
||||
|
Size totalSize = constraint; |
||||
|
int colCount = this.ColumnDefinitions.Count; |
||||
|
int rowCount = this.RowDefinitions.Count; |
||||
|
double totalStarsX = 0; |
||||
|
double totalStarsY = 0; |
||||
|
bool emptyRows = rowCount == 0; |
||||
|
bool emptyCols = colCount == 0; |
||||
|
bool hasChildren = this.Children.Count > 0; |
||||
|
|
||||
|
if (emptyRows) |
||||
|
{ |
||||
|
rowCount = 1; |
||||
|
} |
||||
|
|
||||
|
if (emptyCols) |
||||
|
{ |
||||
|
colCount = 1; |
||||
|
} |
||||
|
|
||||
|
this.CreateMatrices(rowCount, colCount); |
||||
|
|
||||
|
if (emptyRows) |
||||
|
{ |
||||
|
this.rowMatrix[0, 0] = new Segment(0, 0, double.PositiveInfinity, GridUnitType.Star); |
||||
|
this.rowMatrix[0, 0].Stars = 1.0; |
||||
|
totalStarsY += 1.0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (int i = 0; i < rowCount; i++) |
||||
|
{ |
||||
|
RowDefinition rowdef = this.RowDefinitions[i]; |
||||
|
GridLength height = rowdef.Height; |
||||
|
|
||||
|
rowdef.ActualHeight = double.PositiveInfinity; |
||||
|
this.rowMatrix[i, i] = new Segment(0, rowdef.MinHeight, rowdef.MaxHeight, height.GridUnitType); |
||||
|
|
||||
|
if (height.GridUnitType == GridUnitType.Pixel) |
||||
|
{ |
||||
|
this.rowMatrix[i, i].OfferedSize = Clamp(height.Value, this.rowMatrix[i, i].Min, this.rowMatrix[i, i].Max); |
||||
|
this.rowMatrix[i, i].DesiredSize = this.rowMatrix[i, i].OfferedSize; |
||||
|
rowdef.ActualHeight = this.rowMatrix[i, i].OfferedSize; |
||||
|
} |
||||
|
else if (height.GridUnitType == GridUnitType.Star) |
||||
|
{ |
||||
|
this.rowMatrix[i, i].Stars = height.Value; |
||||
|
totalStarsY += height.Value; |
||||
|
} |
||||
|
else if (height.GridUnitType == GridUnitType.Auto) |
||||
|
{ |
||||
|
this.rowMatrix[i, i].OfferedSize = Clamp(0, this.rowMatrix[i, i].Min, this.rowMatrix[i, i].Max); |
||||
|
this.rowMatrix[i, i].DesiredSize = this.rowMatrix[i, i].OfferedSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (emptyCols) |
||||
|
{ |
||||
|
this.colMatrix[0, 0] = new Segment(0, 0, double.PositiveInfinity, GridUnitType.Star); |
||||
|
this.colMatrix[0, 0].Stars = 1.0; |
||||
|
totalStarsX += 1.0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (int i = 0; i < colCount; i++) |
||||
|
{ |
||||
|
ColumnDefinition coldef = this.ColumnDefinitions[i]; |
||||
|
GridLength width = coldef.Width; |
||||
|
|
||||
|
coldef.ActualWidth = double.PositiveInfinity; |
||||
|
this.colMatrix[i, i] = new Segment(0, coldef.MinWidth, coldef.MaxWidth, width.GridUnitType); |
||||
|
|
||||
|
if (width.GridUnitType == GridUnitType.Pixel) |
||||
|
{ |
||||
|
this.colMatrix[i, i].OfferedSize = Clamp(width.Value, this.colMatrix[i, i].Min, this.colMatrix[i, i].Max); |
||||
|
this.colMatrix[i, i].DesiredSize = this.colMatrix[i, i].OfferedSize; |
||||
|
coldef.ActualWidth = this.colMatrix[i, i].OfferedSize; |
||||
|
} |
||||
|
else if (width.GridUnitType == GridUnitType.Star) |
||||
|
{ |
||||
|
this.colMatrix[i, i].Stars = width.Value; |
||||
|
totalStarsX += width.Value; |
||||
|
} |
||||
|
else if (width.GridUnitType == GridUnitType.Auto) |
||||
|
{ |
||||
|
this.colMatrix[i, i].OfferedSize = Clamp(0, this.colMatrix[i, i].Min, this.colMatrix[i, i].Max); |
||||
|
this.colMatrix[i, i].DesiredSize = this.colMatrix[i, i].OfferedSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
List<GridNode> sizes = new List<GridNode>(); |
||||
|
GridNode node; |
||||
|
GridNode separator = new GridNode(null, 0, 0, 0); |
||||
|
int separatorIndex; |
||||
|
|
||||
|
sizes.Add(separator); |
||||
|
|
||||
|
// Pre-process the grid children so that we know what types of elements we have so
|
||||
|
// we can apply our special measuring rules.
|
||||
|
GridWalker gridWalker = new GridWalker(this, this.rowMatrix, this.colMatrix); |
||||
|
|
||||
|
for (int i = 0; i < 6; i++) |
||||
|
{ |
||||
|
// These bools tell us which grid element type we should be measuring. i.e.
|
||||
|
// 'star/auto' means we should measure elements with a star row and auto col
|
||||
|
bool autoAuto = i == 0; |
||||
|
bool starAuto = i == 1; |
||||
|
bool autoStar = i == 2; |
||||
|
bool starAutoAgain = i == 3; |
||||
|
bool nonStar = i == 4; |
||||
|
bool remainingStar = i == 5; |
||||
|
|
||||
|
if (hasChildren) |
||||
|
{ |
||||
|
this.ExpandStarCols(totalSize); |
||||
|
this.ExpandStarRows(totalSize); |
||||
|
} |
||||
|
|
||||
|
foreach (Control child in this.Children) |
||||
|
{ |
||||
|
int col, row; |
||||
|
int colspan, rowspan; |
||||
|
double childSizeX = 0; |
||||
|
double childSizeY = 0; |
||||
|
bool starCol = false; |
||||
|
bool starRow = false; |
||||
|
bool autoCol = false; |
||||
|
bool autoRow = false; |
||||
|
|
||||
|
col = Math.Min(GetColumn(child), colCount - 1); |
||||
|
row = Math.Min(GetRow(child), rowCount - 1); |
||||
|
colspan = Math.Min(GetColumnSpan(child), colCount - col); |
||||
|
rowspan = Math.Min(GetRowSpan(child), rowCount - row); |
||||
|
|
||||
|
for (int r = row; r < row + rowspan; r++) |
||||
|
{ |
||||
|
starRow |= this.rowMatrix[r, r].Type == GridUnitType.Star; |
||||
|
autoRow |= this.rowMatrix[r, r].Type == GridUnitType.Auto; |
||||
|
} |
||||
|
|
||||
|
for (int c = col; c < col + colspan; c++) |
||||
|
{ |
||||
|
starCol |= this.colMatrix[c, c].Type == GridUnitType.Star; |
||||
|
autoCol |= this.colMatrix[c, c].Type == GridUnitType.Auto; |
||||
|
} |
||||
|
|
||||
|
// This series of if statements checks whether or not we should measure
|
||||
|
// the current element and also if we need to override the sizes
|
||||
|
// passed to the Measure call.
|
||||
|
|
||||
|
// If the element has Auto rows and Auto columns and does not span Star
|
||||
|
// rows/cols it should only be measured in the auto_auto phase.
|
||||
|
// There are similar rules governing auto/star and star/auto elements.
|
||||
|
// NOTE: star/auto elements are measured twice. The first time with
|
||||
|
// an override for height, the second time without it.
|
||||
|
if (autoRow && autoCol && !starRow && !starCol) |
||||
|
{ |
||||
|
if (!autoAuto) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
childSizeX = double.PositiveInfinity; |
||||
|
childSizeY = double.PositiveInfinity; |
||||
|
} |
||||
|
else if (starRow && autoCol && !starCol) |
||||
|
{ |
||||
|
if (!(starAuto || starAutoAgain)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (starAuto && gridWalker.HasAutoStar) |
||||
|
{ |
||||
|
childSizeY = double.PositiveInfinity; |
||||
|
} |
||||
|
|
||||
|
childSizeX = double.PositiveInfinity; |
||||
|
} |
||||
|
else if (autoRow && starCol && !starRow) |
||||
|
{ |
||||
|
if (!autoStar) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
childSizeY = double.PositiveInfinity; |
||||
|
} |
||||
|
else if ((autoRow || autoCol) && !(starRow || starCol)) |
||||
|
{ |
||||
|
if (!nonStar) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (autoRow) |
||||
|
{ |
||||
|
childSizeY = double.PositiveInfinity; |
||||
|
} |
||||
|
|
||||
|
if (autoCol) |
||||
|
{ |
||||
|
childSizeX = double.PositiveInfinity; |
||||
|
} |
||||
|
} |
||||
|
else if (!(starRow || starCol)) |
||||
|
{ |
||||
|
if (!nonStar) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (!remainingStar) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (int r = row; r < row + rowspan; r++) |
||||
|
{ |
||||
|
childSizeY += this.rowMatrix[r, r].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
for (int c = col; c < col + colspan; c++) |
||||
|
{ |
||||
|
childSizeX += this.colMatrix[c, c].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
child.Measure(new Size(childSizeX, childSizeY)); |
||||
|
Size desired = child.DesiredSize.Value; |
||||
|
|
||||
|
// Elements distribute their height based on two rules:
|
||||
|
// 1) Elements with rowspan/colspan == 1 distribute their height first
|
||||
|
// 2) Everything else distributes in a LIFO manner.
|
||||
|
// As such, add all UIElements with rowspan/colspan == 1 after the separator in
|
||||
|
// the list and everything else before it. Then to process, just keep popping
|
||||
|
// elements off the end of the list.
|
||||
|
if (!starAuto) |
||||
|
{ |
||||
|
node = new GridNode(this.rowMatrix, row + rowspan - 1, row, desired.Height); |
||||
|
separatorIndex = sizes.IndexOf(separator); |
||||
|
sizes.Insert(node.Row == node.Column ? separatorIndex + 1 : separatorIndex, node); |
||||
|
} |
||||
|
|
||||
|
node = new GridNode(this.colMatrix, col + colspan - 1, col, desired.Width); |
||||
|
|
||||
|
separatorIndex = sizes.IndexOf(separator); |
||||
|
sizes.Insert(node.Row == node.Column ? separatorIndex + 1 : separatorIndex, node); |
||||
|
} |
||||
|
|
||||
|
sizes.Remove(separator); |
||||
|
|
||||
|
while (sizes.Count > 0) |
||||
|
{ |
||||
|
node = sizes.Last(); |
||||
|
node.Matrix[node.Row, node.Column].DesiredSize = Math.Max(node.Matrix[node.Row, node.Column].DesiredSize, node.Size); |
||||
|
this.AllocateDesiredSize(rowCount, colCount); |
||||
|
sizes.Remove(node); |
||||
|
} |
||||
|
|
||||
|
sizes.Add(separator); |
||||
|
} |
||||
|
|
||||
|
// Once we have measured and distributed all sizes, we have to store
|
||||
|
// the results. Every time we want to expand the rows/cols, this will
|
||||
|
// be used as the baseline.
|
||||
|
this.SaveMeasureResults(); |
||||
|
|
||||
|
sizes.Remove(separator); |
||||
|
|
||||
|
double gridSizeX = 0; |
||||
|
double gridSizeY = 0; |
||||
|
|
||||
|
for (int c = 0; c < colCount; c++) |
||||
|
{ |
||||
|
gridSizeX += this.colMatrix[c, c].DesiredSize; |
||||
|
} |
||||
|
|
||||
|
for (int r = 0; r < rowCount; r++) |
||||
|
{ |
||||
|
gridSizeY += this.rowMatrix[r, r].DesiredSize; |
||||
|
} |
||||
|
|
||||
|
return new Size(gridSizeX, gridSizeY); |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
int colCount = this.ColumnDefinitions.Count; |
||||
|
int rowCount = this.RowDefinitions.Count; |
||||
|
int colMatrixDim = this.colMatrix.GetUpperBound(0) + 1; |
||||
|
int rowMatrixDim = this.rowMatrix.GetUpperBound(0) + 1; |
||||
|
|
||||
|
this.RestoreMeasureResults(); |
||||
|
|
||||
|
double totalConsumedX = 0; |
||||
|
double totalConsumedY = 0; |
||||
|
|
||||
|
for (int c = 0; c < colMatrixDim; c++) |
||||
|
{ |
||||
|
this.colMatrix[c, c].OfferedSize = this.colMatrix[c, c].DesiredSize; |
||||
|
totalConsumedX += this.colMatrix[c, c].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
for (int r = 0; r < rowMatrixDim; r++) |
||||
|
{ |
||||
|
this.rowMatrix[r, r].OfferedSize = this.rowMatrix[r, r].DesiredSize; |
||||
|
totalConsumedY += this.rowMatrix[r, r].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
if (totalConsumedX != finalSize.Width) |
||||
|
{ |
||||
|
this.ExpandStarCols(finalSize); |
||||
|
} |
||||
|
|
||||
|
if (totalConsumedY != finalSize.Height) |
||||
|
{ |
||||
|
this.ExpandStarRows(finalSize); |
||||
|
} |
||||
|
|
||||
|
for (int c = 0; c < colCount; c++) |
||||
|
{ |
||||
|
this.ColumnDefinitions[c].ActualWidth = this.colMatrix[c, c].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
for (int r = 0; r < rowCount; r++) |
||||
|
{ |
||||
|
this.RowDefinitions[r].ActualHeight = this.rowMatrix[r, r].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
foreach (Control child in this.Children) |
||||
|
{ |
||||
|
int col = Math.Min(GetColumn(child), colMatrixDim - 1); |
||||
|
int row = Math.Min(GetRow(child), rowMatrixDim - 1); |
||||
|
int colspan = Math.Min(GetColumnSpan(child), colMatrixDim - col); |
||||
|
int rowspan = Math.Min(GetRowSpan(child), rowMatrixDim - row); |
||||
|
|
||||
|
double childFinalX = 0; |
||||
|
double childFinalY = 0; |
||||
|
double childFinalW = 0; |
||||
|
double childFinalH = 0; |
||||
|
|
||||
|
for (int c = 0; c < col; c++) |
||||
|
{ |
||||
|
childFinalX += this.colMatrix[c, c].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
for (int c = col; c < col + colspan; c++) |
||||
|
{ |
||||
|
childFinalW += this.colMatrix[c, c].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
for (int r = 0; r < row; r++) |
||||
|
{ |
||||
|
childFinalY += this.rowMatrix[r, r].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
for (int r = row; r < row + rowspan; r++) |
||||
|
{ |
||||
|
childFinalH += this.rowMatrix[r, r].OfferedSize; |
||||
|
} |
||||
|
|
||||
|
child.Arrange(new Rect(childFinalX, childFinalY, childFinalW, childFinalH)); |
||||
|
} |
||||
|
|
||||
|
return finalSize; |
||||
|
} |
||||
|
|
||||
|
private static double Clamp(double val, double min, double max) |
||||
|
{ |
||||
|
if (val < min) |
||||
|
{ |
||||
|
return min; |
||||
|
} |
||||
|
else if (val > max) |
||||
|
{ |
||||
|
return max; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return val; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void CreateMatrices(int rowCount, int colCount) |
||||
|
{ |
||||
|
if (this.rowMatrix == null || this.colMatrix == null || |
||||
|
this.rowMatrix.GetUpperBound(0) != rowCount - 1 || |
||||
|
this.colMatrix.GetUpperBound(0) != colCount - 1) |
||||
|
{ |
||||
|
this.rowMatrix = new Segment[rowCount, rowCount]; |
||||
|
this.colMatrix = new Segment[colCount, colCount]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ExpandStarCols(Size availableSize) |
||||
|
{ |
||||
|
int columnsCount = this.ColumnDefinitions.Count; |
||||
|
double width = availableSize.Width; |
||||
|
|
||||
|
for (int i = 0; i < this.colMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
if (this.colMatrix[i, i].Type == GridUnitType.Star) |
||||
|
{ |
||||
|
this.colMatrix[i, i].OfferedSize = 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
width = Math.Max(availableSize.Width - this.colMatrix[i, i].OfferedSize, 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.AssignSize(this.colMatrix, 0, this.colMatrix.GetUpperBound(0), ref width, GridUnitType.Star, false); |
||||
|
width = Math.Max(0, width); |
||||
|
|
||||
|
if (columnsCount > 0) |
||||
|
{ |
||||
|
for (int i = 0; i < this.colMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
if (this.colMatrix[i, i].Type == GridUnitType.Star) |
||||
|
{ |
||||
|
this.ColumnDefinitions[i].ActualWidth = this.colMatrix[i, i].OfferedSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ExpandStarRows(Size availableSize) |
||||
|
{ |
||||
|
int rowCount = this.RowDefinitions.Count; |
||||
|
double height = availableSize.Height; |
||||
|
|
||||
|
// When expanding star rows, we need to zero out their height before
|
||||
|
// calling AssignSize. AssignSize takes care of distributing the
|
||||
|
// available size when there are Mins and Maxs applied.
|
||||
|
for (int i = 0; i < this.rowMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
if (this.rowMatrix[i, i].Type == GridUnitType.Star) |
||||
|
{ |
||||
|
this.rowMatrix[i, i].OfferedSize = 0.0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
height = Math.Max(availableSize.Height - this.rowMatrix[i, i].OfferedSize, 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.AssignSize(this.rowMatrix, 0, this.rowMatrix.GetUpperBound(0), ref height, GridUnitType.Star, false); |
||||
|
|
||||
|
if (rowCount > 0) |
||||
|
{ |
||||
|
for (int i = 0; i < this.rowMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
if (this.rowMatrix[i, i].Type == GridUnitType.Star) |
||||
|
{ |
||||
|
this.RowDefinitions[i].ActualHeight = this.rowMatrix[i, i].OfferedSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void AssignSize( |
||||
|
Segment[,] matrix, |
||||
|
int start, |
||||
|
int end, |
||||
|
ref double size, |
||||
|
GridUnitType type, |
||||
|
bool desiredSize) |
||||
|
{ |
||||
|
double count = 0; |
||||
|
bool assigned; |
||||
|
|
||||
|
// Count how many segments are of the correct type. If we're measuring Star rows/cols
|
||||
|
// we need to count the number of stars instead.
|
||||
|
for (int i = start; i <= end; i++) |
||||
|
{ |
||||
|
double segmentSize = desiredSize ? matrix[i, i].DesiredSize : matrix[i, i].OfferedSize; |
||||
|
if (segmentSize < matrix[i, i].Max) |
||||
|
{ |
||||
|
count += type == GridUnitType.Star ? matrix[i, i].Stars : 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
double contribution = size / count; |
||||
|
|
||||
|
assigned = false; |
||||
|
|
||||
|
for (int i = start; i <= end; i++) |
||||
|
{ |
||||
|
double segmentSize = desiredSize ? matrix[i, i].DesiredSize : matrix[i, i].OfferedSize; |
||||
|
|
||||
|
if (!(matrix[i, i].Type == type && segmentSize < matrix[i, i].Max)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
double newsize = segmentSize; |
||||
|
newsize += contribution * (type == GridUnitType.Star ? matrix[i, i].Stars : 1); |
||||
|
newsize = Math.Min(newsize, matrix[i, i].Max); |
||||
|
assigned |= newsize > segmentSize; |
||||
|
size -= newsize - segmentSize; |
||||
|
|
||||
|
if (desiredSize) |
||||
|
{ |
||||
|
matrix[i, i].DesiredSize = newsize; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
matrix[i, i].OfferedSize = newsize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
while (assigned); |
||||
|
} |
||||
|
|
||||
|
private void AllocateDesiredSize(int rowCount, int colCount) |
||||
|
{ |
||||
|
// First allocate the heights of the RowDefinitions, then allocate
|
||||
|
// the widths of the ColumnDefinitions.
|
||||
|
for (int i = 0; i < 2; i++) |
||||
|
{ |
||||
|
Segment[,] matrix = i == 0 ? this.rowMatrix : this.colMatrix; |
||||
|
int count = i == 0 ? rowCount : colCount; |
||||
|
|
||||
|
for (int row = count - 1; row >= 0; row--) |
||||
|
{ |
||||
|
for (int col = row; col >= 0; col--) |
||||
|
{ |
||||
|
bool spansStar = false; |
||||
|
for (int j = row; j >= col; j--) |
||||
|
{ |
||||
|
spansStar |= matrix[j, j].Type == GridUnitType.Star; |
||||
|
} |
||||
|
|
||||
|
// This is the amount of pixels which must be available between the grid rows
|
||||
|
// at index 'col' and 'row'. i.e. if 'row' == 0 and 'col' == 2, there must
|
||||
|
// be at least 'matrix [row][col].size' pixels of height allocated between
|
||||
|
// all the rows in the range col -> row.
|
||||
|
double current = matrix[row, col].DesiredSize; |
||||
|
|
||||
|
// Count how many pixels have already been allocated between the grid rows
|
||||
|
// in the range col -> row. The amount of pixels allocated to each grid row/column
|
||||
|
// is found on the diagonal of the matrix.
|
||||
|
double totalAllocated = 0; |
||||
|
|
||||
|
for (int k = row; k >= col; k--) |
||||
|
{ |
||||
|
totalAllocated += matrix[k, k].DesiredSize; |
||||
|
} |
||||
|
|
||||
|
// If the size requirement has not been met, allocate the additional required
|
||||
|
// size between 'pixel' rows, then 'star' rows, finally 'auto' rows, until all
|
||||
|
// height has been assigned.
|
||||
|
if (totalAllocated < current) |
||||
|
{ |
||||
|
double additional = current - totalAllocated; |
||||
|
|
||||
|
if (spansStar) |
||||
|
{ |
||||
|
this.AssignSize(matrix, col, row, ref additional, GridUnitType.Star, true); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.AssignSize(matrix, col, row, ref additional, GridUnitType.Pixel, true); |
||||
|
this.AssignSize(matrix, col, row, ref additional, GridUnitType.Auto, true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (int r = 0; r < this.rowMatrix.GetUpperBound(0) + 1; r++) |
||||
|
{ |
||||
|
this.rowMatrix[r, r].OfferedSize = this.rowMatrix[r, r].DesiredSize; |
||||
|
} |
||||
|
|
||||
|
for (int c = 0; c < this.colMatrix.GetUpperBound(0) + 1; c++) |
||||
|
{ |
||||
|
this.colMatrix[c, c].OfferedSize = this.colMatrix[c, c].DesiredSize; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void SaveMeasureResults() |
||||
|
{ |
||||
|
for (int i = 0; i < this.rowMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
for (int j = 0; j < this.rowMatrix.GetUpperBound(0) + 1; j++) |
||||
|
{ |
||||
|
this.rowMatrix[i, j].OriginalSize = this.rowMatrix[i, j].OfferedSize; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < this.colMatrix.GetUpperBound(0); i++) |
||||
|
{ |
||||
|
for (int j = 0; j < this.colMatrix.GetUpperBound(0); j++) |
||||
|
{ |
||||
|
this.colMatrix[i, j].OriginalSize = this.colMatrix[i, j].OfferedSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void RestoreMeasureResults() |
||||
|
{ |
||||
|
for (int i = 0; i < this.rowMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
for (int j = 0; j < this.rowMatrix.GetUpperBound(0) + 1; j++) |
||||
|
{ |
||||
|
this.rowMatrix[i, j].OfferedSize = this.rowMatrix[i, j].OriginalSize; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < this.colMatrix.GetUpperBound(0) + 1; i++) |
||||
|
{ |
||||
|
for (int j = 0; j < this.colMatrix.GetUpperBound(0) + 1; j++) |
||||
|
{ |
||||
|
this.colMatrix[i, j].OfferedSize = this.colMatrix[i, j].OriginalSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private struct Segment |
||||
|
{ |
||||
|
public double OriginalSize; |
||||
|
public double Max; |
||||
|
public double Min; |
||||
|
public double DesiredSize; |
||||
|
public double OfferedSize; |
||||
|
public double Stars; |
||||
|
public GridUnitType Type; |
||||
|
|
||||
|
public Segment(double offeredSize, double min, double max, GridUnitType type) |
||||
|
{ |
||||
|
this.OriginalSize = 0; |
||||
|
this.Min = min; |
||||
|
this.Max = max; |
||||
|
this.DesiredSize = 0; |
||||
|
this.OfferedSize = offeredSize; |
||||
|
this.Stars = 0; |
||||
|
this.Type = type; |
||||
|
} |
||||
|
|
||||
|
public void Init(double offeredSize, double min, double max, GridUnitType type) |
||||
|
{ |
||||
|
this.OfferedSize = offeredSize; |
||||
|
this.Min = min; |
||||
|
this.Max = max; |
||||
|
this.Type = type; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private struct GridNode |
||||
|
{ |
||||
|
public int Row; |
||||
|
public int Column; |
||||
|
public double Size; |
||||
|
public Segment[,] Matrix; |
||||
|
|
||||
|
public GridNode(Segment[,] matrix, int row, int col, double size) |
||||
|
{ |
||||
|
this.Matrix = matrix; |
||||
|
this.Row = row; |
||||
|
this.Column = col; |
||||
|
this.Size = size; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class GridWalker |
||||
|
{ |
||||
|
public GridWalker(Grid grid, Segment[,] rowMatrix, Segment[,] colMatrix) |
||||
|
{ |
||||
|
foreach (Control child in grid.Children) |
||||
|
{ |
||||
|
bool starCol = false; |
||||
|
bool starRow = false; |
||||
|
bool autoCol = false; |
||||
|
bool autoRow = false; |
||||
|
|
||||
|
int col = Math.Min(Grid.GetColumn(child), colMatrix.GetUpperBound(0)); |
||||
|
int row = Math.Min(Grid.GetRow(child), rowMatrix.GetUpperBound(0)); |
||||
|
int colspan = Math.Min(Grid.GetColumnSpan(child), colMatrix.GetUpperBound(0)); |
||||
|
int rowspan = Math.Min(Grid.GetRowSpan(child), rowMatrix.GetUpperBound(0)); |
||||
|
|
||||
|
for (int r = row; r < row + rowspan; r++) |
||||
|
{ |
||||
|
starRow |= rowMatrix[r, r].Type == GridUnitType.Star; |
||||
|
autoRow |= rowMatrix[r, r].Type == GridUnitType.Auto; |
||||
|
} |
||||
|
|
||||
|
for (int c = col; c < col + colspan; c++) |
||||
|
{ |
||||
|
starCol |= colMatrix[c, c].Type == GridUnitType.Star; |
||||
|
autoCol |= colMatrix[c, c].Type == GridUnitType.Auto; |
||||
|
} |
||||
|
|
||||
|
this.HasAutoAuto |= autoRow && autoCol && !starRow && !starCol; |
||||
|
this.HasStarAuto |= starRow && autoCol; |
||||
|
this.HasAutoStar |= autoRow && starCol; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool HasAutoAuto { get; private set; } |
||||
|
|
||||
|
public bool HasStarAuto { get; private set; } |
||||
|
|
||||
|
public bool HasAutoStar { get; private set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="GridLength.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
public enum GridUnitType |
||||
|
{ |
||||
|
Auto = 0, |
||||
|
Pixel = 1, |
||||
|
Star = 2, |
||||
|
} |
||||
|
|
||||
|
public struct GridLength : IEquatable<GridLength> |
||||
|
{ |
||||
|
private GridUnitType type; |
||||
|
|
||||
|
private double value; |
||||
|
|
||||
|
public GridLength(double value) |
||||
|
: this(value, GridUnitType.Pixel) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public GridLength(double value, GridUnitType type) |
||||
|
{ |
||||
|
if (value < 0 || double.IsNaN(value) || double.IsInfinity(value)) |
||||
|
{ |
||||
|
throw new ArgumentException("Invalid value", "value"); |
||||
|
} |
||||
|
|
||||
|
if (type < GridUnitType.Auto || type > GridUnitType.Star) |
||||
|
{ |
||||
|
throw new ArgumentException("Invalid value", "type"); |
||||
|
} |
||||
|
|
||||
|
this.type = type; |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
public static GridLength Auto |
||||
|
{ |
||||
|
get { return new GridLength(0, GridUnitType.Auto); } |
||||
|
} |
||||
|
|
||||
|
public GridUnitType GridUnitType |
||||
|
{ |
||||
|
get { return this.type; } |
||||
|
} |
||||
|
|
||||
|
public bool IsAbsolute |
||||
|
{ |
||||
|
get { return this.type == GridUnitType.Pixel; } |
||||
|
} |
||||
|
|
||||
|
public bool IsAuto |
||||
|
{ |
||||
|
get { return this.type == GridUnitType.Auto; } |
||||
|
} |
||||
|
|
||||
|
public bool IsStar |
||||
|
{ |
||||
|
get { return this.type == GridUnitType.Star; } |
||||
|
} |
||||
|
|
||||
|
public double Value |
||||
|
{ |
||||
|
get { return this.value; } |
||||
|
} |
||||
|
|
||||
|
public static bool operator ==(GridLength a, GridLength b) |
||||
|
{ |
||||
|
return (a.IsAuto && b.IsAuto) || (a.value == b.value && a.type == b.type); |
||||
|
} |
||||
|
|
||||
|
public static bool operator !=(GridLength gl1, GridLength gl2) |
||||
|
{ |
||||
|
return !(gl1 == gl2); |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object o) |
||||
|
{ |
||||
|
if (o == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!(o is GridLength)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return this == (GridLength)o; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(GridLength gridLength) |
||||
|
{ |
||||
|
return this == gridLength; |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return this.value.GetHashCode() ^ this.type.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
if (this.IsAuto) |
||||
|
{ |
||||
|
return "Auto"; |
||||
|
} |
||||
|
|
||||
|
string s = this.value.ToString(); |
||||
|
return this.IsStar ? s + "*" : s; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ContentControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
public class HeaderedContentControl : ContentControl, IHeadered |
||||
|
{ |
||||
|
public static readonly PerspexProperty<object> HeaderProperty = |
||||
|
PerspexProperty.Register<ContentControl, object>("Header"); |
||||
|
|
||||
|
public object Header |
||||
|
{ |
||||
|
get { return this.GetValue(HeaderProperty); } |
||||
|
set { this.SetValue(HeaderProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="HeaderedItemsControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
public class HeaderedItemsControl : ItemsControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<object> HeaderProperty = |
||||
|
HeaderedContentControl.HeaderProperty.AddOwner<HeaderedItemsControl>(); |
||||
|
|
||||
|
public object Header |
||||
|
{ |
||||
|
get { return this.GetValue(HeaderProperty); } |
||||
|
set { this.SetValue(HeaderProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IGlobalDataTemplates.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
public interface IGlobalDataTemplates : IEnumerable<DataTemplate> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IHeadered.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public interface IHeadered |
||||
|
{ |
||||
|
object Header { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,115 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Image.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Media.Imaging; |
||||
|
|
||||
|
public class Image : Control |
||||
|
{ |
||||
|
public static readonly PerspexProperty<Bitmap> SourceProperty = |
||||
|
PerspexProperty.Register<Image, Bitmap>("Source"); |
||||
|
|
||||
|
public static readonly PerspexProperty<Stretch> StretchProperty = |
||||
|
PerspexProperty.Register<Image, Stretch>("Stretch", Stretch.Uniform); |
||||
|
|
||||
|
public Bitmap Source |
||||
|
{ |
||||
|
get { return this.GetValue(SourceProperty); } |
||||
|
set { this.SetValue(SourceProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Stretch Stretch |
||||
|
{ |
||||
|
get { return (Stretch)this.GetValue(StretchProperty); } |
||||
|
set { this.SetValue(StretchProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public override void Render(IDrawingContext drawingContext) |
||||
|
{ |
||||
|
Brush background = this.Background; |
||||
|
Bitmap source = this.Source; |
||||
|
|
||||
|
if (background != null) |
||||
|
{ |
||||
|
drawingContext.FillRectange(background, new Rect(this.ActualSize)); |
||||
|
} |
||||
|
|
||||
|
if (source != null) |
||||
|
{ |
||||
|
Rect viewPort = new Rect(this.ActualSize); |
||||
|
Size sourceSize = new Size(source.PixelWidth, source.PixelHeight); |
||||
|
Vector scale = CalculateScaling(this.ActualSize, sourceSize, this.Stretch); |
||||
|
Size scaledSize = sourceSize * scale; |
||||
|
Rect destRect = viewPort |
||||
|
.Center(new Rect(scaledSize)) |
||||
|
.Intersect(viewPort); |
||||
|
Rect sourceRect = new Rect(sourceSize) |
||||
|
.Center(new Rect(destRect.Size / scale)); |
||||
|
|
||||
|
drawingContext.DrawImage(source, 1, sourceRect, destRect); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static Vector CalculateScaling(Size availableSize, Size imageSize, Stretch stretch) |
||||
|
{ |
||||
|
double scaleX = 1; |
||||
|
double scaleY = 1; |
||||
|
|
||||
|
if (stretch != Stretch.None) |
||||
|
{ |
||||
|
scaleX = availableSize.Width / imageSize.Width; |
||||
|
scaleY = availableSize.Height / imageSize.Height; |
||||
|
|
||||
|
switch (stretch) |
||||
|
{ |
||||
|
case Stretch.Uniform: |
||||
|
scaleX = scaleY = Math.Min(scaleX, scaleY); |
||||
|
break; |
||||
|
case Stretch.UniformToFill: |
||||
|
scaleX = scaleY = Math.Max(scaleX, scaleY); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return new Vector(scaleX, scaleY); |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
double width = 0; |
||||
|
double height = 0; |
||||
|
Vector scale = new Vector(); |
||||
|
|
||||
|
if (this.Source != null) |
||||
|
{ |
||||
|
width = this.Source.PixelWidth; |
||||
|
height = this.Source.PixelHeight; |
||||
|
|
||||
|
if (this.Width > 0) |
||||
|
{ |
||||
|
availableSize = new Size(this.Width, availableSize.Height); |
||||
|
} |
||||
|
|
||||
|
if (this.Height > 0) |
||||
|
{ |
||||
|
availableSize = new Size(availableSize.Width, this.Height); |
||||
|
} |
||||
|
|
||||
|
scale = CalculateScaling(availableSize, new Size(width, height), this.Stretch); |
||||
|
} |
||||
|
|
||||
|
return new Size(width * scale.X, height * scale.Y); |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
return finalSize; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,137 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ItemsControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
|
||||
|
public class ItemsControl : TemplatedControl |
||||
|
{ |
||||
|
private static readonly ItemsPanelTemplate DefaultPanel = |
||||
|
new ItemsPanelTemplate(() => new StackPanel { Orientation = Orientation.Vertical }); |
||||
|
|
||||
|
public static readonly PerspexProperty<IEnumerable> ItemsProperty = |
||||
|
PerspexProperty.Register<ItemsControl, IEnumerable>("Items"); |
||||
|
|
||||
|
public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty = |
||||
|
PerspexProperty.Register<ItemsControl, ItemsPanelTemplate>("ItemsPanel", defaultValue: DefaultPanel); |
||||
|
|
||||
|
private Dictionary<object, Control> controlsByItem = new Dictionary<object, Control>(); |
||||
|
|
||||
|
private Dictionary<Control, object> itemsByControl = new Dictionary<Control, object>(); |
||||
|
|
||||
|
public ItemsControl() |
||||
|
{ |
||||
|
this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged); |
||||
|
} |
||||
|
|
||||
|
public IEnumerable Items |
||||
|
{ |
||||
|
get { return this.GetValue(ItemsProperty); } |
||||
|
set { this.SetValue(ItemsProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public ItemsPanelTemplate ItemsPanel |
||||
|
{ |
||||
|
get { return this.GetValue(ItemsPanelProperty); } |
||||
|
set { this.SetValue(ItemsPanelProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Control GetControlForItem(object item) |
||||
|
{ |
||||
|
Control result; |
||||
|
this.controlsByItem.TryGetValue(item, out result); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public object GetItemForControl(Control control) |
||||
|
{ |
||||
|
object result; |
||||
|
this.itemsByControl.TryGetValue(control, out result); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<Control> GetAllItemControls() |
||||
|
{ |
||||
|
return this.controlsByItem.Values; |
||||
|
} |
||||
|
|
||||
|
internal Control CreateItemControl(object item) |
||||
|
{ |
||||
|
Control control = this.CreateItemControlOverride(item); |
||||
|
this.itemsByControl.Add(control, item); |
||||
|
this.controlsByItem.Add(item, control); |
||||
|
return control; |
||||
|
} |
||||
|
|
||||
|
internal void RemoveItemControls(IEnumerable items) |
||||
|
{ |
||||
|
foreach (object i in items) |
||||
|
{ |
||||
|
Control control = this.GetControlForItem(i); |
||||
|
this.controlsByItem.Remove(i); |
||||
|
this.itemsByControl.Remove(control); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal void ClearItemControls() |
||||
|
{ |
||||
|
this.controlsByItem.Clear(); |
||||
|
this.itemsByControl.Clear(); |
||||
|
} |
||||
|
|
||||
|
protected virtual Control CreateItemControlOverride(object item) |
||||
|
{ |
||||
|
return (item as Control) ?? this.GetDataTemplate(item).Build(item); |
||||
|
} |
||||
|
|
||||
|
private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value) |
||||
|
{ |
||||
|
INotifyPropertyChanged inpc = value.Item1 as INotifyPropertyChanged; |
||||
|
|
||||
|
if (inpc != null) |
||||
|
{ |
||||
|
inpc.PropertyChanged -= ItemsPropertyChanged; |
||||
|
} |
||||
|
|
||||
|
if (value.Item2 == null || !value.Item2.OfType<object>().Any()) |
||||
|
{ |
||||
|
this.Classes.Add(":empty"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.Classes.Remove(":empty"); |
||||
|
} |
||||
|
|
||||
|
inpc = value.Item2 as INotifyPropertyChanged; |
||||
|
|
||||
|
if (inpc != null) |
||||
|
{ |
||||
|
inpc.PropertyChanged += ItemsPropertyChanged; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ItemsPropertyChanged(object sender, PropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.PropertyName == "Count") |
||||
|
{ |
||||
|
if (((IList)sender).Count == 0) |
||||
|
{ |
||||
|
this.Classes.Add(":empty"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.Classes.Remove(":empty"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class ItemsPanelTemplate |
||||
|
{ |
||||
|
public ItemsPanelTemplate(Func<Panel> build) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(build != null); |
||||
|
|
||||
|
this.Build = build; |
||||
|
} |
||||
|
|
||||
|
public Func<Panel> Build { get; private set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,178 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ItemsControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.Linq; |
||||
|
|
||||
|
public class ItemsPresenter : Control, IVisual |
||||
|
{ |
||||
|
public static readonly PerspexProperty<IEnumerable> ItemsProperty = |
||||
|
ItemsControl.ItemsProperty.AddOwner<ItemsPresenter>(); |
||||
|
|
||||
|
public static readonly PerspexProperty<ItemsPanelTemplate> ItemsPanelProperty = |
||||
|
ItemsControl.ItemsPanelProperty.AddOwner<ItemsPresenter>(); |
||||
|
|
||||
|
private Panel panel; |
||||
|
|
||||
|
public ItemsPresenter() |
||||
|
{ |
||||
|
this.GetObservableWithHistory(ItemsProperty).Subscribe(this.ItemsChanged); |
||||
|
} |
||||
|
|
||||
|
public IEnumerable Items |
||||
|
{ |
||||
|
get { return this.GetValue(ItemsProperty); } |
||||
|
set { this.SetValue(ItemsProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public ItemsPanelTemplate ItemsPanel |
||||
|
{ |
||||
|
get { return this.GetValue(ItemsPanelProperty); } |
||||
|
set { this.SetValue(ItemsPanelProperty, value); } |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
Panel panel = this.GetPanel(); |
||||
|
panel.Measure(availableSize); |
||||
|
return panel.DesiredSize.Value; |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
this.GetPanel().Arrange(new Rect(finalSize)); |
||||
|
return finalSize; |
||||
|
} |
||||
|
|
||||
|
private Control CreateItemControl(object item) |
||||
|
{ |
||||
|
ItemsControl i = this.TemplatedParent as ItemsControl; |
||||
|
|
||||
|
if (i != null) |
||||
|
{ |
||||
|
return i.CreateItemControl(item); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return this.GetDataTemplate(item).Build(item) as Control; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private IEnumerable<Control> CreateItemControls(IEnumerable items) |
||||
|
{ |
||||
|
if (items != null) |
||||
|
{ |
||||
|
return items |
||||
|
.Cast<object>() |
||||
|
.Select(x => this.CreateItemControl(x)) |
||||
|
.OfType<Control>(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return Enumerable.Empty<Control>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ClearItemControls() |
||||
|
{ |
||||
|
ItemsControl i = this.TemplatedParent as ItemsControl; |
||||
|
|
||||
|
if (i != null) |
||||
|
{ |
||||
|
i.ClearItemControls(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void RemoveItemControls(IEnumerable items) |
||||
|
{ |
||||
|
ItemsControl i = this.TemplatedParent as ItemsControl; |
||||
|
|
||||
|
if (i != null) |
||||
|
{ |
||||
|
i.RemoveItemControls(items); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Panel GetPanel() |
||||
|
{ |
||||
|
if (this.panel == null && this.ItemsPanel != null) |
||||
|
{ |
||||
|
this.panel = this.ItemsPanel.Build(); |
||||
|
this.ItemsChanged(Tuple.Create(default(IEnumerable), this.Items)); |
||||
|
} |
||||
|
|
||||
|
return this.panel; |
||||
|
} |
||||
|
|
||||
|
private void ItemsChanged(Tuple<IEnumerable, IEnumerable> value) |
||||
|
{ |
||||
|
if (value.Item1 != null) |
||||
|
{ |
||||
|
INotifyCollectionChanged incc = value.Item1 as INotifyCollectionChanged; |
||||
|
|
||||
|
if (incc != null) |
||||
|
{ |
||||
|
incc.CollectionChanged -= this.ItemsCollectionChanged; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.ClearItemControls(); |
||||
|
|
||||
|
if (this.panel != null) |
||||
|
{ |
||||
|
var controls = this.CreateItemControls(value.Item2).ToList(); |
||||
|
|
||||
|
foreach (var control in controls) |
||||
|
{ |
||||
|
control.TemplatedParent = null; |
||||
|
} |
||||
|
|
||||
|
this.panel.Children = new Controls(controls); |
||||
|
|
||||
|
INotifyCollectionChanged incc = value.Item2 as INotifyCollectionChanged; |
||||
|
|
||||
|
if (incc != null) |
||||
|
{ |
||||
|
incc.CollectionChanged += this.ItemsCollectionChanged; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
||||
|
{ |
||||
|
if (this.panel != null) |
||||
|
{ |
||||
|
// TODO: Handle Move and Replace.
|
||||
|
switch (e.Action) |
||||
|
{ |
||||
|
case NotifyCollectionChangedAction.Add: |
||||
|
var controls = this.CreateItemControls(e.NewItems).ToList(); |
||||
|
|
||||
|
foreach (var control in controls) |
||||
|
{ |
||||
|
control.TemplatedParent = null; |
||||
|
} |
||||
|
|
||||
|
this.panel.Children.AddRange(controls); |
||||
|
break; |
||||
|
|
||||
|
case NotifyCollectionChangedAction.Remove: |
||||
|
this.RemoveItemControls(e.OldItems); |
||||
|
break; |
||||
|
|
||||
|
case NotifyCollectionChangedAction.Reset: |
||||
|
this.ItemsChanged(Tuple.Create(this.Items, this.Items)); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Panel.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Base class for controls that can contain multiple children.
|
||||
|
/// </summary>
|
||||
|
public class Panel : Control |
||||
|
{ |
||||
|
private Controls children; |
||||
|
|
||||
|
public Controls Children |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (this.children == null) |
||||
|
{ |
||||
|
this.children = new Controls(); |
||||
|
} |
||||
|
|
||||
|
return this.children; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(value != null); |
||||
|
|
||||
|
if (this.children != value) |
||||
|
{ |
||||
|
this.children = value; |
||||
|
this.ClearVisualChildren(); |
||||
|
this.AddVisualChildren(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,138 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
||||
|
<PropertyGroup> |
||||
|
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProjectGuid>{D2221C82-4A25-4583-9B43-D791E3F6820C}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex.Controls</RootNamespace> |
||||
|
<AssemblyName>Perspex.Controls</AssemblyName> |
||||
|
<DefaultLanguage>en-US</DefaultLanguage> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile> |
||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="Border.cs" /> |
||||
|
<Compile Include="Button.cs" /> |
||||
|
<Compile Include="CheckBox.cs" /> |
||||
|
<Compile Include="ColumnDefinition.cs" /> |
||||
|
<Compile Include="ColumnDefinitions.cs" /> |
||||
|
<Compile Include="ContentControl.cs" /> |
||||
|
<Compile Include="ContentPresenter.cs" /> |
||||
|
<Compile Include="Control.cs" /> |
||||
|
<Compile Include="ControlExtensions.cs" /> |
||||
|
<Compile Include="Controls.cs" /> |
||||
|
<Compile Include="ControlTemplate.cs" /> |
||||
|
<Compile Include="DataTemplate.cs" /> |
||||
|
<Compile Include="IGlobalDataTemplates.cs" /> |
||||
|
<Compile Include="DataTemplates.cs" /> |
||||
|
<Compile Include="Decorator.cs" /> |
||||
|
<Compile Include="DefinitionBase.cs" /> |
||||
|
<Compile Include="Grid.cs" /> |
||||
|
<Compile Include="GridLength.cs" /> |
||||
|
<Compile Include="HeaderedContentControl.cs" /> |
||||
|
<Compile Include="HeaderedItemsControl.cs" /> |
||||
|
<Compile Include="IHeadered.cs" /> |
||||
|
<Compile Include="Image.cs" /> |
||||
|
<Compile Include="ItemsControl.cs" /> |
||||
|
<Compile Include="ItemsPanelTemplate.cs" /> |
||||
|
<Compile Include="ItemsPresenter.cs" /> |
||||
|
<Compile Include="Panel.cs" /> |
||||
|
<Compile Include="RowDefinition.cs" /> |
||||
|
<Compile Include="RowDefinitions.cs" /> |
||||
|
<Compile Include="SelectingItemsControl.cs" /> |
||||
|
<Compile Include="StackPanel.cs" /> |
||||
|
<Compile Include="TabControl.cs" /> |
||||
|
<Compile Include="TabItem.cs" /> |
||||
|
<Compile Include="TabStrip.cs" /> |
||||
|
<Compile Include="TemplatedControl.cs" /> |
||||
|
<Compile Include="TextBlock.cs" /> |
||||
|
<Compile Include="TextBox.cs" /> |
||||
|
<Compile Include="TextBoxView.cs" /> |
||||
|
<Compile Include="ToggleButton.cs" /> |
||||
|
<Compile Include="TreeDataTemplate.cs" /> |
||||
|
<Compile Include="TreeView.cs" /> |
||||
|
<Compile Include="TreeViewItem.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Splat"> |
||||
|
<HintPath>..\packages\Splat.1.3.3\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Core"> |
||||
|
<HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Core.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Interfaces"> |
||||
|
<HintPath>..\packages\Rx-Interfaces.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Interfaces.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Linq"> |
||||
|
<HintPath>..\packages\Rx-Linq.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Linq.dll</HintPath> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Perspex.Base\Perspex.Base.csproj"> |
||||
|
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
||||
|
<Name>Perspex.Base</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Input\Perspex.Input.csproj"> |
||||
|
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
||||
|
<Name>Perspex.Input</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
||||
|
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
||||
|
<Name>Perspex.Interactivity</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Layout\Perspex.Layout.csproj"> |
||||
|
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
||||
|
<Name>Perspex.Layout</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
||||
|
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
||||
|
<Name>Perspex.SceneGraph</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Styling\Perspex.Styling.csproj"> |
||||
|
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
||||
|
<Name>Perspex.Styling</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
||||
|
<Import Project="..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets" Condition="Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" /> |
||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> |
||||
|
<PropertyGroup> |
||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> |
||||
|
</PropertyGroup> |
||||
|
<Error Condition="!Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets'))" /> |
||||
|
</Target> |
||||
|
<!-- 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> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Resources; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
[assembly: AssemblyTitle("Perspex.Controls")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.Controls")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2014")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
[assembly: NeutralResourcesLanguage("en")] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,53 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RowDefinition.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class RowDefinition : DefinitionBase |
||||
|
{ |
||||
|
public static readonly PerspexProperty<double> MaxHeightProperty = |
||||
|
PerspexProperty.Register<RowDefinition, double>("MaxHeight", double.PositiveInfinity); |
||||
|
|
||||
|
public static readonly PerspexProperty<double> MinHeightProperty = |
||||
|
PerspexProperty.Register<RowDefinition, double>("MinHeight"); |
||||
|
|
||||
|
public static readonly PerspexProperty<GridLength> HeightProperty = |
||||
|
PerspexProperty.Register<RowDefinition, GridLength>("Height", new GridLength(1, GridUnitType.Star)); |
||||
|
|
||||
|
public RowDefinition() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public RowDefinition(GridLength height) |
||||
|
{ |
||||
|
this.Height = height; |
||||
|
} |
||||
|
|
||||
|
public double ActualHeight |
||||
|
{ |
||||
|
get; |
||||
|
internal set; |
||||
|
} |
||||
|
|
||||
|
public double MaxHeight |
||||
|
{ |
||||
|
get { return this.GetValue(MaxHeightProperty); } |
||||
|
set { this.SetValue(MaxHeightProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public double MinHeight |
||||
|
{ |
||||
|
get { return this.GetValue(MinHeightProperty); } |
||||
|
set { this.SetValue(MinHeightProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public GridLength Height |
||||
|
{ |
||||
|
get { return this.GetValue(HeightProperty); } |
||||
|
set { this.SetValue(HeightProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RowDefinitions.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class RowDefinitions : PerspexList<RowDefinition> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="SelectingItemsControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class SelectingItemsControl : ItemsControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<object> SelectedItemProperty = |
||||
|
PerspexProperty.Register<SelectingItemsControl, object>("SelectedItem"); |
||||
|
|
||||
|
public object SelectedItem |
||||
|
{ |
||||
|
get { return this.GetValue(SelectedItemProperty); } |
||||
|
set { this.SetValue(SelectedItemProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="StackPanel.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
public enum Orientation |
||||
|
{ |
||||
|
Vertical, |
||||
|
Horizontal, |
||||
|
} |
||||
|
|
||||
|
public class StackPanel : Panel |
||||
|
{ |
||||
|
public static readonly PerspexProperty<double> GapProperty = |
||||
|
PerspexProperty.Register<StackPanel, double>("Gap"); |
||||
|
|
||||
|
public static readonly PerspexProperty<Orientation> OrientationProperty = |
||||
|
PerspexProperty.Register<StackPanel, Orientation>("Orientation"); |
||||
|
|
||||
|
public double Gap |
||||
|
{ |
||||
|
get { return this.GetValue(GapProperty); } |
||||
|
set { this.SetValue(GapProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public Orientation Orientation |
||||
|
{ |
||||
|
get { return this.GetValue(OrientationProperty); } |
||||
|
set { this.SetValue(OrientationProperty, value); } |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
double childAvailableWidth = double.PositiveInfinity; |
||||
|
double childAvailableHeight = double.PositiveInfinity; |
||||
|
|
||||
|
if (this.Orientation == Orientation.Vertical) |
||||
|
{ |
||||
|
childAvailableWidth = availableSize.Width; |
||||
|
|
||||
|
if (!double.IsNaN(this.Width)) |
||||
|
{ |
||||
|
childAvailableWidth = this.Width; |
||||
|
} |
||||
|
|
||||
|
childAvailableWidth = Math.Min(childAvailableWidth, this.MaxWidth); |
||||
|
childAvailableWidth = Math.Max(childAvailableWidth, this.MinWidth); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
childAvailableHeight = availableSize.Height; |
||||
|
|
||||
|
if (!double.IsNaN(this.Height)) |
||||
|
{ |
||||
|
childAvailableHeight = this.Height; |
||||
|
} |
||||
|
|
||||
|
childAvailableHeight = Math.Min(childAvailableHeight, this.MaxHeight); |
||||
|
childAvailableHeight = Math.Max(childAvailableHeight, this.MinHeight); |
||||
|
} |
||||
|
|
||||
|
double measuredWidth = 0; |
||||
|
double measuredHeight = 0; |
||||
|
double gap = this.Gap; |
||||
|
|
||||
|
foreach (Control child in this.Children) |
||||
|
{ |
||||
|
child.Measure(new Size(childAvailableWidth, childAvailableHeight)); |
||||
|
Size size = child.DesiredSize.Value; |
||||
|
|
||||
|
if (Orientation == Orientation.Vertical) |
||||
|
{ |
||||
|
measuredHeight += size.Height + gap; |
||||
|
measuredWidth = Math.Max(measuredWidth, size.Width); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
measuredWidth += size.Width + gap; |
||||
|
measuredHeight = Math.Max(measuredHeight, size.Height); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return new Size(measuredWidth, measuredHeight); |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
double arrangedWidth = finalSize.Width; |
||||
|
double arrangedHeight = finalSize.Height; |
||||
|
double gap = this.Gap; |
||||
|
|
||||
|
if (Orientation == Orientation.Vertical) |
||||
|
{ |
||||
|
arrangedHeight = 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
arrangedWidth = 0; |
||||
|
} |
||||
|
|
||||
|
foreach (Control child in this.Children.Where(x => x.DesiredSize.HasValue)) |
||||
|
{ |
||||
|
double childWidth = child.DesiredSize.Value.Width; |
||||
|
double childHeight = child.DesiredSize.Value.Height; |
||||
|
|
||||
|
if (Orientation == Orientation.Vertical) |
||||
|
{ |
||||
|
childWidth = finalSize.Width; |
||||
|
|
||||
|
Rect childFinal = new Rect(0, arrangedHeight, childWidth, childHeight); |
||||
|
|
||||
|
if (childFinal.IsEmpty) |
||||
|
{ |
||||
|
child.Arrange(new Rect()); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
child.Arrange(childFinal); |
||||
|
} |
||||
|
|
||||
|
arrangedWidth = Math.Max(arrangedWidth, childWidth); |
||||
|
arrangedHeight += childHeight + gap; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
childHeight = finalSize.Height; |
||||
|
Rect childFinal = new Rect(arrangedWidth, 0, childWidth, childHeight); |
||||
|
child.Arrange(childFinal); |
||||
|
arrangedWidth += childWidth + gap; |
||||
|
arrangedHeight = Math.Max(arrangedHeight, childHeight); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Orientation == Orientation.Vertical) |
||||
|
{ |
||||
|
arrangedHeight = Math.Max(arrangedHeight - gap, finalSize.Height); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
arrangedWidth = Math.Max(arrangedWidth - gap, finalSize.Width); |
||||
|
} |
||||
|
|
||||
|
return new Size(arrangedWidth, arrangedHeight); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TabControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
public class TabControl : SelectingItemsControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<object> SelectedContentProperty = |
||||
|
PerspexProperty.Register<TabControl, object>("SelectedContent"); |
||||
|
|
||||
|
private TabStrip tabStrip; |
||||
|
|
||||
|
public TabControl() |
||||
|
{ |
||||
|
this.GetObservable(SelectedItemProperty).Skip(1).Subscribe(this.SelectedItemChanged); |
||||
|
} |
||||
|
|
||||
|
protected override void OnTemplateApplied() |
||||
|
{ |
||||
|
this.tabStrip = this.GetTemplateControls() |
||||
|
.OfType<TabStrip>() |
||||
|
.FirstOrDefault(); |
||||
|
|
||||
|
if (this.tabStrip != null) |
||||
|
{ |
||||
|
if (this.IsSet(SelectedItemProperty)) |
||||
|
{ |
||||
|
this.SelectedItem = SelectedItem; |
||||
|
} |
||||
|
|
||||
|
this.tabStrip.GetObservable(TabStrip.SelectedItemProperty).Subscribe(x => |
||||
|
{ |
||||
|
this.SelectedItem = x; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void SelectedItemChanged(object item) |
||||
|
{ |
||||
|
this.SelectedItem = item; |
||||
|
|
||||
|
ContentControl content = item as ContentControl; |
||||
|
|
||||
|
if (content != null) |
||||
|
{ |
||||
|
this.SetValue(SelectedContentProperty, content.Content); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.SetValue(SelectedContentProperty, item); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TabItem.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
public class TabItem : HeaderedContentControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<bool> IsSelectedProperty = |
||||
|
PerspexProperty.Register<TabItem, bool>("IsSelected"); |
||||
|
|
||||
|
public TabItem() |
||||
|
{ |
||||
|
this.AddPseudoClass(IsSelectedProperty, ":selected"); |
||||
|
AffectsRender(IsSelectedProperty); |
||||
|
} |
||||
|
|
||||
|
public bool IsSelected |
||||
|
{ |
||||
|
get { return this.GetValue(IsSelectedProperty); } |
||||
|
set { this.SetValue(IsSelectedProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,88 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TabStrip.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Linq; |
||||
|
using Perspex.Input; |
||||
|
|
||||
|
public class TabStrip : SelectingItemsControl |
||||
|
{ |
||||
|
private static readonly ItemsPanelTemplate PanelTemplate = new ItemsPanelTemplate( |
||||
|
() => new StackPanel()); |
||||
|
|
||||
|
static TabStrip() |
||||
|
{ |
||||
|
ItemsPanelProperty.OverrideDefaultValue(typeof(TabStrip), PanelTemplate); |
||||
|
} |
||||
|
|
||||
|
public TabStrip() |
||||
|
{ |
||||
|
this.PointerPressed += this.OnPointerPressed; |
||||
|
this.GetObservable(ItemsProperty).Subscribe(this.ItemsChanged); |
||||
|
this.GetObservable(SelectedItemProperty).Subscribe(this.SelectedItemChanged); |
||||
|
} |
||||
|
|
||||
|
protected override Control CreateItemControlOverride(object item) |
||||
|
{ |
||||
|
TabItem result = item as TabItem; |
||||
|
|
||||
|
if (result == null) |
||||
|
{ |
||||
|
result = new TabItem |
||||
|
{ |
||||
|
Content = this.GetDataTemplate(item).Build(item), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
result.IsSelected = this.SelectedItem == item; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private void ItemsChanged(IEnumerable items) |
||||
|
{ |
||||
|
if (items != null) |
||||
|
{ |
||||
|
this.SelectedItem = |
||||
|
items.OfType<TabItem>().FirstOrDefault(x => x.IsSelected) ?? |
||||
|
items.OfType<object>().FirstOrDefault(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.SelectedItem = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void OnPointerPressed(object sender, PointerEventArgs e) |
||||
|
{ |
||||
|
IVisual source = (IVisual)e.Source; |
||||
|
ContentPresenter presenter = source.GetVisualAncestors() |
||||
|
.OfType<ContentPresenter>() |
||||
|
.FirstOrDefault(); |
||||
|
|
||||
|
if (presenter != null) |
||||
|
{ |
||||
|
TabItem item = presenter.TemplatedParent as TabItem; |
||||
|
|
||||
|
if (item != null) |
||||
|
{ |
||||
|
this.SelectedItem = item; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void SelectedItemChanged(object selectedItem) |
||||
|
{ |
||||
|
foreach (TabItem item in this.GetAllItemControls()) |
||||
|
{ |
||||
|
item.IsSelected = item == selectedItem; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TemplatedControl.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Styling; |
||||
|
using Splat; |
||||
|
|
||||
|
public class TemplatedControl : Control, IVisual, ITemplatedControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<ControlTemplate> TemplateProperty = |
||||
|
PerspexProperty.Register<TemplatedControl, ControlTemplate>("Template"); |
||||
|
|
||||
|
public ControlTemplate Template |
||||
|
{ |
||||
|
get { return this.GetValue(TemplateProperty); } |
||||
|
set { this.SetValue(TemplateProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public sealed override void Render(IDrawingContext context) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override Size ArrangeOverride(Size finalSize) |
||||
|
{ |
||||
|
Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; |
||||
|
|
||||
|
if (child != null) |
||||
|
{ |
||||
|
child.Arrange(new Rect(finalSize)); |
||||
|
return child.ActualSize; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return new Size(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
Control child = ((IVisual)this).VisualChildren.SingleOrDefault() as Control; |
||||
|
|
||||
|
if (child != null) |
||||
|
{ |
||||
|
child.Measure(availableSize); |
||||
|
return child.DesiredSize.Value; |
||||
|
} |
||||
|
|
||||
|
return new Size(); |
||||
|
} |
||||
|
|
||||
|
protected override IEnumerable<Visual> CreateVisualChildren() |
||||
|
{ |
||||
|
this.Log().Debug(string.Format( |
||||
|
"Creating template for {0} (#{1:x8})", |
||||
|
this.GetType().Name, |
||||
|
this.GetHashCode())); |
||||
|
|
||||
|
var child = this.Template.Build(this); |
||||
|
return Enumerable.Repeat(child, 1); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnTemplateApplied() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TextBlock.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Platform; |
||||
|
using Splat; |
||||
|
|
||||
|
public class TextBlock : Control |
||||
|
{ |
||||
|
public static readonly PerspexProperty<string> TextProperty = |
||||
|
PerspexProperty.Register<TextBlock, string>("Text"); |
||||
|
|
||||
|
static TextBlock() |
||||
|
{ |
||||
|
AffectsMeasure(TextProperty); |
||||
|
} |
||||
|
|
||||
|
public string Text |
||||
|
{ |
||||
|
get { return this.GetValue(TextProperty); } |
||||
|
set { this.SetValue(TextProperty, value); } |
||||
|
} |
||||
|
|
||||
|
private FormattedText FormattedText |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return new FormattedText |
||||
|
{ |
||||
|
FontFamilyName = "Segoe UI", |
||||
|
FontSize = this.FontSize, |
||||
|
Text = this.Text, |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void Render(IDrawingContext context) |
||||
|
{ |
||||
|
Brush background = this.Background; |
||||
|
|
||||
|
if (background != null) |
||||
|
{ |
||||
|
context.FillRectange(background, new Rect(this.ActualSize)); |
||||
|
} |
||||
|
|
||||
|
context.DrawText(this.Foreground, new Rect(this.ActualSize), this.FormattedText); |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size availableSize) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(this.Text)) |
||||
|
{ |
||||
|
ITextService textService = Locator.Current.GetService<ITextService>(); |
||||
|
return textService.Measure(this.FormattedText, availableSize); |
||||
|
} |
||||
|
|
||||
|
return new Size(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,133 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TextBox.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Styling; |
||||
|
using Splat; |
||||
|
|
||||
|
public class TextBox : TemplatedControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<string> TextProperty = |
||||
|
TextBlock.TextProperty.AddOwner<TextBox>(); |
||||
|
|
||||
|
private int caretIndex; |
||||
|
|
||||
|
private TextBoxView textBoxView; |
||||
|
|
||||
|
static TextBox() |
||||
|
{ |
||||
|
FocusableProperty.OverrideDefaultValue(typeof(TextBox), true); |
||||
|
} |
||||
|
|
||||
|
public TextBox() |
||||
|
{ |
||||
|
this.GotFocus += (s, e) => this.textBoxView.GotFocus(); |
||||
|
this.LostFocus += (s, e) => this.textBoxView.LostFocus(); |
||||
|
this.KeyDown += this.OnKeyDown; |
||||
|
this.PointerPressed += this.OnPointerPressed; |
||||
|
} |
||||
|
|
||||
|
public int CaretIndex |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.caretIndex; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
value = Math.Min(Math.Max(value, 0), this.Text.Length); |
||||
|
|
||||
|
if (this.caretIndex != value) |
||||
|
{ |
||||
|
this.caretIndex = value; |
||||
|
this.textBoxView.CaretMoved(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string Text |
||||
|
{ |
||||
|
get { return this.GetValue(TextProperty); } |
||||
|
set { this.SetValue(TextProperty, value); } |
||||
|
} |
||||
|
|
||||
|
protected override void OnTemplateApplied() |
||||
|
{ |
||||
|
Decorator textContainer = this.GetVisualDescendents() |
||||
|
.OfType<Decorator>() |
||||
|
.FirstOrDefault(x => x.Id == "textContainer"); |
||||
|
|
||||
|
if (textContainer == null) |
||||
|
{ |
||||
|
throw new Exception( |
||||
|
"TextBox template doesn't contain a textContainer " + |
||||
|
"or textContainer is not a Decorator."); |
||||
|
} |
||||
|
|
||||
|
textContainer.Content = this.textBoxView = new TextBoxView(this); |
||||
|
this.GetObservable(TextProperty).Subscribe(_ => this.textBoxView.InvalidateText()); |
||||
|
} |
||||
|
|
||||
|
private void OnKeyDown(object sender, KeyEventArgs e) |
||||
|
{ |
||||
|
string text = this.Text; |
||||
|
|
||||
|
switch (e.Key) |
||||
|
{ |
||||
|
case Key.Left: |
||||
|
--this.CaretIndex; |
||||
|
break; |
||||
|
|
||||
|
case Key.Right: |
||||
|
++this.CaretIndex; |
||||
|
break; |
||||
|
|
||||
|
case Key.Back: |
||||
|
if (this.caretIndex > 0) |
||||
|
{ |
||||
|
this.Text = text.Substring(0, this.caretIndex - 1) + text.Substring(this.caretIndex); |
||||
|
--this.CaretIndex; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
|
||||
|
case Key.Delete: |
||||
|
if (this.caretIndex < text.Length) |
||||
|
{ |
||||
|
this.Text = text.Substring(0, this.caretIndex) + text.Substring(this.caretIndex + 1); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
if (!string.IsNullOrEmpty(e.Text)) |
||||
|
{ |
||||
|
this.Text = text.Substring(0, this.caretIndex) + e.Text + text.Substring(this.caretIndex); |
||||
|
++this.CaretIndex; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
e.Handled = true; |
||||
|
} |
||||
|
|
||||
|
private void OnPointerPressed(object sender, PointerEventArgs e) |
||||
|
{ |
||||
|
IPlatformRenderInterface platform = Locator.Current.GetService<IPlatformRenderInterface>(); |
||||
|
this.CaretIndex = platform.TextService.GetCaretIndex( |
||||
|
this.textBoxView.FormattedText, |
||||
|
e.GetPosition(this.textBoxView), |
||||
|
this.ActualSize); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,125 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TextBoxView.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Threading; |
||||
|
using Splat; |
||||
|
|
||||
|
internal class TextBoxView : Control |
||||
|
{ |
||||
|
private TextBox parent; |
||||
|
|
||||
|
private FormattedText formattedText; |
||||
|
|
||||
|
private DispatcherTimer caretTimer; |
||||
|
|
||||
|
private bool caretBlink; |
||||
|
|
||||
|
public TextBoxView(TextBox parent) |
||||
|
{ |
||||
|
this.caretTimer = new DispatcherTimer(); |
||||
|
this.caretTimer.Interval = TimeSpan.FromMilliseconds(500); |
||||
|
this.caretTimer.Tick += this.CaretTimerTick; |
||||
|
this.parent = parent; |
||||
|
} |
||||
|
|
||||
|
public FormattedText FormattedText |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (this.formattedText == null) |
||||
|
{ |
||||
|
this.formattedText = this.CreateFormattedText(); |
||||
|
} |
||||
|
|
||||
|
return this.formattedText; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public new void GotFocus() |
||||
|
{ |
||||
|
this.caretBlink = true; |
||||
|
this.caretTimer.Start(); |
||||
|
} |
||||
|
|
||||
|
public new void LostFocus() |
||||
|
{ |
||||
|
this.caretTimer.Stop(); |
||||
|
this.InvalidateVisual(); |
||||
|
} |
||||
|
|
||||
|
public void InvalidateText() |
||||
|
{ |
||||
|
this.formattedText = null; |
||||
|
this.InvalidateMeasure(); |
||||
|
} |
||||
|
|
||||
|
internal void CaretMoved() |
||||
|
{ |
||||
|
this.caretBlink = true; |
||||
|
this.caretTimer.Stop(); |
||||
|
this.caretTimer.Start(); |
||||
|
this.InvalidateVisual(); |
||||
|
} |
||||
|
|
||||
|
public override void Render(IDrawingContext context) |
||||
|
{ |
||||
|
Rect rect = new Rect(this.ActualSize); |
||||
|
|
||||
|
context.DrawText(Brushes.Black, rect, this.FormattedText); |
||||
|
|
||||
|
if (this.parent.IsFocused) |
||||
|
{ |
||||
|
ITextService textService = Locator.Current.GetService<ITextService>(); |
||||
|
Point caretPos = textService.GetCaretPosition( |
||||
|
this.formattedText, |
||||
|
this.parent.CaretIndex, |
||||
|
this.ActualSize); |
||||
|
double[] lineHeights = textService.GetLineHeights(this.formattedText, this.ActualSize); |
||||
|
Brush caretBrush = Brushes.Black; |
||||
|
|
||||
|
if (this.caretBlink) |
||||
|
{ |
||||
|
context.DrawLine( |
||||
|
new Pen(caretBrush, 1), |
||||
|
caretPos, |
||||
|
new Point(caretPos.X, caretPos.Y + lineHeights[0])); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override Size MeasureOverride(Size constraint) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(this.parent.Text)) |
||||
|
{ |
||||
|
ITextService textService = Locator.Current.GetService<ITextService>(); |
||||
|
return textService.Measure(this.FormattedText, constraint); |
||||
|
} |
||||
|
|
||||
|
return new Size(); |
||||
|
} |
||||
|
|
||||
|
private FormattedText CreateFormattedText() |
||||
|
{ |
||||
|
return new FormattedText |
||||
|
{ |
||||
|
FontFamilyName = "Segoe UI", |
||||
|
FontSize = this.FontSize, |
||||
|
Text = this.parent.Text, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private void CaretTimerTick(object sender, EventArgs e) |
||||
|
{ |
||||
|
this.caretBlink = !this.caretBlink; |
||||
|
this.InvalidateVisual(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="ToggleButton.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
public class ToggleButton : Button |
||||
|
{ |
||||
|
public static readonly PerspexProperty<bool> IsCheckedProperty = |
||||
|
PerspexProperty.Register<ToggleButton, bool>("IsChecked"); |
||||
|
|
||||
|
public ToggleButton() |
||||
|
{ |
||||
|
this.Click += (s, e) => this.IsChecked = !this.IsChecked; |
||||
|
this.AddPseudoClass(IsCheckedProperty, ":checked"); |
||||
|
} |
||||
|
|
||||
|
public bool IsChecked |
||||
|
{ |
||||
|
get { return this.GetValue(IsCheckedProperty); } |
||||
|
set { this.SetValue(IsCheckedProperty, value); } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TreeDataTemplate.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
|
||||
|
public class TreeDataTemplate : DataTemplate |
||||
|
{ |
||||
|
public TreeDataTemplate( |
||||
|
Func<object, Control> build, |
||||
|
Func<object, IEnumerable> itemsSelector) |
||||
|
: this(o => true, build, itemsSelector) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Type type, |
||||
|
Func<object, Control> build, |
||||
|
Func<object, IEnumerable> itemsSelector) |
||||
|
: this(o => IsInstance(o, type), build, itemsSelector) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Type type, |
||||
|
Func<object, Control> build, |
||||
|
Func<object, IEnumerable> itemsSelector, |
||||
|
Func<object, bool> isExpanded) |
||||
|
: this(o => IsInstance(o, type), build, itemsSelector, isExpanded) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Func<object, bool> match, |
||||
|
Func<object, Control> build, |
||||
|
Func<object, IEnumerable> itemsSelector) |
||||
|
: this(match, build, itemsSelector, _ => false) |
||||
|
{ |
||||
|
this.ItemsSelector = itemsSelector; |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Func<object, bool> match, |
||||
|
Func<object, Control> build, |
||||
|
Func<object, IEnumerable> itemsSelector, |
||||
|
Func<object, bool> isExpanded) |
||||
|
: base(match, build) |
||||
|
{ |
||||
|
this.ItemsSelector = itemsSelector; |
||||
|
this.IsExpanded = isExpanded; |
||||
|
} |
||||
|
|
||||
|
public Func<object, IEnumerable> ItemsSelector { get; private set; } |
||||
|
|
||||
|
public Func<object, bool> IsExpanded { get; private set; } |
||||
|
} |
||||
|
|
||||
|
public class TreeDataTemplate<T> : TreeDataTemplate |
||||
|
{ |
||||
|
public TreeDataTemplate( |
||||
|
Func<T, Control> build, |
||||
|
Func<T, IEnumerable> itemsSelector) |
||||
|
: base(typeof(T), Cast(build), Cast(itemsSelector)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Func<T, Control> build, |
||||
|
Func<T, IEnumerable> itemsSelector, |
||||
|
Func<T, bool> isExpanded) |
||||
|
: base(typeof(T), Cast(build), Cast(itemsSelector), Cast(isExpanded)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Func<T, bool> match, |
||||
|
Func<T, Control> build, |
||||
|
Func<T, IEnumerable> itemsSelector) |
||||
|
: base(CastMatch(match), Cast(build), Cast(itemsSelector)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public TreeDataTemplate( |
||||
|
Func<T, bool> match, |
||||
|
Func<T, Control> build, |
||||
|
Func<T, IEnumerable> itemsSelector, |
||||
|
Func<T, bool> isExpanded) |
||||
|
: base(CastMatch(match), Cast(build), Cast(itemsSelector), Cast(isExpanded)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private static Func<object, bool> CastMatch(Func<T, bool> f) |
||||
|
{ |
||||
|
return o => (o is T) ? f((T)o) : false; |
||||
|
} |
||||
|
|
||||
|
private static Func<object, TResult> Cast<TResult>(Func<T, TResult> f) |
||||
|
{ |
||||
|
return o => f((T)o); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TreeView.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Input; |
||||
|
|
||||
|
public class TreeView : SelectingItemsControl |
||||
|
{ |
||||
|
public TreeView() |
||||
|
{ |
||||
|
this.PointerPressed += this.OnPointerPressed; |
||||
|
} |
||||
|
|
||||
|
protected override Control CreateItemControlOverride(object item) |
||||
|
{ |
||||
|
TreeViewItem result = item as TreeViewItem; |
||||
|
|
||||
|
if (result == null) |
||||
|
{ |
||||
|
TreeDataTemplate template = this.GetTreeDataTemplate(item); |
||||
|
|
||||
|
result = new TreeViewItem |
||||
|
{ |
||||
|
Header = template.Build(item), |
||||
|
Items = template.ItemsSelector(item), |
||||
|
IsExpanded = template.IsExpanded(item), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private TreeDataTemplate GetTreeDataTemplate(object item) |
||||
|
{ |
||||
|
DataTemplate template = this.GetDataTemplate(item); |
||||
|
TreeDataTemplate treeTemplate = template as TreeDataTemplate; |
||||
|
|
||||
|
if (treeTemplate == null) |
||||
|
{ |
||||
|
treeTemplate = new TreeDataTemplate(template.Build, x => null); |
||||
|
} |
||||
|
|
||||
|
return treeTemplate; |
||||
|
} |
||||
|
|
||||
|
private void OnPointerPressed(object sender, PointerEventArgs e) |
||||
|
{ |
||||
|
IVisual source = (IVisual)e.Source; |
||||
|
ContentPresenter contentPresenter = source.GetVisualAncestors() |
||||
|
.OfType<ContentPresenter>() |
||||
|
.FirstOrDefault(); |
||||
|
|
||||
|
if (contentPresenter != null) |
||||
|
{ |
||||
|
TreeViewItem item = contentPresenter.TemplatedParent as TreeViewItem; |
||||
|
|
||||
|
if (item != null) |
||||
|
{ |
||||
|
foreach (var i in this.GetVisualDescendents().OfType<TreeViewItem>()) |
||||
|
{ |
||||
|
i.IsSelected = i == item; |
||||
|
} |
||||
|
|
||||
|
this.SelectedItem = this.GetItemForControl(item); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="TreeViewItem.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
using System.Linq; |
||||
|
|
||||
|
public class TreeViewItem : HeaderedItemsControl |
||||
|
{ |
||||
|
public static readonly PerspexProperty<bool> IsExpandedProperty = |
||||
|
PerspexProperty.Register<TreeViewItem, bool>("IsExpanded"); |
||||
|
|
||||
|
public static readonly PerspexProperty<bool> IsSelectedProperty = |
||||
|
PerspexProperty.Register<TreeViewItem, bool>("IsSelected"); |
||||
|
|
||||
|
TreeView parent; |
||||
|
|
||||
|
public TreeViewItem() |
||||
|
{ |
||||
|
this.AddPseudoClass(IsSelectedProperty, ":selected"); |
||||
|
AffectsRender(IsSelectedProperty); |
||||
|
} |
||||
|
|
||||
|
public bool IsExpanded |
||||
|
{ |
||||
|
get { return this.GetValue(IsExpandedProperty); } |
||||
|
set { this.SetValue(IsExpandedProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public bool IsSelected |
||||
|
{ |
||||
|
get { return this.GetValue(IsSelectedProperty); } |
||||
|
set { this.SetValue(IsSelectedProperty, value); } |
||||
|
} |
||||
|
|
||||
|
protected override Control CreateItemControlOverride(object item) |
||||
|
{ |
||||
|
if (this.parent != null) |
||||
|
{ |
||||
|
return this.parent.CreateItemControl(item); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void OnVisualParentChanged(IVisual oldParent) |
||||
|
{ |
||||
|
this.parent = this.GetVisualAncestors().OfType<TreeView>().FirstOrDefault(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="Rx-Core" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Interfaces" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Linq" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Main" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-PlatformServices" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Splat" version="1.3.3" targetFramework="portable-net45+win" /> |
||||
|
<package id="StyleCop.MSBuild" version="4.7.49.0" targetFramework="portable-net45+win" developmentDependency="true" /> |
||||
|
</packages> |
||||
@ -1,5 +1,5 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<packages> |
<packages> |
||||
<package id="Magick.NET-Q16-x86" version="6.8.9.101" targetFramework="net45" /> |
<package id="Magick.NET-Q16-x86" version="6.8.9.101" targetFramework="net45" /> |
||||
<package id="Splat" version="1.1.1" targetFramework="net45" /> |
<package id="Splat" version="1.3.3" targetFramework="net45" /> |
||||
</packages> |
</packages> |
||||
@ -0,0 +1,47 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IFocusManager.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using Perspex.Interactivity; |
||||
|
|
||||
|
public class FocusManager : IFocusManager |
||||
|
{ |
||||
|
public IInputElement Current |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public void Focus(IInputElement control) |
||||
|
{ |
||||
|
IInteractive current = this.Current as IInteractive; |
||||
|
IInteractive next = control as IInteractive; |
||||
|
|
||||
|
if (current != null) |
||||
|
{ |
||||
|
current.RaiseEvent(new RoutedEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.LostFocusEvent, |
||||
|
Source = current, |
||||
|
OriginalSource = current, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
this.Current = control; |
||||
|
|
||||
|
if (next != null) |
||||
|
{ |
||||
|
next.RaiseEvent(new RoutedEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.GotFocusEvent, |
||||
|
Source = next, |
||||
|
OriginalSource = next, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IFocusManager.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
public interface IFocusManager |
||||
|
{ |
||||
|
IInputElement Current { get; } |
||||
|
|
||||
|
void Focus(IInputElement focusable); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IInputDevice.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
public interface IInputDevice |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IInputElement.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Interactivity; |
||||
|
|
||||
|
public interface IInputElement : IInteractive |
||||
|
{ |
||||
|
event EventHandler<RoutedEventArgs> GotFocus; |
||||
|
|
||||
|
event EventHandler<RoutedEventArgs> LostFocus; |
||||
|
|
||||
|
event EventHandler<KeyEventArgs> KeyDown; |
||||
|
|
||||
|
event EventHandler<PointerEventArgs> PointerEnter; |
||||
|
|
||||
|
event EventHandler<PointerEventArgs> PointerLeave; |
||||
|
|
||||
|
event EventHandler<PointerEventArgs> PointerPressed; |
||||
|
|
||||
|
event EventHandler<PointerEventArgs> PointerReleased; |
||||
|
|
||||
|
bool Focusable { get; } |
||||
|
|
||||
|
bool IsFocused { get; } |
||||
|
|
||||
|
bool IsPointerOver { get; } |
||||
|
|
||||
|
void Focus(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IInputManager.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Layout; |
||||
|
|
||||
|
public interface IInputManager |
||||
|
{ |
||||
|
IObservable<RawInputEventArgs> RawEventReceived { get; } |
||||
|
|
||||
|
void Process(RawInputEventArgs e); |
||||
|
|
||||
|
void SetPointerOver(IPointerDevice device, IVisual visual, Point p); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IKeyboardDevice.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
[Flags] |
||||
|
public enum ModifierKeys |
||||
|
{ |
||||
|
None = 0, |
||||
|
Alt = 1, |
||||
|
Control = 2, |
||||
|
Shift = 4, |
||||
|
Windows = 8, |
||||
|
} |
||||
|
|
||||
|
[Flags] |
||||
|
public enum KeyStates |
||||
|
{ |
||||
|
None = 0, |
||||
|
Down = 1, |
||||
|
Toggled = 2, |
||||
|
} |
||||
|
|
||||
|
public interface IKeyboardDevice : IInputDevice |
||||
|
{ |
||||
|
ModifierKeys Modifiers { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IMouseDevice.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
public interface IMouseDevice : IPointerDevice |
||||
|
{ |
||||
|
Point Position { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IPointerDevice.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Interactivity; |
||||
|
|
||||
|
public interface IPointerDevice : IInputDevice |
||||
|
{ |
||||
|
IInteractive Captured { get; } |
||||
|
|
||||
|
void Capture(IInteractive control); |
||||
|
|
||||
|
Point GetPosition(IVisual relativeTo); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Control.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Interactivity; |
||||
|
using Splat; |
||||
|
|
||||
|
public class InputElement : Interactive, IInputElement |
||||
|
{ |
||||
|
public static readonly PerspexProperty<bool> FocusableProperty = |
||||
|
PerspexProperty.Register<InputElement, bool>("Focusable"); |
||||
|
|
||||
|
public static readonly PerspexProperty<bool> IsFocusedProperty = |
||||
|
PerspexProperty.Register<InputElement, bool>("IsFocused", false); |
||||
|
|
||||
|
public static readonly PerspexProperty<bool> IsPointerOverProperty = |
||||
|
PerspexProperty.Register<InputElement, bool>("IsPointerOver"); |
||||
|
|
||||
|
public static readonly RoutedEvent<RoutedEventArgs> GotFocusEvent = |
||||
|
RoutedEvent.Register<InputElement, RoutedEventArgs>("GotFocus", RoutingStrategy.Bubble); |
||||
|
|
||||
|
public static readonly RoutedEvent<RoutedEventArgs> LostFocusEvent = |
||||
|
RoutedEvent.Register<InputElement, RoutedEventArgs>("LostFocus", RoutingStrategy.Bubble); |
||||
|
|
||||
|
public static readonly RoutedEvent<KeyEventArgs> KeyDownEvent = |
||||
|
RoutedEvent.Register<InputElement, KeyEventArgs>("KeyDown", RoutingStrategy.Bubble); |
||||
|
|
||||
|
public static readonly RoutedEvent<KeyEventArgs> PreviewKeyDownEvent = |
||||
|
RoutedEvent.Register<InputElement, KeyEventArgs>("PreviewKeyDown", RoutingStrategy.Tunnel); |
||||
|
|
||||
|
public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent = |
||||
|
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerEnter", RoutingStrategy.Direct); |
||||
|
|
||||
|
public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent = |
||||
|
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerLeave", RoutingStrategy.Direct); |
||||
|
|
||||
|
public static readonly RoutedEvent<PointerEventArgs> PointerPressedEvent = |
||||
|
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerPressed", RoutingStrategy.Bubble); |
||||
|
|
||||
|
public static readonly RoutedEvent<PointerEventArgs> PointerReleasedEvent = |
||||
|
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerReleased", RoutingStrategy.Bubble); |
||||
|
|
||||
|
public InputElement() |
||||
|
{ |
||||
|
this.GotFocus += (s, e) => this.IsFocused = true; |
||||
|
this.LostFocus += (s, e) => this.IsFocused = false; |
||||
|
this.PointerEnter += (s, e) => this.IsPointerOver = true; |
||||
|
this.PointerLeave += (s, e) => this.IsPointerOver = false; |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<RoutedEventArgs> GotFocus |
||||
|
{ |
||||
|
add { this.AddHandler(GotFocusEvent, value); } |
||||
|
remove { this.RemoveHandler(GotFocusEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<RoutedEventArgs> LostFocus |
||||
|
{ |
||||
|
add { this.AddHandler(LostFocusEvent, value); } |
||||
|
remove { this.RemoveHandler(LostFocusEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<KeyEventArgs> KeyDown |
||||
|
{ |
||||
|
add { this.AddHandler(KeyDownEvent, value); } |
||||
|
remove { this.RemoveHandler(KeyDownEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<PointerEventArgs> PointerEnter |
||||
|
{ |
||||
|
add { this.AddHandler(PointerEnterEvent, value); } |
||||
|
remove { this.RemoveHandler(PointerEnterEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<PointerEventArgs> PointerLeave |
||||
|
{ |
||||
|
add { this.AddHandler(PointerLeaveEvent, value); } |
||||
|
remove { this.RemoveHandler(PointerLeaveEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<PointerEventArgs> PointerPressed |
||||
|
{ |
||||
|
add { this.AddHandler(PointerPressedEvent, value); } |
||||
|
remove { this.RemoveHandler(PointerPressedEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler<PointerEventArgs> PointerReleased |
||||
|
{ |
||||
|
add { this.AddHandler(PointerReleasedEvent, value); } |
||||
|
remove { this.RemoveHandler(PointerReleasedEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public bool Focusable |
||||
|
{ |
||||
|
get { return this.GetValue(FocusableProperty); } |
||||
|
set { this.SetValue(FocusableProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public bool IsFocused |
||||
|
{ |
||||
|
get { return this.GetValue(IsFocusedProperty); } |
||||
|
private set { this.SetValue(IsFocusedProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public bool IsPointerOver |
||||
|
{ |
||||
|
get { return this.GetValue(IsPointerOverProperty); } |
||||
|
internal set { this.SetValue(IsPointerOverProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public void Focus() |
||||
|
{ |
||||
|
Locator.Current.GetService<IFocusManager>().Focus(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="InputManager.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Subjects; |
||||
|
using Perspex.Input.Raw; |
||||
|
|
||||
|
public class InputManager : IInputManager |
||||
|
{ |
||||
|
private List<IInputElement> pointerOvers = new List<IInputElement>(); |
||||
|
|
||||
|
private Subject<RawInputEventArgs> rawEventReceived = new Subject<RawInputEventArgs>(); |
||||
|
|
||||
|
public IObservable<RawInputEventArgs> RawEventReceived |
||||
|
{ |
||||
|
get { return this.rawEventReceived; } |
||||
|
} |
||||
|
|
||||
|
public void Process(RawInputEventArgs e) |
||||
|
{ |
||||
|
this.rawEventReceived.OnNext(e); |
||||
|
} |
||||
|
|
||||
|
public void SetPointerOver(IPointerDevice device, IVisual visual, Point p) |
||||
|
{ |
||||
|
IEnumerable<IInputElement> hits = visual.GetVisualsAt(p).OfType<IInputElement>(); |
||||
|
|
||||
|
foreach (var control in this.pointerOvers.Except(hits).ToList()) |
||||
|
{ |
||||
|
PointerEventArgs e = new PointerEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerLeaveEvent, |
||||
|
Device = device, |
||||
|
OriginalSource = control, |
||||
|
Source = control, |
||||
|
}; |
||||
|
|
||||
|
this.pointerOvers.Remove(control); |
||||
|
control.RaiseEvent(e); |
||||
|
} |
||||
|
|
||||
|
foreach (var control in hits.Except(this.pointerOvers)) |
||||
|
{ |
||||
|
PointerEventArgs e = new PointerEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerEnterEvent, |
||||
|
Device = device, |
||||
|
OriginalSource = control, |
||||
|
Source = control, |
||||
|
}; |
||||
|
|
||||
|
this.pointerOvers.Add(control); |
||||
|
control.RaiseEvent(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,214 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Key.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
public enum Key |
||||
|
{ |
||||
|
None = 0, |
||||
|
Cancel = 1, |
||||
|
Back = 2, |
||||
|
Tab = 3, |
||||
|
LineFeed = 4, |
||||
|
Clear = 5, |
||||
|
Return = 6, |
||||
|
Enter = 6, |
||||
|
Pause = 7, |
||||
|
CapsLock = 8, |
||||
|
Capital = 8, |
||||
|
HangulMode = 9, |
||||
|
KanaMode = 9, |
||||
|
JunjaMode = 10, |
||||
|
FinalMode = 11, |
||||
|
KanjiMode = 12, |
||||
|
HanjaMode = 12, |
||||
|
Escape = 13, |
||||
|
ImeConvert = 14, |
||||
|
ImeNonConvert = 15, |
||||
|
ImeAccept = 16, |
||||
|
ImeModeChange = 17, |
||||
|
Space = 18, |
||||
|
PageUp = 19, |
||||
|
Prior = 19, |
||||
|
PageDown = 20, |
||||
|
Next = 20, |
||||
|
End = 21, |
||||
|
Home = 22, |
||||
|
Left = 23, |
||||
|
Up = 24, |
||||
|
Right = 25, |
||||
|
Down = 26, |
||||
|
Select = 27, |
||||
|
Print = 28, |
||||
|
Execute = 29, |
||||
|
Snapshot = 30, |
||||
|
PrintScreen = 30, |
||||
|
Insert = 31, |
||||
|
Delete = 32, |
||||
|
Help = 33, |
||||
|
D0 = 34, |
||||
|
D1 = 35, |
||||
|
D2 = 36, |
||||
|
D3 = 37, |
||||
|
D4 = 38, |
||||
|
D5 = 39, |
||||
|
D6 = 40, |
||||
|
D7 = 41, |
||||
|
D8 = 42, |
||||
|
D9 = 43, |
||||
|
A = 44, |
||||
|
B = 45, |
||||
|
C = 46, |
||||
|
D = 47, |
||||
|
E = 48, |
||||
|
F = 49, |
||||
|
G = 50, |
||||
|
H = 51, |
||||
|
I = 52, |
||||
|
J = 53, |
||||
|
K = 54, |
||||
|
L = 55, |
||||
|
M = 56, |
||||
|
N = 57, |
||||
|
O = 58, |
||||
|
P = 59, |
||||
|
Q = 60, |
||||
|
R = 61, |
||||
|
S = 62, |
||||
|
T = 63, |
||||
|
U = 64, |
||||
|
V = 65, |
||||
|
W = 66, |
||||
|
X = 67, |
||||
|
Y = 68, |
||||
|
Z = 69, |
||||
|
LWin = 70, |
||||
|
RWin = 71, |
||||
|
Apps = 72, |
||||
|
Sleep = 73, |
||||
|
NumPad0 = 74, |
||||
|
NumPad1 = 75, |
||||
|
NumPad2 = 76, |
||||
|
NumPad3 = 77, |
||||
|
NumPad4 = 78, |
||||
|
NumPad5 = 79, |
||||
|
NumPad6 = 80, |
||||
|
NumPad7 = 81, |
||||
|
NumPad8 = 82, |
||||
|
NumPad9 = 83, |
||||
|
Multiply = 84, |
||||
|
Add = 85, |
||||
|
Separator = 86, |
||||
|
Subtract = 87, |
||||
|
Decimal = 88, |
||||
|
Divide = 89, |
||||
|
F1 = 90, |
||||
|
F2 = 91, |
||||
|
F3 = 92, |
||||
|
F4 = 93, |
||||
|
F5 = 94, |
||||
|
F6 = 95, |
||||
|
F7 = 96, |
||||
|
F8 = 97, |
||||
|
F9 = 98, |
||||
|
F10 = 99, |
||||
|
F11 = 100, |
||||
|
F12 = 101, |
||||
|
F13 = 102, |
||||
|
F14 = 103, |
||||
|
F15 = 104, |
||||
|
F16 = 105, |
||||
|
F17 = 106, |
||||
|
F18 = 107, |
||||
|
F19 = 108, |
||||
|
F20 = 109, |
||||
|
F21 = 110, |
||||
|
F22 = 111, |
||||
|
F23 = 112, |
||||
|
F24 = 113, |
||||
|
NumLock = 114, |
||||
|
Scroll = 115, |
||||
|
LeftShift = 116, |
||||
|
RightShift = 117, |
||||
|
LeftCtrl = 118, |
||||
|
RightCtrl = 119, |
||||
|
LeftAlt = 120, |
||||
|
RightAlt = 121, |
||||
|
BrowserBack = 122, |
||||
|
BrowserForward = 123, |
||||
|
BrowserRefresh = 124, |
||||
|
BrowserStop = 125, |
||||
|
BrowserSearch = 126, |
||||
|
BrowserFavorites = 127, |
||||
|
BrowserHome = 128, |
||||
|
VolumeMute = 129, |
||||
|
VolumeDown = 130, |
||||
|
VolumeUp = 131, |
||||
|
MediaNextTrack = 132, |
||||
|
MediaPreviousTrack = 133, |
||||
|
MediaStop = 134, |
||||
|
MediaPlayPause = 135, |
||||
|
LaunchMail = 136, |
||||
|
SelectMedia = 137, |
||||
|
LaunchApplication1 = 138, |
||||
|
LaunchApplication2 = 139, |
||||
|
OemSemicolon = 140, |
||||
|
Oem1 = 140, |
||||
|
OemPlus = 141, |
||||
|
OemComma = 142, |
||||
|
OemMinus = 143, |
||||
|
OemPeriod = 144, |
||||
|
OemQuestion = 145, |
||||
|
Oem2 = 145, |
||||
|
OemTilde = 146, |
||||
|
Oem3 = 146, |
||||
|
AbntC1 = 147, |
||||
|
AbntC2 = 148, |
||||
|
OemOpenBrackets = 149, |
||||
|
Oem4 = 149, |
||||
|
OemPipe = 150, |
||||
|
Oem5 = 150, |
||||
|
OemCloseBrackets = 151, |
||||
|
Oem6 = 151, |
||||
|
OemQuotes = 152, |
||||
|
Oem7 = 152, |
||||
|
Oem8 = 153, |
||||
|
OemBackslash = 154, |
||||
|
Oem102 = 154, |
||||
|
ImeProcessed = 155, |
||||
|
System = 156, |
||||
|
OemAttn = 157, |
||||
|
DbeAlphanumeric = 157, |
||||
|
OemFinish = 158, |
||||
|
DbeKatakana = 158, |
||||
|
DbeHiragana = 159, |
||||
|
OemCopy = 159, |
||||
|
DbeSbcsChar = 160, |
||||
|
OemAuto = 160, |
||||
|
DbeDbcsChar = 161, |
||||
|
OemEnlw = 161, |
||||
|
OemBackTab = 162, |
||||
|
DbeRoman = 162, |
||||
|
DbeNoRoman = 163, |
||||
|
Attn = 163, |
||||
|
CrSel = 164, |
||||
|
DbeEnterWordRegisterMode = 164, |
||||
|
ExSel = 165, |
||||
|
DbeEnterImeConfigureMode = 165, |
||||
|
EraseEof = 166, |
||||
|
DbeFlushString = 166, |
||||
|
Play = 167, |
||||
|
DbeCodeInput = 167, |
||||
|
DbeNoCodeInput = 168, |
||||
|
Zoom = 168, |
||||
|
NoName = 169, |
||||
|
DbeDetermineString = 169, |
||||
|
DbeEnterDialogConversionMode = 170, |
||||
|
Pa1 = 170, |
||||
|
OemClear = 171, |
||||
|
DeadCharProcessed = 172, |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PointerEventArgs.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Interactivity; |
||||
|
|
||||
|
public class KeyEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
public IKeyboardDevice Device { get; set; } |
||||
|
|
||||
|
public Key Key { get; set; } |
||||
|
|
||||
|
public string Text { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="KeyboardDevice.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Splat; |
||||
|
|
||||
|
public abstract class KeyboardDevice : IKeyboardDevice |
||||
|
{ |
||||
|
public KeyboardDevice() |
||||
|
{ |
||||
|
this.InputManager.RawEventReceived |
||||
|
.OfType<RawKeyEventArgs>() |
||||
|
.Where(x => x.Device == this) |
||||
|
.Subscribe(this.ProcessRawEvent); |
||||
|
} |
||||
|
|
||||
|
public IInputManager InputManager |
||||
|
{ |
||||
|
get { return Locator.Current.GetService<IInputManager>(); } |
||||
|
} |
||||
|
|
||||
|
public IFocusManager FocusManager |
||||
|
{ |
||||
|
get { return Locator.Current.GetService<IFocusManager>(); } |
||||
|
} |
||||
|
|
||||
|
public IInputElement FocusedElement |
||||
|
{ |
||||
|
get; |
||||
|
protected set; |
||||
|
} |
||||
|
|
||||
|
public abstract ModifierKeys Modifiers { get; } |
||||
|
|
||||
|
private void ProcessRawEvent(RawKeyEventArgs e) |
||||
|
{ |
||||
|
IInputElement element = this.FocusedElement; |
||||
|
|
||||
|
if (element != null) |
||||
|
{ |
||||
|
switch (e.Type) |
||||
|
{ |
||||
|
case RawKeyEventType.KeyDown: |
||||
|
element.RaiseEvent(new KeyEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PreviewKeyDownEvent, |
||||
|
Device = this, |
||||
|
Key = e.Key, |
||||
|
Text = e.Text, |
||||
|
Source = element, |
||||
|
OriginalSource = element, |
||||
|
}); |
||||
|
element.RaiseEvent(new KeyEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.KeyDownEvent, |
||||
|
Device = this, |
||||
|
Key = e.Key, |
||||
|
Text = e.Text, |
||||
|
Source = element, |
||||
|
OriginalSource = element, |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,163 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="MouseDevice.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Interactivity; |
||||
|
using Splat; |
||||
|
|
||||
|
public abstract class MouseDevice : IMouseDevice |
||||
|
{ |
||||
|
public MouseDevice() |
||||
|
{ |
||||
|
this.InputManager.RawEventReceived |
||||
|
.OfType<RawMouseEventArgs>() |
||||
|
.Where(x => x.Device == this) |
||||
|
.Subscribe(this.ProcessRawEvent); |
||||
|
} |
||||
|
|
||||
|
public IInteractive Captured |
||||
|
{ |
||||
|
get; |
||||
|
protected set; |
||||
|
} |
||||
|
|
||||
|
public IInputManager InputManager |
||||
|
{ |
||||
|
get { return Locator.Current.GetService<IInputManager>(); } |
||||
|
} |
||||
|
|
||||
|
public Point Position |
||||
|
{ |
||||
|
get; |
||||
|
protected set; |
||||
|
} |
||||
|
|
||||
|
public virtual void Capture(IInteractive control) |
||||
|
{ |
||||
|
this.Captured = control; |
||||
|
} |
||||
|
|
||||
|
public Point GetPosition(IVisual relativeTo) |
||||
|
{ |
||||
|
Point p = this.GetClientPosition(); |
||||
|
IVisual v = relativeTo; |
||||
|
|
||||
|
while (v != null) |
||||
|
{ |
||||
|
p -= v.Bounds.Position; |
||||
|
v = v.VisualParent; |
||||
|
} |
||||
|
|
||||
|
return p; |
||||
|
} |
||||
|
|
||||
|
protected abstract Point GetClientPosition(); |
||||
|
|
||||
|
private void ProcessRawEvent(RawMouseEventArgs e) |
||||
|
{ |
||||
|
this.Position = e.Position; |
||||
|
|
||||
|
switch (e.Type) |
||||
|
{ |
||||
|
case RawMouseEventType.Move: |
||||
|
this.MouseMove((IMouseDevice)e.Device, (IVisual)e.Root, e.Position); |
||||
|
break; |
||||
|
case RawMouseEventType.LeftButtonDown: |
||||
|
this.MouseDown((IMouseDevice)e.Device, (IVisual)e.Root, e.Position); |
||||
|
break; |
||||
|
case RawMouseEventType.LeftButtonUp: |
||||
|
this.MouseUp((IMouseDevice)e.Device, (IVisual)e.Root, e.Position); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void MouseMove(IMouseDevice device, IVisual visual, Point p) |
||||
|
{ |
||||
|
if (this.Captured == null) |
||||
|
{ |
||||
|
this.InputManager.SetPointerOver(this, visual, p); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Point offset = new Point(); |
||||
|
|
||||
|
foreach (IVisual ancestor in this.Captured.GetVisualAncestors()) |
||||
|
{ |
||||
|
offset += ancestor.Bounds.Position; |
||||
|
} |
||||
|
|
||||
|
this.InputManager.SetPointerOver(this, this.Captured, p - offset); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void MouseDown(IMouseDevice device, IVisual visual, Point p) |
||||
|
{ |
||||
|
IVisual hit = visual.GetVisualAt(p); |
||||
|
|
||||
|
if (hit != null) |
||||
|
{ |
||||
|
IInteractive source = this.GetSource(hit); |
||||
|
|
||||
|
if (source != null) |
||||
|
{ |
||||
|
source.RaiseEvent(new PointerEventArgs |
||||
|
{ |
||||
|
Device = this, |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
OriginalSource = source, |
||||
|
Source = source, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
IInputElement focusable = this.GetFocusable(hit); |
||||
|
|
||||
|
if (focusable != null && focusable.Focusable) |
||||
|
{ |
||||
|
focusable.Focus(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void MouseUp(IMouseDevice device, IVisual visual, Point p) |
||||
|
{ |
||||
|
IVisual hit = visual.GetVisualAt(p); |
||||
|
|
||||
|
if (hit != null) |
||||
|
{ |
||||
|
IInteractive source = this.GetSource(hit); |
||||
|
|
||||
|
if (source != null) |
||||
|
{ |
||||
|
source.RaiseEvent(new PointerEventArgs |
||||
|
{ |
||||
|
Device = this, |
||||
|
RoutedEvent = InputElement.PointerReleasedEvent, |
||||
|
OriginalSource = source, |
||||
|
Source = source, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private IInputElement GetFocusable(IVisual hit) |
||||
|
{ |
||||
|
return this.Captured as IInputElement ?? |
||||
|
hit.GetSelfAndVisualAncestors().OfType<IInputElement>().FirstOrDefault(x => x.Focusable); |
||||
|
} |
||||
|
|
||||
|
private IInteractive GetSource(IVisual hit) |
||||
|
{ |
||||
|
return this.Captured ?? |
||||
|
(hit as IInteractive) ?? |
||||
|
hit.GetSelfAndVisualAncestors().OfType<IInteractive>().FirstOrDefault(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
||||
|
<PropertyGroup> |
||||
|
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProjectGuid>{62024B2D-53EB-4638-B26B-85EEAA54866E}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex.Input</RootNamespace> |
||||
|
<AssemblyName>Perspex.Input</AssemblyName> |
||||
|
<DefaultLanguage>en-US</DefaultLanguage> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile> |
||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<!-- A reference to the entire .NET Framework is automatically included --> |
||||
|
<ProjectReference Include="..\Perspex.Base\Perspex.Base.csproj"> |
||||
|
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
||||
|
<Name>Perspex.Base</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
||||
|
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
||||
|
<Name>Perspex.Interactivity</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Layout\Perspex.Layout.csproj"> |
||||
|
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
||||
|
<Name>Perspex.Layout</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
||||
|
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
||||
|
<Name>Perspex.SceneGraph</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="FocusManager.cs" /> |
||||
|
<Compile Include="IFocusManager.cs" /> |
||||
|
<Compile Include="IInputDevice.cs" /> |
||||
|
<Compile Include="IInputElement.cs" /> |
||||
|
<Compile Include="IInputManager.cs" /> |
||||
|
<Compile Include="IKeyboardDevice.cs" /> |
||||
|
<Compile Include="IMouseDevice.cs" /> |
||||
|
<Compile Include="InputElement.cs" /> |
||||
|
<Compile Include="InputManager.cs" /> |
||||
|
<Compile Include="IPointerDevice.cs" /> |
||||
|
<Compile Include="Key.cs" /> |
||||
|
<Compile Include="KeyboardDevice.cs" /> |
||||
|
<Compile Include="KeyEventArgs.cs" /> |
||||
|
<Compile Include="MouseDevice.cs" /> |
||||
|
<Compile Include="PointerEventArgs.cs" /> |
||||
|
<Compile Include="Raw\RawInputEventArgs.cs" /> |
||||
|
<Compile Include="Raw\RawKeyEventArgs.cs" /> |
||||
|
<Compile Include="Raw\RawMouseEventArgs.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Splat"> |
||||
|
<HintPath>..\packages\Splat.1.3.3\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Core"> |
||||
|
<HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Core.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Interfaces"> |
||||
|
<HintPath>..\packages\Rx-Interfaces.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Interfaces.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Linq"> |
||||
|
<HintPath>..\packages\Rx-Linq.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Linq.dll</HintPath> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
||||
|
<Import Project="..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets" Condition="Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" /> |
||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> |
||||
|
<PropertyGroup> |
||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> |
||||
|
</PropertyGroup> |
||||
|
<Error Condition="!Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets'))" /> |
||||
|
</Target> |
||||
|
<!-- 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> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,21 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PointerEventArgs.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Interactivity; |
||||
|
|
||||
|
public class PointerEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
public IPointerDevice Device { get; set; } |
||||
|
|
||||
|
public Point GetPosition(IVisual relativeTo) |
||||
|
{ |
||||
|
return this.Device.GetPosition(relativeTo); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Resources; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
[assembly: AssemblyTitle("Perspex.Input")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.Input")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2014")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
[assembly: NeutralResourcesLanguage("en")] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,27 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RawInputEventArgs.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input.Raw |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Perspex.Layout; |
||||
|
|
||||
|
public class RawInputEventArgs : EventArgs |
||||
|
{ |
||||
|
public RawInputEventArgs(IInputDevice device) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(device != null); |
||||
|
|
||||
|
this.Device = device; |
||||
|
} |
||||
|
|
||||
|
public IInputDevice Device { get; private set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RawKeyEventArgs.cs" company="Tricycle">
|
||||
|
// Copyright 2014 Tricycle. All rights reserved.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input.Raw |
||||
|
{ |
||||
|
public enum RawKeyEventType |
||||
|
{ |
||||
|
KeyDown, |
||||
|
KeyUp |
||||
|
} |
||||
|
|
||||
|
public class RawKeyEventArgs : RawInputEventArgs |
||||
|
{ |
||||
|
public RawKeyEventArgs(KeyboardDevice device, RawKeyEventType type, Key key, string text) |
||||
|
: base(device) |
||||
|
{ |
||||
|
this.Key = key; |
||||
|
this.Type = type; |
||||
|
this.Text = text; |
||||
|
} |
||||
|
|
||||
|
public Key Key { get; set; } |
||||
|
|
||||
|
public string Text { get; set; } |
||||
|
|
||||
|
public RawKeyEventType Type { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RawMouseEventArgs.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Input.Raw |
||||
|
{ |
||||
|
using System; |
||||
|
using Perspex.Layout; |
||||
|
|
||||
|
public enum RawMouseEventType |
||||
|
{ |
||||
|
Move, |
||||
|
LeftButtonDown, |
||||
|
LeftButtonUp, |
||||
|
} |
||||
|
|
||||
|
public class RawMouseEventArgs : RawInputEventArgs |
||||
|
{ |
||||
|
public RawMouseEventArgs( |
||||
|
IInputDevice device, |
||||
|
ILayoutRoot root, |
||||
|
RawMouseEventType type, |
||||
|
Point position) |
||||
|
: base(device) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(device != null); |
||||
|
Contract.Requires<ArgumentNullException>(root != null); |
||||
|
|
||||
|
this.Root = root; |
||||
|
this.Position = position; |
||||
|
this.Type = type; |
||||
|
} |
||||
|
|
||||
|
public ILayoutRoot Root { get; private set; } |
||||
|
|
||||
|
public Point Position { get; private set; } |
||||
|
|
||||
|
public RawMouseEventType Type { get; private set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="Rx-Core" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Interfaces" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Linq" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Main" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-PlatformServices" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Splat" version="1.3.3" targetFramework="portable-net45+win" /> |
||||
|
<package id="StyleCop.MSBuild" version="4.7.49.0" targetFramework="portable-net45+win" developmentDependency="true" /> |
||||
|
</packages> |
||||
@ -0,0 +1,13 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IInteractive.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Interactivity |
||||
|
{ |
||||
|
public interface IInteractive : IVisual |
||||
|
{ |
||||
|
void RaiseEvent(RoutedEventArgs e); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,124 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="Interactive.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Interactivity |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reactive; |
||||
|
using System.Reactive.Linq; |
||||
|
using Perspex.Layout; |
||||
|
|
||||
|
public class Interactive : Layoutable, IInteractive |
||||
|
{ |
||||
|
private Dictionary<RoutedEvent, List<Delegate>> eventHandlers = new Dictionary<RoutedEvent, List<Delegate>>(); |
||||
|
|
||||
|
public void AddHandler(RoutedEvent routedEvent, Delegate handler) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(routedEvent != null); |
||||
|
Contract.Requires<NullReferenceException>(handler != null); |
||||
|
|
||||
|
List<Delegate> delegates; |
||||
|
|
||||
|
if (!this.eventHandlers.TryGetValue(routedEvent, out delegates)) |
||||
|
{ |
||||
|
delegates = new List<Delegate>(); |
||||
|
this.eventHandlers.Add(routedEvent, delegates); |
||||
|
} |
||||
|
|
||||
|
delegates.Add(handler); |
||||
|
} |
||||
|
|
||||
|
public IObservable<EventPattern<T>> GetObservable<T>(RoutedEvent<T> routedEvent) where T : RoutedEventArgs |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(routedEvent != null); |
||||
|
|
||||
|
return Observable.FromEventPattern<T>( |
||||
|
handler => this.AddHandler(routedEvent, handler), |
||||
|
handler => this.RemoveHandler(routedEvent, handler)); |
||||
|
} |
||||
|
|
||||
|
public void RemoveHandler(RoutedEvent routedEvent, Delegate handler) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(routedEvent != null); |
||||
|
Contract.Requires<NullReferenceException>(handler != null); |
||||
|
|
||||
|
List<Delegate> delegates; |
||||
|
|
||||
|
if (this.eventHandlers.TryGetValue(routedEvent, out delegates)) |
||||
|
{ |
||||
|
delegates.Remove(handler); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void RaiseEvent(RoutedEventArgs e) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(e != null); |
||||
|
|
||||
|
if (e.RoutedEvent != null) |
||||
|
{ |
||||
|
switch (e.RoutedEvent.RoutingStrategy) |
||||
|
{ |
||||
|
case RoutingStrategy.Bubble: |
||||
|
this.BubbleEvent(e); |
||||
|
break; |
||||
|
case RoutingStrategy.Direct: |
||||
|
this.RaiseEventImpl(e); |
||||
|
break; |
||||
|
case RoutingStrategy.Tunnel: |
||||
|
this.TunnelEvent(e); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void BubbleEvent(RoutedEventArgs e) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(e != null); |
||||
|
|
||||
|
foreach (var target in this.GetSelfAndVisualAncestors().OfType<Interactive>()) |
||||
|
{ |
||||
|
target.RaiseEventImpl(e); |
||||
|
|
||||
|
if (e.Handled) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void TunnelEvent(RoutedEventArgs e) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(e != null); |
||||
|
|
||||
|
foreach (var target in this.GetSelfAndVisualAncestors().OfType<Interactive>().Reverse()) |
||||
|
{ |
||||
|
target.RaiseEventImpl(e); |
||||
|
|
||||
|
if (e.Handled) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void RaiseEventImpl(RoutedEventArgs e) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(e != null); |
||||
|
|
||||
|
List<Delegate> delegates; |
||||
|
|
||||
|
if (this.eventHandlers.TryGetValue(e.RoutedEvent, out delegates)) |
||||
|
{ |
||||
|
foreach (Delegate handler in delegates) |
||||
|
{ |
||||
|
handler.DynamicInvoke(this, e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,90 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
||||
|
<PropertyGroup> |
||||
|
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProjectGuid>{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex</RootNamespace> |
||||
|
<AssemblyName>Perspex.Interactive</AssemblyName> |
||||
|
<DefaultLanguage>en-US</DefaultLanguage> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile> |
||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<!-- A reference to the entire .NET Framework is automatically included --> |
||||
|
<ProjectReference Include="..\Perspex.Base\Perspex.Base.csproj"> |
||||
|
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
||||
|
<Name>Perspex.Base</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.Layout\Perspex.Layout.csproj"> |
||||
|
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
||||
|
<Name>Perspex.Layout</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
||||
|
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
||||
|
<Name>Perspex.SceneGraph</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="IInteractive.cs" /> |
||||
|
<Compile Include="Interactive.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
<Compile Include="RoutedEvent.cs" /> |
||||
|
<Compile Include="RoutedEventArgs.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Splat"> |
||||
|
<HintPath>..\packages\Splat.1.3.3\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Core"> |
||||
|
<HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Core.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Interfaces"> |
||||
|
<HintPath>..\packages\Rx-Interfaces.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Interfaces.dll</HintPath> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Linq"> |
||||
|
<HintPath>..\packages\Rx-Linq.2.1.30214.0\lib\Portable-Net45+WinRT45+WP8\System.Reactive.Linq.dll</HintPath> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
||||
|
<Import Project="..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets" Condition="Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" /> |
||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> |
||||
|
<PropertyGroup> |
||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> |
||||
|
</PropertyGroup> |
||||
|
<Error Condition="!Exists('..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\StyleCop.MSBuild.4.7.49.0\build\StyleCop.MSBuild.Targets'))" /> |
||||
|
</Target> |
||||
|
<!-- 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> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Resources; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
[assembly: AssemblyTitle("Perspex.Interactive")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.Interactive")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2014")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
[assembly: NeutralResourcesLanguage("en")] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,97 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RoutedEvent.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Interactivity |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
public enum RoutingStrategy |
||||
|
{ |
||||
|
Tunnel, |
||||
|
Bubble, |
||||
|
Direct, |
||||
|
} |
||||
|
|
||||
|
public class RoutedEvent |
||||
|
{ |
||||
|
public RoutedEvent( |
||||
|
string name, |
||||
|
RoutingStrategy routingStrategy, |
||||
|
Type eventArgsType, |
||||
|
Type ownerType) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
Contract.Requires<NullReferenceException>(eventArgsType != null); |
||||
|
Contract.Requires<NullReferenceException>(ownerType != null); |
||||
|
Contract.Requires<InvalidCastException>(typeof(RoutedEventArgs).GetTypeInfo().IsAssignableFrom(eventArgsType.GetTypeInfo())); |
||||
|
Contract.Requires<InvalidCastException>(typeof(Interactive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo())); |
||||
|
|
||||
|
this.EventArgsType = eventArgsType; |
||||
|
this.Name = name; |
||||
|
this.OwnerType = ownerType; |
||||
|
this.RoutingStrategy = routingStrategy; |
||||
|
} |
||||
|
|
||||
|
public Type EventArgsType |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public string Name |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public Type OwnerType |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public RoutingStrategy RoutingStrategy |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public static RoutedEvent<TEventArgs> Register<TOwner, TEventArgs>( |
||||
|
string name, |
||||
|
RoutingStrategy routingStrategy) |
||||
|
where TOwner : Interactive |
||||
|
where TEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
|
||||
|
return new RoutedEvent<TEventArgs>(name, routingStrategy, typeof(TOwner)); |
||||
|
} |
||||
|
|
||||
|
public static RoutedEvent<TEventArgs> Register<TEventArgs>( |
||||
|
string name, |
||||
|
RoutingStrategy routingStrategy, |
||||
|
Type ownerType) |
||||
|
where TEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
|
||||
|
return new RoutedEvent<TEventArgs>(name, routingStrategy, ownerType); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class RoutedEvent<TEventArgs> : RoutedEvent |
||||
|
where TEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
public RoutedEvent(string name, RoutingStrategy routingStrategy, Type ownerType) |
||||
|
: base(name, routingStrategy, typeof(TEventArgs), ownerType) |
||||
|
{ |
||||
|
Contract.Requires<NullReferenceException>(name != null); |
||||
|
Contract.Requires<NullReferenceException>(ownerType != null); |
||||
|
Contract.Requires<InvalidCastException>(typeof(Interactive).GetTypeInfo().IsAssignableFrom(ownerType.GetTypeInfo())); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="RoutedEventArgs.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Interactivity |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
public class RoutedEventArgs : EventArgs |
||||
|
{ |
||||
|
public RoutedEventArgs() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public RoutedEventArgs(RoutedEvent routedEvent) |
||||
|
{ |
||||
|
this.RoutedEvent = routedEvent; |
||||
|
} |
||||
|
|
||||
|
public RoutedEventArgs(RoutedEvent routedEvent, IInteractive source) |
||||
|
{ |
||||
|
this.RoutedEvent = routedEvent; |
||||
|
this.Source = source; |
||||
|
} |
||||
|
|
||||
|
public bool Handled { get; set; } |
||||
|
|
||||
|
public IInteractive OriginalSource { get; set; } |
||||
|
|
||||
|
public RoutedEvent RoutedEvent { get; set; } |
||||
|
|
||||
|
public IInteractive Source { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="Rx-Core" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Interfaces" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Linq" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-Main" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Rx-PlatformServices" version="2.1.30214.0" targetFramework="portable-net45+win" /> |
||||
|
<package id="Splat" version="1.3.3" targetFramework="portable-net45+win" /> |
||||
|
<package id="StyleCop.MSBuild" version="4.7.49.0" targetFramework="portable-net45+win" developmentDependency="true" /> |
||||
|
</packages> |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue