// ----------------------------------------------------------------------- // // Copyright 2014 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Controls { using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using Perspex.Collections; using Perspex.Input; using Perspex.Interactivity; using Perspex.Media; using Perspex.Rendering; using Perspex.Styling; using Splat; public class Control : InputElement, ILogical, IStyleable, IStyleHost { public static readonly PerspexProperty ParentProperty = PerspexProperty.Register("Parent"); public static readonly PerspexProperty TagProperty = PerspexProperty.Register("Tag"); public static readonly PerspexProperty TemplatedParentProperty = PerspexProperty.Register("TemplatedParent", inherits: true); public static readonly RoutedEvent RequestBringIntoViewEvent = RoutedEvent.Register("RequestBringIntoView", RoutingStrategy.Bubble); private static readonly IReadOnlyPerspexList EmptyChildren = new SingleItemPerspexList(); private Classes classes = new Classes(); private DataTemplates dataTemplates; private string id; private Styles styles; static Control() { Control.AffectsMeasure(Control.IsVisibleProperty); PseudoClass(InputElement.IsEnabledCoreProperty, x => !x, ":disabled"); PseudoClass(InputElement.IsFocusedProperty, ":focus"); PseudoClass(InputElement.IsPointerOverProperty, ":pointerover"); } 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 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 Styles Styles { get { if (this.styles == null) { this.styles = new Styles(); } return this.styles; } set { this.styles = value; } } public Control Parent { get { return this.GetValue(ParentProperty); } internal set { this.SetValue(ParentProperty, value); } } public object Tag { get { return this.GetValue(TagProperty); } set { this.SetValue(TagProperty, value); } } public ITemplatedControl TemplatedParent { get { return this.GetValue(TemplatedParentProperty); } internal set { this.SetValue(TemplatedParentProperty, value); } } ILogical ILogical.LogicalParent { get { return this.Parent; } } IReadOnlyPerspexList ILogical.LogicalChildren { get { return EmptyChildren; } } public void BringIntoView() { this.BringIntoView(new Rect(this.ActualSize)); } public void BringIntoView(Rect rect) { var ev = new RequestBringIntoViewEventArgs { RoutedEvent = RequestBringIntoViewEvent, TargetObject = this, TargetRect = rect, }; this.RaiseEvent(ev); } protected static void PseudoClass(PerspexProperty property, string className) { PseudoClass(property, x => x, className); } protected static void PseudoClass( PerspexProperty property, Func selector, string className) { Contract.Requires(property != null); Contract.Requires(selector != null); Contract.Requires(className != null); Contract.Requires(property != null); if (string.IsNullOrWhiteSpace(className)) { throw new ArgumentException("Cannot supply an empty className."); } Observable.Merge(property.Changed, property.Initialized) .Subscribe(e => { if (selector((T)e.NewValue)) { ((Control)e.Sender).Classes.Add(className); } else { ((Control)e.Sender).Classes.Remove(className); } }); } protected override void OnAttachedToVisualTree(IRenderRoot root) { base.OnAttachedToVisualTree(root); IStyler styler = Locator.Current.GetService(); styler.ApplyStyles(this); } } }